1 /** 2 Copyright: Copyright (c) 2015-2016, Joakim Brännström. All rights reserved. 3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 Basic support structure for enabling semantic representation in D of languages. 7 Assuming that submodules need: 8 - indentation. 9 - key/value store. 10 - recursing rendering. 11 */ 12 module dsrcgen.base; 13 14 @safe: 15 16 private struct KV { 17 string k; 18 string v; 19 20 this(T)(string k, T v) { 21 import std.conv : to; 22 23 this.k = k; 24 this.v = to!string(v); 25 } 26 } 27 28 package struct AttrSetter { 29 template opDispatch(string name) { 30 @property auto opDispatch(T)(T v) { 31 static if (name.length > 1 && name[$ - 1] == '_') { 32 return KV(name[0 .. $ - 1], v); 33 } else { 34 return KV(name, v); 35 } 36 } 37 } 38 } 39 40 public: 41 42 /// Methods for setting attributes inside slice operator via $.x. 43 mixin template Attrs() { 44 import std..string; 45 46 public string[string] attrs; 47 48 auto opIndex(T...)(T kvs) { 49 foreach (kv; kvs) { 50 attrs[kv.k] = kv.v; 51 } 52 return this; 53 } 54 55 auto opDollar(int dim)() { 56 return AttrSetter(); 57 } 58 } 59 60 /** Interface for rendering functionality. 61 * 62 * After the semantic representation is finished the BaseElement interface is 63 * used to recursively render the representation. 64 */ 65 interface BaseElement { 66 /// Recursively render the modules. 67 string render() pure; 68 69 /// Query the module for an indented string representation. 70 string renderIndent(int parent_level, int level) pure; 71 72 /// Query the module for a concatenated string of the childrens representation. 73 string renderRecursive(int parent_level, int level) pure; 74 75 /// Query the module for post recursive data. 76 string renderPostRecursive(int parent_level, int level) pure; 77 } 78 79 /// Raw text representation without indentation. 80 class Text(T) : T { 81 private string contents; 82 83 /// Use content as is. 84 this(string contents) pure { 85 this.contents = contents; 86 } 87 88 /// Render content without indentation. 89 override string renderIndent(int parent_level, int level) pure { 90 return contents; 91 } 92 } 93 94 /** An empty node. 95 * 96 * Not affected by indentation. 97 * 98 * Useful to inject other nodes under it. 99 */ 100 class Empty(T) : T { 101 } 102 103 /** Common functionality for modules. 104 * 105 * Support indentation. 106 * Children structure. 107 * Recursive rendering of content + children. 108 * Line separation independent of accidental sep(). 109 * 110 * TODO refactor, lessen the coupling by moving functionality to pure, free functions. 111 * TODO refactor, use a GC-less allocator like Array. 112 * TODO refactor, toString to support the range versions with a sink. 113 */ 114 class BaseModule : BaseElement { 115 116 /// Empty with defaults. 117 this() pure nothrow @nogc { 118 } 119 120 /** Module with a indent different fro default 121 * Params: 122 * indent_width = number of whitespaces to use as indent for each level 123 */ 124 this(int indent_width) pure nothrow @nogc { 125 this.indent_width = indent_width; 126 } 127 128 /** Set indent suppression from this point and all children. 129 * 130 * Number of levels to suppress indent of children. 131 * Propagated to leafs. 132 * 133 * It can't suppress indent to lower than parent. 134 * To suppress further use suppressThisIndent. 135 * 136 * Params: 137 * levels = nr of indentation levels to suppress 138 */ 139 void suppressIndent(int levels) pure nothrow @nogc { 140 this.suppress_child_indent = levels; 141 } 142 143 /** Suppress indentation by also affecting the level propagated from the parent. 144 * 145 * Params: 146 * levels = nr of indentation levels to suppress 147 */ 148 void suppressThisIndent(int levels) pure nothrow @nogc { 149 this.suppress_indent = levels; 150 } 151 152 /// Sets the width of the indentation 153 void setIndentation(int ind) pure nothrow @nogc { 154 this.indent_width = ind; 155 } 156 157 /// Clear the node of childrens. 158 auto reset() pure nothrow { 159 children.length = 0; 160 return this; 161 } 162 163 /// Separate with at most count empty lines. 164 void sep(int count = 1) pure nothrow { 165 import std.ascii : newline; 166 167 count -= sep_lines; 168 if (count <= 0) 169 return; 170 foreach (i; 0 .. count) { 171 children ~= new Text!(typeof(this))(newline); 172 } 173 174 sep_lines += count; 175 } 176 177 /** Insert element at the front. 178 * 179 * Resets line separation. 180 * 181 * TODO change to insertFront to be consistent with std.container API. 182 */ 183 void prepend(BaseElement e) pure nothrow { 184 children = e ~ children; 185 sep_lines = 0; 186 } 187 188 /** Insert element at the back. 189 * 190 * Resets line separation. 191 * 192 * TODO change to insertBack to be consistent with std.container API. 193 */ 194 void append(BaseElement e) pure nothrow { 195 children ~= e; 196 sep_lines = 0; 197 } 198 199 /** Remove all children and clear line separation. 200 * 201 * Resets line separation. 202 * Why? 203 * Conceptually restarts the container so no children then nothing to 204 * separate with empty lines. 205 */ 206 void clearChildren() pure nothrow { 207 children.length = 0; 208 sep_lines = 0; 209 } 210 211 /** Render content with an indentation that takes the parent in 212 * consideration. 213 */ 214 string indent(string s, int parent_level, int level) const pure nothrow { 215 import std.algorithm : max; 216 217 level = max(0, parent_level, level); 218 char[] indent; 219 indent.length = indent_width * level; 220 indent[] = ' '; 221 222 return indent.idup ~ s; 223 } 224 225 override string renderIndent(int parent_level, int level) pure { 226 return ""; 227 } 228 229 override string renderRecursive(int parent_level, int level) pure { 230 import std.algorithm : max; 231 232 level -= suppress_indent; 233 string s = renderIndent(parent_level, level); 234 235 // suppressing is intented to affects children. The current leaf is 236 // intented according to the parent or propagated level. 237 int child_level = level - suppress_child_indent; 238 foreach (e; children) { 239 // lock indent to the level of the parent. it allows a suppression of many levels of children. 240 s ~= e.renderRecursive(max(parent_level, level), child_level + 1); 241 } 242 s ~= renderPostRecursive(parent_level, level); 243 244 return s; 245 } 246 247 override string renderPostRecursive(int parent_level, int level) pure { 248 return ""; 249 } 250 251 override string render() pure { 252 return renderRecursive(0 - suppress_child_indent, 0 - suppress_child_indent); 253 } 254 255 private: 256 int indent_width = 4; 257 int suppress_indent; 258 int suppress_child_indent; 259 260 BaseElement[] children; 261 int sep_lines; 262 }