1 module argon.compiler; 2 3 import std.array; 4 import std.format; 5 import std.typecons; 6 import std.conv; 7 8 import argon.ast; 9 import argon.parser; 10 11 template renderArgonTemplate(string file, Args...) 12 { 13 auto renderArgonTemplate(Writer)(auto ref Writer writer) 14 { 15 static if (__traits(compiles, writer.bodyWriter)) 16 { 17 auto output = writer.bodyWriter; 18 } 19 else 20 { 21 auto output = writer; 22 } 23 24 enum mainTemplate = compileArgonTemplate!file(); 25 enum includeTemplates = compileArgonIncludeTemplates!(mainTemplate[1])() ~ mainTemplate; 26 27 void delegate()[string] file_table; 28 29 auto get_namespace(string namespace)() 30 { 31 static foreach(i, Arg; Args) 32 static if (__traits(compiles, Arg == namespace) && Arg == namespace) 33 return Args[i+1]; 34 } 35 36 auto register_file(string fileName)(void delegate() func) 37 { 38 file_table[fileName] = func; 39 } 40 41 auto do_file(string fileName)() 42 { 43 file_table[fileName](); 44 } 45 46 static foreach(includeTemplate; includeTemplates) 47 { 48 mixin(includeTemplate[0]); 49 } 50 51 do_file!file(); 52 } 53 } 54 55 private alias ArgonTemplate = Tuple!(string, string[]); 56 57 private ArgonTemplate compileArgonTemplate(string file)() 58 { 59 auto compiler = new ArgonCompiler(file, import(file)); 60 return tuple(compiler.source, compiler.includeFiles); 61 } 62 63 private ArgonTemplate[] compileArgonIncludeTemplates(string[] files)() 64 { 65 ArgonTemplate[] templates; 66 static foreach(i, file; files) 67 { 68 mixin(format(q{ 69 enum template_%s = compileArgonTemplate!file(); 70 templates ~= template_%s ~ compileArgonIncludeTemplates!(template_%s[1])(); 71 }, i, i, i)); 72 } 73 return templates; 74 } 75 76 final class ArgonCompiler : ASTVisitor 77 { 78 string file; 79 string[] includeFiles; 80 ASTTemplateNode templateAST; 81 82 private Appender!string output; 83 private size_t[string] listElementDict; 84 private size_t lastUniqueId; 85 86 this(string file, string source) 87 { 88 this.file = file; 89 templateAST = parseArgonTemplate(file, source); 90 templateAST.accept(this); 91 } 92 93 void visit(ASTTemplateNode node) 94 { 95 auto functionId = node.file.hashOf(); 96 output.formattedWrite("void template_%s()\n{\n", functionId); 97 foreach(child; node.children) 98 { 99 child.accept(this); 100 } 101 output.formattedWrite("}\nregister_file!(`%s`)(&template_%s);\n\n", this.file, functionId); 102 } 103 104 void visit(ASTHTMLNode node) 105 { 106 output.formattedWrite("output.write(`%s`);\n", node.html.replace("`", "` ~ '`' ~ `")); 107 } 108 109 void visit(ASTIncludeNode node) 110 { 111 includeFiles ~= node.file; 112 output.formattedWrite("do_file!(`%s`)();\n", node.file); 113 } 114 115 void visit(ASTElementNode node) 116 { 117 output.put("output.write(text("); 118 node.identifier.accept(this); 119 output.put("));\n"); 120 } 121 122 void visit(ASTListNode node) 123 { 124 auto idString = format("%s:%-(%s.%)", node.identifier.namespace, node.identifier.callChain); 125 listElementDict[idString] = uniqueId; 126 output.formattedWrite("foreach(list_%s, element_%s; ", listElementDict[idString], listElementDict[idString]); 127 node.identifier.accept(this); 128 output.put(")\n{\n"); 129 foreach(child; node.children) 130 { 131 child.accept(this); 132 } 133 output.put("}\n"); 134 } 135 136 void visit(ASTIdentifierNode node) 137 { 138 Appender!string identifier; 139 140 identifier.formattedWrite("get_namespace!(`%s`)()", node.namespace); 141 foreach(i, call; node.callChain) 142 { 143 if (call == "[$]") 144 { 145 auto idString = format("%s:%-(%s.%)", node.namespace, node.callChain[0..i]); 146 identifier.formattedWrite("[list_%s]", listElementDict[idString]); 147 } 148 else if (call == "[#]") 149 { 150 auto idString = format("%s:%-(%s.%)", node.namespace, node.callChain[0..i]); 151 identifier = Appender!string.init; 152 identifier.formattedWrite("list_%s", listElementDict[idString]); 153 } 154 else 155 { 156 identifier.formattedWrite(".%s", call); 157 } 158 } 159 output.put(identifier.data); 160 } 161 162 @property string source() 163 { 164 return output.data; 165 } 166 167 @property size_t uniqueId() 168 { 169 return lastUniqueId++; 170 } 171 }