| | 9 | * |
|---|
| | 10 | * THEORY: |
|---|
| | 11 | * Nested mixins are used. The outermost one tricks the compiler into |
|---|
| | 12 | * converting the expression into a standard form with precedence and |
|---|
| | 13 | * associativity information. The inner mixin determines the type and value |
|---|
| | 14 | * of each symbol in the expression, by mixing in '.stringof' expressions. |
|---|
| | 15 | * The two levels of mixins are required because of some quirks in the behaviour |
|---|
| | 16 | * of stringof. |
|---|
| | 17 | * |
|---|
| | 18 | * BUGS: |
|---|
| | 19 | * The following syntax is not supported: |
|---|
| | 20 | * unary operators & ! |
|---|
| | 21 | * && and || operators |
|---|
| | 22 | * Comparision operators and NCEG floating point operators |
|---|
| | 23 | * ?: |
|---|
| | 24 | * $ not really supported: a[$-1] should be replaced by a[a.length-1] |
|---|
| | 25 | * cast |
|---|
| | 26 | * new, delete, anonymous classes |
|---|
| | 27 | * mixin, assert, and import expressions |
|---|
| | 28 | * D 2.004 string literals. |
|---|
| | 29 | * QUIRKS: |
|---|
| | 30 | * Functions are typed as function pointers in the symbol table, unless they are |
|---|
| | 31 | * used as properties, in which case they appear as function types. |
|---|
| | 32 | * If the expression refers to a non-existent variable, a sensible error message |
|---|
| | 33 | * will be printed, but it will be followed by a torrent of useless error messages. |
|---|
| | 34 | * |
|---|
| | 38 | public: |
|---|
| | 39 | |
|---|
| | 40 | /** |
|---|
| | 41 | * Returns a string, which, when mixed in, lexes, parses, and semantically |
|---|
| | 42 | * analyses the given expression, then creates a struct literal of type |
|---|
| | 43 | * AbstractSyntaxTree, containing a symbol table, and a syntax tree in a |
|---|
| | 44 | * very simple placeholder format, which refers to the elements in the symbol table. |
|---|
| | 45 | * |
|---|
| | 46 | * Syntax trees are represented as placeholder expressions, with associativity |
|---|
| | 47 | * and precedence indicated by parentheses. |
|---|
| | 48 | * |
|---|
| | 49 | |
|---|
| | 50 | * Typically, this function will be a code generator for a mixin. |
|---|
| | 51 | */ |
|---|
| | 52 | char [] syntaxtreeof(char [] expression) |
|---|
| | 53 | { |
|---|
| | 54 | char [] expr=expression; |
|---|
| | 55 | char [][] symbols = replaceSymbolsWithPlaceholders(expr); |
|---|
| | 56 | // if (symbols.length> 25) return `static assert(0, "Too many symbols in expression.");`; |
|---|
| | 57 | return `mixin(mixin_SymbolTable(` ~ wrapInQuotes(expression)~ `,` ~ mixin_getPrecedence(expr) ~ `))`; |
|---|
| | 58 | } |
|---|
| | 59 | |
|---|
| | 60 | |
|---|
| | 61 | /** The values of the A, B, C, ... placeholders |
|---|
| | 62 | * |
|---|
| | 63 | * Members: |
|---|
| | 64 | * type the name of the type, as text |
|---|
| | 65 | * value The value, as text. This will be either a symbol name, or a literal. |
|---|
| | 66 | * Note that if it is a numeric or text literal, value[0] will be one of the characters |
|---|
| | 67 | * 0123456789"'- |
|---|
| | 68 | */ |
|---|
| | 69 | struct Symbol { |
|---|
| | 70 | char [] type; // the name of the type, as text |
|---|
| | 71 | char [] value; // the value, as text. |
|---|
| | 72 | } |
|---|
| | 73 | |
|---|
| | 74 | /** The result of semantic analysis of the original expression |
|---|
| | 75 | * |
|---|
| | 76 | */ |
|---|
| | 77 | struct AbstractSyntaxTree { |
|---|
| | 78 | char [] expression; /// syntax tree in Placeholder format, eg A+=(B*C) |
|---|
| | 79 | Symbol[] symbolTable; /// The types and values of A,B,C,... |
|---|
| | 80 | } |
|---|
| | 81 | |
|---|
| | 82 | private: |
|---|
| 121 | | |
|---|
| 122 | | // The values of the A, B, C, ... placeholders |
|---|
| 123 | | struct Symbol { |
|---|
| 124 | | char [] type; // the name of the type, as text |
|---|
| 125 | | char [] value; // the value, as text |
|---|
| 126 | | bool isConst; // is it a compile-time constant? |
|---|
| 127 | | } |
|---|
| 128 | | |
|---|
| 129 | | /** FOR MIXIN |
|---|
| 130 | | * Given an array of symbols, return a string to be mixed in, which will |
|---|
| 131 | | * generate the type and value associated with each symbol. |
|---|
| 132 | | * When mixed in, a Symbol[] array literal will be generated. |
|---|
| 133 | | */ |
|---|
| 134 | | char[] mixin_SymbolTable(char [][] symbols) |
|---|
| 135 | | { |
|---|
| 136 | | // Creates an array[2] of string literals, where [0] is the type, and [1] is the value |
|---|
| 137 | | // This is passed to the toSymTable function which determines which entries are constants. |
|---|
| 138 | | char [] result = "toSymTable(["; |
|---|
| 139 | | for(int i=0; i<symbols.length; ++i) { |
|---|
| 140 | | if (i>0) result ~=","; |
|---|
| 141 | | if (symbols[i][0]>='0' && symbols[i][0]<='9') { |
|---|
| 142 | | result ~= "[typeof(" ~ symbols[i] ~ `).stringof, (` ~symbols[i] ~ `).stringof]`; |
|---|
| 143 | | } else |
|---|
| 144 | | result ~= "[typeof(" ~ symbols[i] ~ `).stringof, ` ~symbols[i] ~ `.stringof]`; |
|---|
| 145 | | } |
|---|
| 146 | | result ~="])"; |
|---|
| 147 | | return result; |
|---|
| 148 | | } |
|---|
| 149 | | |
|---|
| 150 | | /// Converts the input [type][value] array into a Symbol[] array. |
|---|
| 151 | | Symbol [] toSymTable(char [][2][] symboltable) |
|---|
| 152 | | { |
|---|
| 153 | | Symbol [] symbolTable=[]; |
|---|
| 154 | | for (int i=0; i<symboltable.length; ++i) { |
|---|
| 155 | | char c = symboltable[i][1][0]; |
|---|
| 156 | | bool isConst = ((c>='0' && c<='9')||c=='-'|| c=='"' || c=='\''); |
|---|
| 157 | | symbolTable ~= Symbol(symboltable[i][0], symboltable[i][1], isConst); |
|---|
| 158 | | } |
|---|
| 159 | | return symbolTable; |
|---|
| 160 | | } |
|---|
| 161 | | |
|---|
| | 307 | // ==== SEMANTIC PASS ==== |
|---|
| | 308 | |
|---|
| | 309 | bool isFunc(int n, char [] expr) |
|---|
| | 310 | { |
|---|
| | 311 | char c = n + 'A'; |
|---|
| | 312 | for (int i=0; i<expr.length-1; ++i) { |
|---|
| | 313 | if ((expr[i]==c) && expr[i+1]=='(') return true; |
|---|
| | 314 | } |
|---|
| | 315 | return false; |
|---|
| | 316 | } |
|---|
| | 317 | |
|---|
| | 318 | public: |
|---|
| | 319 | /** FOR MIXIN |
|---|
| | 320 | * Given a raw expression, and a processed expression (with placeholders, and |
|---|
| | 321 | * correct precedence), return a string to be mixed in, which will |
|---|
| | 322 | * generate the type and value associated with each symbol. |
|---|
| | 323 | * When mixed in, an AbstractSyntaxTree containing a Symbol[] array literal will be generated. |
|---|
| | 324 | */ |
|---|
| | 325 | char [] mixin_SymbolTable(char [] expression, char [] exprWithPlaceholders) |
|---|
| | 326 | { |
|---|
| | 327 | char [] expr = expression; |
|---|
| | 328 | char [][] symbols = replaceSymbolsWithPlaceholders(expr); |
|---|
| | 329 | // Creates an array[2] of string literals, where [0] is the type, and [1] is the value |
|---|
| | 330 | // This is passed to the toSymTable function which determines which entries are constants. |
|---|
| | 331 | char [] result = `AbstractSyntaxTree("` ~ exprWithPlaceholders ~ `",[`; |
|---|
| | 332 | for(int i=0; i<symbols.length; ++i) { |
|---|
| | 333 | if (i>0) result ~=","; |
|---|
| | 334 | if (isFunc(i, exprWithPlaceholders)) |
|---|
| | 335 | result ~= `Symbol(typeof(&` ~ symbols[i] ~ `).stringof,"` ~ symbols[i]~ `")`; |
|---|
| | 336 | else if (symbols[i][0]>='0' && symbols[i][0]<='9') { |
|---|
| | 337 | result ~= `Symbol(typeof(` ~ symbols[i] ~ `).stringof, (` ~symbols[i] ~ `).stringof)`; |
|---|
| | 338 | } else |
|---|
| | 339 | result ~= `Symbol(typeof(` ~ symbols[i] ~ `).stringof, ` ~symbols[i] ~ `.stringof)`; |
|---|
| | 340 | } |
|---|
| | 341 | result ~="])"; |
|---|
| | 342 | return result; |
|---|
| | 343 | } |
|---|
| | 344 | |
|---|
| | 345 | public: |
|---|
| | 346 | char [] wrapInQuotes(char [] instr) |
|---|
| | 347 | { |
|---|
| | 348 | char [] str=`"`; |
|---|
| | 349 | foreach(char c; instr) { |
|---|
| | 350 | if (c=='"') str~=`\"`; |
|---|
| | 351 | else str~=c; |
|---|
| | 352 | } |
|---|
| | 353 | return str~ `"`; |
|---|
| | 354 | } |
|---|