1 module metad.compiler;
2 
3 private import pegged.grammar;
4 private import std..string;
5 private import std.typecons;
6 
7 /++
8  + A compile-time compiler (meta-compiler).
9  +
10  + To use: (see the unittest for a running example)
11  +
12  + Create your pegged grammar.
13  +    enum _g = q{ GRAMMAR: ... };
14  +    mixin (grammar(_g));
15  +
16  + Create a template for your compiler, and mixin the default signature.
17  +   struct myCompiler(ParseTree T) {
18  +      mixin Compiler!(T,myCompiler);
19  +      // ....
20  +   }
21  +
22  + The "Compiler" mixin binds the "compileNode" function to a nested scope. We can override the compileNode function 
23  + for specific matches in the parse tree. To do this, we use string mixins, that bind to
24  + the local scope.  The template "nodeOverride" takes a condition and a function, and creates a local override
25  + that binds the function to "compileNode" if the static condition matches against a tree node.  The "compilerOverride" template
26  + binds to a node name.
27  + This goes inside the parser template definition, and in this case we're overriding the behaviour
28  + for a nodes matching a rule named "GRAMMAR.Text".  The override template accepts a second string argument
29  + representing the code to be mixed in when the name matches. The code should return a string that will be output
30  + from the compiler, to be mixed in to the users program.  In this example, we concantenate the "matches" property.
31  + "T" refers to the current node.
32  +      // ...
33  +      mixin (compilerOverride!("GRAMMAR.Text","T.matches.join(\"\")"));
34  +      // ...
35  +
36  + Then, we invoke the compiler.
37  +    enum _d = q{ ... the input to your parser ... };
38  +    mixin Compiler!(myParser,GRAMMAR(_d));
39  +
40  + Or we can just pull out the text and not mix it in to our program right away
41  +    enum code = myParser!(GRAMMAR(_d)).compileNode();
42  + 
43  +/
44  
45 template Compiler(ParseTree T,alias Parser) {
46 
47     static string[] compileChildNodes() {
48         string[] result;
49         static foreach(x;T.children) {
50             result~=Parser!(x).compileNode;
51         }
52         return result;
53     }
54 
55     static string compileNode() {
56         return compileChildNodes().join("");
57     }
58 
59     template nodeOverride(string Cond,string Func) {
60         enum nodeOverride = q{
61             static if(__C__) {
62                 static string compileNode() {
63                     return __F__;
64                 }
65             }
66         }.replace("__C__",Cond).replace("__F__",Func);
67     }
68 
69     template compilerOverride(string N,string F) {
70         enum compilerOverride = nodeOverride!("T.name == \""~N~"\"",F);
71     }
72 
73     template compile(alias Parser) {
74         mixin(Parser.compileNode);
75     }
76 
77     //template compileAlias(alias Parser) {
78     //    enum compileAlias = mixin(Parser.compileNode);
79     //}
80 }
81 
82 template Compiler(alias Parser,alias data) {
83     alias _compiler = Parser!(data);
84     mixin _compiler.compile!(_compiler);
85 }
86 
87 //struct MatchesTuple(string M) {
88 //    alias matches = M;
89 //}
90 //
91 //alias x = MatchesTuple!("X");
92 //alias y = Tuple!(MatchesTuple!("X"),"m");
93 
94 template TupleCompiler(ParseTree T,alias Parser) {
95     mixin Compiler!(T,Parser);
96 
97     static string[] compileChildValues() {
98         string[] result;
99         static foreach(x;T.children) {
100             result ~= "\"" ~ x.name ~ "\"";
101             //result ~= "[\"" ~ x.matches.map!(s=>s.replace("\"","\\\"")).join("\",\"") ~ "\"]";
102             result ~= "[]";
103         }
104         return result;
105     }
106     static string[] compileChildTypes() {
107         string[] result;
108         //string value = "";
109         static foreach(x;T.children) {
110             //value = "";
111             //value ~= "Tuple!(" ~ Parser!(x).compileNode ~ ",";
112             //static if (x.name.indexOf(".")!=-1)
113             //    value ~= '"' ~ x.name[x.name.indexOf(".")+1..$] ~ '"';
114             //else value ~= '"' ~ x.name ~ '"';
115             //value ~= ',';
116             //value ~= "MatchesTuple!(\"" ~ (x.matches.join("").replace("\"","\\\"")) ~ "\")";
117             ////value ~= "Tuple!(\"" ~ (x.matches.join("").replace("\"","\\\"")) ~ "\")";
118             //value ~= ",\"matches\"";
119             //value ~= ")";
120             result ~= "string";
121             result ~= "\"name\"";
122             result ~= "string[]";
123             result ~= "\"matches\"";
124             //result ~= value;
125         }
126         return result;
127         //return value;
128     }
129 
130     static string compileNode() {
131         return "Tuple!("~compileChildTypes().join(",")~")("~compileChildValues().join(",")~")";
132     }
133 
134 }
135 
136 template TupleCompiler(alias Parser,alias data) {
137     alias _compiler = Parser!(data);
138     //enum TupleCompiler = _compiler.compileAlias!(_compiler);
139     pragma(msg,"Tuples: \n"~_compiler.compileNode);
140     mixin("auto TupleCompiler = " ~ _compiler.compileNode ~ ";");
141 }
142 
143 template LoadGrammar(string fname,string docRoot) {
144     mixin(grammar(import(fname)));
145     enum parser = mixin(docRoot);
146 }
147 
148 template loadGrammarAndData(string grammarFile,string docRoot,string dataFile) {
149     mixin loadGrammar!(grammmarFile,docRoot);
150     enum loadGrammarAndData = parser(import(dataFile));
151 }
152 
153 template loadAndCompile(string grammarFile,string docRoot,string dataFile,alias _Compiler) {
154     enum _data = loadGrammarAndData(grammarFile,docRoot,dataFile);
155     mixin Compiler!(_Compiler,_data);
156 }
157 
158 unittest {
159     import std.array;
160     import std.typecons;
161     import std..string;
162     import std.algorithm;
163     
164     import pegged.grammar;
165 
166     enum _g = q{
167 GRAMMAR(Template):
168     Doc <- Line+ :endOfInput
169     Line <- (Var / Text)
170     Var <- :LDelim ^Template :RDelim
171     LDelim <- "{{"
172     RDelim <- "}}"
173     Text <- ~((!LDelim) Char )*
174     Char <- .
175     };
176     mixin(grammar(_g));
177     
178     enum __M = "MyStruct";
179     enum __T = "MyType";    
180 
181     enum _d = q{
182         struct {{__T}} {
183             enum v = "v";
184             struct {{__M}} {
185             };
186             static {{__M}} m;
187         };
188     };
189     
190     struct myCompiler(ParseTree T,alias Parser=myCompiler) {
191         mixin Compiler!(T,Parser);
192         //mixin (compilerDocNode!("compileChildNodes().join(\"\")"));
193         mixin (compilerOverride!("GRAMMAR.Text","T.matches.join(\"\")"));
194         mixin (compilerOverride!("identifier","mixin(T.matches.join(\"\"))"));
195     }
196 
197     pragma(msg,"Compiling:\n"~_d);
198     pragma(msg,"Tree:\n" ~ GRAMMAR!identifier(_d).toString);
199 
200     enum compiled = myCompiler!(GRAMMAR!identifier(_d)).compileNode();
201     pragma(msg,"Compiled to:\n" ~ compiled);
202     //mixin(compiled);
203     
204     mixin Compiler!(myCompiler,GRAMMAR!identifier(_d));
205     static assert(mixin("MyType.v") == "v");
206 }
207 
208 unittest {
209         
210     import pegged.grammar;
211 
212     enum _g = q{
213 GRAMMAR(Template):
214     Doc <- Line+ :endOfInput
215     Line <- (Var / Text)
216     Var <- :LDelim ^Template :RDelim
217     LDelim <- "{{"
218     RDelim <- "}}"
219     Text <- ~((!LDelim) Char )*
220     Char <- .
221     };
222     mixin(grammar(_g));
223     
224     enum __M = "MyStruct";
225     enum __T = "MyType";    
226 
227     enum _d = q{
228         struct {{__T}} {
229             enum v = "v";
230             struct {{__M}} {
231             };
232             static {{__M}} m;
233         };
234     };
235     
236     struct myTupleCompiler(ParseTree T,alias Parser=myTupleCompiler) {
237         mixin TupleCompiler!(T,Parser);
238         //mixin (compilerOverride!("GRAMMAR","compileChildNodes().join(\"\")"));
239         //mixin (compilerOverride!("identifier","\" ~ T.matches.join(\"\") ~ \",\"identifier\")"));
240     }
241     alias compiledTuple = TupleCompiler!(myTupleCompiler,GRAMMAR!identifier(_d));
242     //static assert(mixin("MyType.v") == "v");
243 }
244 
245