1 /**
2 Dynamic grammars:
3     - no CT parsing (this one will stay)
4     - no memoization (could be added)
5     - slightly less handy parameterized rules (could be better)
6     - no semantic actions (drat, this one is the worse)
7     - no calling from other grammars?
8 
9 Advantages:
10     - fully runtime configurable: change rules, add rules, delete rules.
11 */
12 module pegged.dynamic.grammar;
13 
14 import std.array;
15 import std.stdio;
16 
17 import pegged.peg;
18 import pegged.parser;
19 import pegged.dynamic.peg;
20 
21 struct ParameterizedRule
22 {
23     size_t numArgs;
24     Dynamic delegate(Dynamic[]) code;
25 
26     Dynamic opCall(D...)(D rules)
27     {
28         Dynamic[] args;
29         foreach(i,rule; rules)
30         {
31             static if (is(typeof(rule) == Dynamic))
32                 args ~= rule;
33             else
34                 args ~= rule(); // Dynamic delegate()
35         }
36 
37         if(rules.length == numArgs)
38         {
39             switch(numArgs)
40             {
41                 case 1:
42                     return code([args[0]]);
43                 case 2:
44                     return code([args[0], args[1]]);
45                 case 3:
46                     return code([args[0], args[1], args[2]]);
47                 case 4:
48                     return code([args[0], args[1], args[2], args[3]]);
49                 case 5:
50                     return code([args[0], args[1], args[2], args[3], args[4]]);
51                 case 6:
52                     return code([args[0], args[1], args[2], args[3], args[4], args[5]]);
53                 default:
54                     throw new Exception("Unimplemented parameterized rule with more than 6 arguments.");
55             }
56         }
57         else
58             throw new Exception( "ParameterizedRule called with "
59                                ~ to!string(rules.length)
60                                ~ " args. Waited for "
61                                ~ to!string(numArgs) ~ ".");
62         return code(args);
63     }
64 }
65 
66 ParameterizedRule parameterizedRule(size_t n, Dynamic delegate(Dynamic[] d) code)
67 {
68     ParameterizedRule pr;
69     pr.numArgs = n;
70     pr.code = code;
71     return pr;
72 }
73 
74 struct DynamicGrammar
75 {
76     string grammarName;
77     string startingRule;
78     Dynamic[string] rules;
79     ParameterizedRule[string] paramRules;
80 
81     ParseTree decimateTree(ParseTree p)
82     {
83         if(p.children.length == 0) return p;
84 
85         ParseTree[] filterChildren(ParseTree pt)
86         {
87             ParseTree[] result;
88             foreach(child; pt.children)
89             {
90                 if (  (child.name.startsWith(grammarName) && child.matches.length != 0)
91                    || !child.successful && child.children.length == 0)
92                 {
93                     child.children = filterChildren(child);
94                     result ~= child;
95                 }
96                 else if (child.name.startsWith("keep!(")) // 'keep' node are never discarded.
97                                                // They have only one child, the node to keep
98                 {
99                     result ~= child.children[0];
100                 }
101                 else // discard this node, but see if its children contain nodes to keep
102                 {
103                     result ~= filterChildren(child);
104                 }
105             }
106             return result;
107         }
108         p.children = filterChildren(p);
109         return p;
110     }
111 
112     ParseTree opCall(ParseTree p)
113     {
114         return decimateTree(rules[startingRule](p));
115         /+
116         result.children = [result];
117         result.name = grammarName;
118         return result;
119         +/
120     }
121 
122     ParseTree opCall(string input)
123     {
124         return decimateTree(rules[startingRule](ParseTree(``, false, [], input, 0, 0)));
125         /+
126         result.children = [result];
127         result.name = grammarName;
128         return result;
129         +/
130     }
131 
132     void opIndexAssign(D)(D code, string s)
133     {
134         rules[s]= named(code, grammarName ~ "." ~ s);
135     }
136 
137     Dynamic opIndex(string s)
138     {
139         return rules[s];
140     }
141 }
142 
143 // Helper to insert 'Spacing' before and after Primaries
144 ParseTree spaceArrow(ParseTree input)
145 {
146     ParseTree wrapInSpaces(ParseTree p)
147     {
148         ParseTree spacer =
149         ParseTree("Pegged.Prefix", true, null, null, 0,0, [
150             ParseTree("Pegged.Suffix", true, null, null, 0, 0, [
151                 ParseTree("Pegged.Primary", true, null, null, 0, 0, [
152                     ParseTree("Pegged.RhsName", true, null, null, 0,0, [
153                         ParseTree("Pegged.Identifier", true, ["Spacing"])
154                     ])
155                 ])
156             ])
157         ]);
158         ParseTree result = ParseTree("Pegged.WrapAround", true, p.matches, p.input, p.begin, p.end, p.children);
159         result.children = spacer ~ result.children ~ spacer;
160         return result;
161     }
162     return modify!( p => p.name == "Pegged.Primary",
163                     wrapInSpaces)(input);
164 }
165 
166 Dynamic makeRule(string def, Dynamic[string] context)
167 {
168     ParseTree p = Pegged.decimateTree(Pegged.Definition(def));
169     return makeRule(p, context);
170 }
171 
172 Dynamic makeRule(ParseTree def, Dynamic[string] context)
173 {
174     Dynamic code;
175 
176     Dynamic getDyn(string name)
177     {
178         if (name in context)
179             return context[name];
180         else
181             throw new Exception("Unknown name: " ~ name);
182     }
183 
184     Dynamic ruleFromTree(ParseTree p)
185     {
186         //writeln("rfT: ", p.name, " ", p.matches);
187         //Dynamic result;
188         switch(p.name)
189         {
190             case "Pegged.Expression":
191                 //if (p.children.length > 1) // OR expression
192                 //{
193                     Dynamic[] children;
194                     foreach(seq; p.children)
195                         children ~= ruleFromTree(seq);
196                     return distribute!(or)(children);
197                 //}
198                 //else // One child -> just a sequence, no need for a or!( , )
199                 //{
200                 //    return ruleFromTree(p.children[0]);
201                 //}
202                 //break;
203             case "Pegged.Sequence":
204                 //if (p.children.length > 1) // real sequence
205                 //{
206                     Dynamic[] children;
207                     foreach(seq; p.children)
208                         children ~= ruleFromTree(seq);
209                     return distribute!(pegged.dynamic.peg.and)(children);
210                 /+}
211                 else // One child -> just a Suffix, no need for a and!( , )
212                 {
213                     return ruleFromTree(p.children[0]);
214                 }
215                 +/
216             case "Pegged.Prefix":
217                 ParseTree temp;
218                 foreach_reverse(i,child; p.children[0..$-1]) // transforming a list into a linear tree
219                 {
220                     temp  = p.children[$-1];
221                     p.children[$-1] = child;
222                     p.children[$-1].children = [temp];
223                 }
224                 return ruleFromTree(p.children[$-1]);
225             case "Pegged.Suffix":
226                 foreach(child; p.children[1..$])
227                 {
228                     ParseTree temp = p.children[0];
229                     p.children[0] = child;
230                     p.children[0].children = [temp];
231                 }
232                 return ruleFromTree(p.children[0]);
233             case "Pegged.Primary":
234                 return ruleFromTree(p.children[0]);
235             case "Pegged.RhsName":
236                 return ruleFromTree(p.children[0]);
237             case "Pegged.Identifier":
238                 //writeln("Identifier: ", p.matches[0], " ");//, getDyn(p.matches[0])()(ParseTree()).name);
239                 return getDyn(p.matches[0]);
240             case "Pegged.Literal":
241                 //writeln("Literal: ", p.matches);
242                 if(p.matches.length == 3) // standard case
243                     return literal(p.matches[1]);
244                 else // only two children -> empty literal
245                     return eps();
246             case "Pegged.CharClass":
247                 if (p.children.length > 1)
248                 {
249                     Dynamic[] children;
250                     foreach(seq; p.children)
251                         children ~= ruleFromTree(seq);
252                     return distribute!(pegged.dynamic.peg.or)(children);
253                 }
254                 else // One child -> just a sequence, no need for a or!( , )
255                 {
256                     return ruleFromTree(p.children[0]);
257                 }
258                 //break;
259             case "Pegged.CharRange":
260                 /// Make the generation at the Char level: directly what is needed, be it `` or "" or whatever
261                 if (p.children.length > 1) // a-b range
262                 {
263                     return charRange(p.matches[0].front, p.matches[2].front);
264                 }
265                 else // lone char
266                 {
267                     //result = "pegged.peg.literal!(";
268                     string ch = p.matches[0];
269                     switch (ch)
270                     {
271                         case "\\[":
272                         case "\\]":
273                         case "\\-":
274                             return literal(ch[0..1]);
275                         case "\\\'":
276                             return literal("\'");
277                         case "\\`":
278                             return literal("`");
279                         case "\\":
280                         case "\\\\":
281                             return literal("\\");
282                         case "\"":
283                         case "\\\"":
284                             return literal("\"");
285                         case "\n":
286                         case "\r":
287                         case "\t":
288                             return literal(ch);
289                         default:
290                             return literal(ch);
291                     }
292                 }
293             case "Pegged.Char":
294                 string ch = p.matches[0];
295                 switch (ch)
296                 {
297                     case "\\[":
298                     case "\\]":
299                     case "\\-":
300 
301                     case "\\\'":
302                     case "\\\"":
303                     case "\\`":
304                     case "\\\\":
305                         return literal(ch[1..$]);
306                     case "\n":
307                     case "\r":
308                     case "\t":
309                         return literal(ch);
310                     default:
311                         return literal(ch);
312                 }
313                 //break;
314             case "Pegged.POS":
315                 return posLookahead(ruleFromTree(p.children[0]));
316             case "Pegged.NEG":
317                 return negLookahead(ruleFromTree(p.children[0]));
318             case "Pegged.FUSE":
319                 return fuse(ruleFromTree(p.children[0]));
320             case "Pegged.DISCARD":
321                 return discard(ruleFromTree(p.children[0]));
322             case "Pegged.KEEP":
323                 return keep(ruleFromTree(p.children[0]));
324             case "Pegged.DROP":
325                 return drop(ruleFromTree(p.children[0]));
326             case "Pegged.PROPAGATE":
327                 return propagate(ruleFromTree(p.children[0]));
328             case "Pegged.OPTION":
329                 return option(ruleFromTree(p.children[0]));
330             case "Pegged.ZEROORMORE":
331                 return zeroOrMore(ruleFromTree(p.children[0]));
332             case "Pegged.ONEORMORE":
333                 return oneOrMore(ruleFromTree(p.children[0]));
334             case "Pegged.Action":
335                 Dynamic result = ruleFromTree(p.children[0]);
336                 foreach(act; p.matches[1..$])
337                     result = action(result, getDyn(act));
338                 return result;
339             case "Pegged.ANY":
340                 return any();
341             case "Pegged.WrapAround":
342                 return wrapAround( ruleFromTree(p.children[0])
343                                         , ruleFromTree(p.children[1])
344                                         , ruleFromTree(p.children[2]));
345             default:
346                 throw new Exception("Bad tree: " ~ p.toString());
347         }
348     }
349 
350     switch(def.children[1].children[0].name)
351     {
352         case "Pegged.LEFTARROW":
353             code = ruleFromTree(def.children[2]);
354             break;
355         case "Pegged.FUSEARROW":
356             code = fuse(ruleFromTree(def.children[2]));
357             break;
358         case "Pegged.DISCARDARROW":
359             code = discard(ruleFromTree(def.children[2]));
360             break;
361         case "Pegged.KEEPARROW":
362             code = keep(ruleFromTree(def.children[2]));
363             break;
364         case "Pegged.DROPARROW":
365             code = drop(ruleFromTree(def.children[2]));
366             break;
367         case "Pegged.PROPAGATEARROW":
368             code = propagate(ruleFromTree(def.children[2]));
369             break;
370         case "Pegged.SPACEARROW":
371             ParseTree modified = spaceArrow(def.children[2]);
372             code = ruleFromTree(modified);
373             break;
374         case "Pegged.ACTIONARROW":
375             Dynamic actionResult = ruleFromTree(def.children[2]);
376             foreach(act; def.children[1].matches[1..$])
377                 actionResult = action(actionResult, getDyn(act));
378             code = actionResult;
379             break;
380         default:
381             throw new Exception("Unknow arrow: " ~ def.children[1].children[0].name);
382             //break;
383     }
384 
385     return named(code, def.matches[0]);
386 }
387 
388 DynamicGrammar grammar(string definition, Dynamic[string] context = null)
389 {
390     //writeln("Entering dyn gram");
391     ParseTree defAsParseTree = Pegged(definition);
392     //writeln(defAsParseTree);
393     if (!defAsParseTree.successful)
394     {
395         // To work around a strange bug with ParseTree printing at compile time
396         throw new Exception("Bad grammar input: " ~ defAsParseTree.toString(""));
397     }
398 
399     DynamicGrammar gram;
400     foreach(name, rule; context)
401     {
402         gram.rules[name] = rule;
403     }
404 
405     ParseTree p = defAsParseTree.children[0];
406 
407     string grammarName = p.children[0].matches[0];
408     string shortGrammarName = p.children[0].matches[0];
409     gram.grammarName = shortGrammarName;
410 
411     // Predefined spacing
412     gram.rules["Spacing"] = discard(zeroOrMore(or(literal(" "), literal("\t"), literal("\n"), literal("\r"))));
413 
414     ParseTree[] definitions = p.children[1 .. $];
415 
416     foreach(i,def; definitions)
417     {
418         gram[def.matches[0]] = fail();
419     }
420 
421     foreach(i,def; definitions)
422     {
423         string shortName = def.matches[0];
424 
425         if (i == 0)
426             gram.startingRule = shortName;
427         // prepending the global grammar name, to get a qualified-name rule 'Gram.Rule'
428         def.matches[0] = shortGrammarName ~ "." ~ def.matches[0];
429         gram.rules[shortName] = makeRule(def, gram.rules);
430     }
431 
432     return gram;
433 }
434 
435 Dynamic distribute(alias fun)(Dynamic[] args)
436 {
437     //mixin(makeSwitch(40));
438     switch(args.length)
439     {
440         case 1:
441             return fun(args[0]);
442         case 2:
443             return fun(args[0], args[1]);
444         case 3:
445             return fun(args[0], args[1], args[2]);
446         case 4:
447             return fun(args[0], args[1], args[2], args[3]);
448         case 5:
449             return fun(args[0], args[1], args[2], args[3], args[4]);
450         case 6:
451             return fun(args[0], args[1], args[2], args[3], args[4], args[5]);
452         default:
453             return fun(fun(args[0], args[1], args[2], args[3], args[4], args[5]), distribute!fun(args[6..$]));
454     }
455 }