1 
2 //          Copyright Tim Schendekehl 2023.
3 // Distributed under the Boost Software License, Version 1.0.
4 //    (See accompanying file LICENSE_1_0.txt or copy at
5 //          https://www.boost.org/LICENSE_1_0.txt)
6 
7 /**
8 This module implements a tree creator, where all tokens, nonterminals
9 and arrays are represented using the same base class. The list of childs
10 is just an array of this class. Create an instance of `DynamicParseTreeCreator`
11 for the used grammar and pass it to the parser.
12 */
13 module dparsergen.core.dynamictree;
14 import dparsergen.core.grammarinfo;
15 import dparsergen.core.location;
16 import dparsergen.core.nodetype;
17 import dparsergen.core.nonterminalunion;
18 import dparsergen.core.parsestackelem;
19 import dparsergen.core.utils;
20 import std.algorithm;
21 import std.array;
22 import std.conv;
23 
24 /**
25 Base class for all tree nodes.
26 
27 Params:
28     Location = Type of location in source file.
29     LocationRangeImpl = Template for determining how location ranges are
30         stored (start + length, start + end, ...).
31 */
32 abstract class DynamicParseTree(Location, alias LocationRangeImpl = LocationRangeStartLength)
33 {
34     alias LocationDiff = typeof(Location.init - Location.init);
35     alias LocationRange = LocationRangeImpl!Location;
36 
37     /**
38     Location of this tree node in the source file.
39     */
40     LocationRange location;
41 
42     static foreach (field; ["startFromParent", "inputLength", "start", "end"])
43     {
44         static if (__traits(hasMember, LocationRange, field))
45         {
46             mixin("final typeof(() { LocationRange x; return x." ~ field ~ "; }()) " ~ field ~ "() const {"
47                 ~ "return location." ~ field ~ ";}");
48 
49             mixin("static if (__traits(compiles, () { LocationRange x; x." ~ field ~ " = x." ~ field ~ "; }))"
50                 ~ "final void " ~ field ~ "(typeof(() { LocationRange x; return x." ~ field ~ "; }()) n){"
51                 ~ "location." ~ field ~ " = n; }");
52         }
53     }
54 
55     static if (__traits(hasMember, LocationRange, "setStartEnd"))
56     {
57         final void setStartEnd(typeof((){ LocationRange x; return x.start; }()) start,
58                                typeof((){ LocationRange x; return x.end; }()) end)
59         {
60             location.setStartEnd(start, end);
61         }
62     }
63 
64     /**
65     Information about the grammar for this tree node.
66     */
67     immutable(GrammarInfo)* grammarInfo;
68 
69     /**
70     ID of production for this tree starting at `grammarInfo.startProductionID`
71     or `ProductionID.max` if not applicable.
72     See `grammarInfo.allProductions[productionID - grammarInfo.startProductionID]`
73     for details about the prodution.
74     */
75     ProductionID productionID;
76 
77     /**
78     ID of nonterminal for this tree starting at `grammarInfo.startNonterminalID`
79     or `SymbolID.max` if not applicable.
80     See `grammarInfo.allNonterminals[nonterminalID - grammarInfo.startNonterminalID]`
81     for details about the nonterminal.
82     */
83     SymbolID nonterminalID;
84 
85     /**
86     Type of tree node. Should match the used subclass.
87     */
88     immutable NodeType nodeType;
89 
90     /**
91     Name for nonterminals.
92     */
93     string name() const
94     in (nodeType != NodeType.token)
95     {
96         assert(0);
97     }
98 
99     /**
100     Text content for tokens.
101     */
102     string content() const
103     in (nodeType == NodeType.token)
104     {
105         assert(0);
106     }
107 
108     /**
109     Childs of this subtree. Will be empty for tokens.
110     */
111     inout(DynamicParseTree[]) childs() inout
112     {
113         return [];
114     }
115 
116     private this(SymbolID nonterminalID, ProductionID productionID,
117             NodeType nodeType, immutable GrammarInfo* grammarInfo)
118     {
119         if (nodeType == NodeType.token || nodeType == NodeType.array)
120         {
121             assert(nonterminalID == SymbolID.max);
122             assert(productionID == ProductionID.max);
123         }
124         else
125         {
126             assert(nonterminalID >= grammarInfo.startNonterminalID);
127             assert(nonterminalID - grammarInfo.startNonterminalID
128                     < grammarInfo.allNonterminals.length);
129             if (productionID != ProductionID.max)
130             {
131                 assert(productionID >= grammarInfo.startProductionID);
132                 assert(
133                         productionID - grammarInfo.startProductionID
134                         < grammarInfo.allProductions.length);
135             }
136         }
137 
138         this.nonterminalID = nonterminalID;
139         this.nodeType = nodeType;
140         this.grammarInfo = grammarInfo;
141         this.productionID = productionID;
142     }
143 
144     override string toString() const
145     {
146         return treeToString(this);
147     }
148 
149     final bool isToken() const
150     {
151         return nodeType == NodeType.token;
152     }
153 
154     /**
155     Returns the index of a child by name or size_t.max.
156     */
157     final size_t childIndexByName(string name)
158     in (name.length)
159     in (nodeType == NodeType.nonterminal)
160     in (grammarInfo !is null)
161     {
162         immutable(SymbolInstance)[] symbols = grammarInfo
163             .allProductions[productionID - grammarInfo.startProductionID].symbols;
164 
165         size_t i;
166         size_t found = size_t.max;
167         foreach (ref symbol; symbols)
168         {
169             if (symbol.dropNode)
170                 continue;
171             if (symbol.symbolInstanceName == name)
172             {
173                 found = i;
174             }
175             i++;
176         }
177         assert(i == childs.length);
178 
179         return found;
180     }
181 
182     /**
183     Checks if this subtree has a child with some name.
184     */
185     final bool hasChildWithName(string name)
186     in (name.length)
187     in (nodeType == NodeType.nonterminal)
188     in (grammarInfo !is null)
189     {
190         size_t found = childIndexByName(name);
191         return found != size_t.max;
192     }
193 
194     /**
195     Gets a child of this subtree by name.
196     */
197     final DynamicParseTree childByName(string name)
198     {
199         size_t found = childIndexByName(name);
200         assert(found != size_t.max);
201         return childs[found];
202     }
203 
204     /**
205     Gets the name of child with index `index`.
206     */
207     final string childName(size_t index)
208     in (nodeType == NodeType.nonterminal)
209     in (grammarInfo !is null)
210     {
211         immutable(SymbolInstance)[] symbols = grammarInfo
212             .allProductions[productionID - grammarInfo.startProductionID].symbols;
213         size_t i;
214         foreach (ref symbol; symbols)
215         {
216             if (symbol.dropNode)
217                 continue;
218             if (i == index)
219             {
220                 return symbols[i].symbolInstanceName;
221             }
222             i++;
223         }
224         assert(false);
225     }
226 }
227 
228 /**
229 Tree node class for token.
230 */
231 final class DynamicParseTreeToken(Location, alias LocationRangeImpl = LocationRangeStartLength)
232     : DynamicParseTree!(Location, LocationRangeImpl)
233 {
234     private string content_;
235 
236     /**
237     Creates tree node for token.
238 
239     Params:
240         content = Text for the token.
241         grammarInfo = Information about the grammar this token belongs to.
242     */
243     this(string content, immutable GrammarInfo* grammarInfo)
244     {
245         super(SymbolID.max, ProductionID.max, NodeType.token, grammarInfo);
246         this.content_ = content;
247     }
248 
249     override string content() const
250     {
251         return content_;
252     }
253 }
254 
255 /**
256 Tree node class for nonterminal.
257 */
258 final class DynamicParseTreeNonterminal(Location, alias LocationRangeImpl = LocationRangeStartLength)
259     : DynamicParseTree!(Location, LocationRangeImpl)
260 {
261     private DynamicParseTree!(Location, LocationRangeImpl)[] childs_;
262 
263     /**
264     Creates tree node for nonterminal.
265 
266     Params:
267         nonterminalID = ID for nonterminal of this tree node matching grammarInfo.
268         productionID = ID for production of this tree node matching grammarInfo.
269         childs = Childs of this tree node. The number of childs should match the production.
270         grammarInfo = Information about the grammar this nonterminal belongs to.
271     */
272     this(SymbolID nonterminalID, ProductionID productionID,
273             DynamicParseTree!(Location, LocationRangeImpl)[] childs, immutable GrammarInfo* grammarInfo)
274     {
275         super(nonterminalID, productionID, NodeType.nonterminal, grammarInfo);
276         this.childs_ = childs;
277     }
278 
279     override string name() const
280     {
281         return grammarInfo.allNonterminals[nonterminalID - grammarInfo.startNonterminalID].name;
282     }
283 
284     override inout(DynamicParseTree!(Location, LocationRangeImpl)[]) childs() inout
285     {
286         return childs_;
287     }
288 }
289 
290 /**
291 Tree node class for array.
292 */
293 final class DynamicParseTreeArray(Location, alias LocationRangeImpl = LocationRangeStartLength)
294     : DynamicParseTree!(Location, LocationRangeImpl)
295 {
296     private DynamicParseTree!(Location, LocationRangeImpl)[] childs_;
297 
298     /**
299     Creates tree node for array.
300 
301     Params:
302         childs = Childs of this tree node.
303         grammarInfo = Information about the grammar this array belongs to.
304     */
305     this(DynamicParseTree!(Location, LocationRangeImpl)[] childs, immutable GrammarInfo* grammarInfo)
306     {
307         super(SymbolID.max, ProductionID.max, NodeType.array, grammarInfo);
308         this.childs_ = childs;
309     }
310 
311     override string name() const
312     {
313         return "[]";
314     }
315 
316     override inout(DynamicParseTree!(Location, LocationRangeImpl)[]) childs() inout
317     {
318         return childs_;
319     }
320 }
321 
322 /**
323 Tree node class for merged nodes.
324 */
325 final class DynamicParseTreeMerged(Location, alias LocationRangeImpl = LocationRangeStartLength)
326     : DynamicParseTree!(Location, LocationRangeImpl)
327 {
328     private DynamicParseTree!(Location, LocationRangeImpl)[] childs_;
329 
330     /**
331     Creates tree node for merged nodes.
332 
333     Params:
334         nonterminalID = ID for nonterminal of this tree node matching grammarInfo.
335         childs = Childs of this tree node. The number of childs should match the production.
336         grammarInfo = Information about the grammar this nonterminal belongs to.
337     */
338     this(SymbolID nonterminalID,
339             DynamicParseTree!(Location, LocationRangeImpl)[] childs, immutable GrammarInfo* grammarInfo)
340     {
341         super(nonterminalID, ProductionID.max, NodeType.merged, grammarInfo);
342         this.childs_ = childs;
343     }
344 
345     override string name() const
346     {
347         return grammarInfo.allNonterminals[nonterminalID - grammarInfo.startNonterminalID].name;
348     }
349 
350     override inout(DynamicParseTree!(Location, LocationRangeImpl)[]) childs() inout
351     {
352         return childs_;
353     }
354 }
355 
356 /**
357 Type for temporarily storing array of parse trees during parsing. The array
358 will later be stored as DynamicParseTreeArray.
359 */
360 struct DynamicParseTreeTmpArray(Location, alias LocationRangeImpl = LocationRangeStartLength)
361 {
362     alias LocationDiff = typeof(Location.init - Location.init);
363     DynamicParseTree!(Location, LocationRangeImpl)[] trees;
364     Location end;
365     alias trees this;
366     enum isValid = true;
367     enum specialArrayType = true;
368 }
369 
370 /**
371 Convert tree to string.
372 */
373 void treeToString(Location, alias LocationRangeImpl = LocationRangeStartLength)(
374         const DynamicParseTree!(Location, LocationRangeImpl) tree, ref Appender!string app)
375 {
376     if (tree is null)
377     {
378         app.put("null");
379         return;
380     }
381 
382     if (tree.nodeType == NodeType.token)
383     {
384         app.put("\"");
385         foreach (dchar c; tree.content)
386             app.put(escapeChar(c, false));
387         app.put("\"");
388     }
389     else if (tree.nodeType == NodeType.array)
390     {
391         foreach (i, c; tree.childs)
392         {
393             if (i)
394                 app.put(", ");
395             treeToString(c, app);
396         }
397     }
398     else if (tree.nodeType == NodeType.nonterminal || tree.nodeType == NodeType.merged)
399     {
400         if (tree.nodeType == NodeType.merged)
401             app.put("Merged:");
402         app.put(tree.name);
403         app.put("(");
404         foreach (i, c; tree.childs)
405         {
406             if (!app.data.endsWith(", ") && !app.data.endsWith("("))
407                 app.put(", ");
408             treeToString(c, app);
409         }
410         app.put(")");
411     }
412     else
413         assert(false);
414 }
415 
416 /// ditto
417 string treeToString(Location, alias LocationRangeImpl = LocationRangeStartLength)(
418         const DynamicParseTree!(Location, LocationRangeImpl) tree)
419 {
420     Appender!string app;
421     treeToString(tree, app);
422     return app.data;
423 }
424 
425 /**
426 Class for creating trees during parsing.
427 
428 Params:
429     GrammarModule = Alias to the module with parser and information about the grammar.
430     Location_ = Type of location in source file.
431     LocationRangeImpl = Template for determining how location ranges are
432         stored (start + length, start + end, ...).
433 */
434 class DynamicParseTreeCreator(alias GrammarModule, Location_,
435         alias LocationRangeImpl = LocationRangeStartLength)
436 {
437     alias Location = Location_;
438     alias LocationDiff = typeof(Location.init - Location.init);
439     alias allTokens = GrammarModule.allTokens;
440     alias allNonterminals = GrammarModule.allNonterminals;
441     alias allProductions = GrammarModule.allProductions;
442     alias Type = DynamicParseTree!(Location, LocationRangeImpl);
443     alias NonterminalArray = DynamicParseTreeTmpArray!(Location,
444             LocationRangeImpl);
445     enum startNonterminalID = GrammarModule.startNonterminalID;
446     enum endNonterminalID = GrammarModule.endNonterminalID;
447     enum startProductionID = GrammarModule.startProductionID;
448     enum endProductionID = GrammarModule.endProductionID;
449 
450     /**
451     Type used for nonterminal with ID nonterminalID.
452     */
453     template NonterminalType(SymbolID nonterminalID)
454     {
455         static if (
456             allNonterminals[nonterminalID - startNonterminalID].flags & NonterminalFlags.array)
457             alias NonterminalType = DynamicParseTreeTmpArray!(Location,
458                     LocationRangeImpl);
459         else static if (
460             allNonterminals[nonterminalID - startNonterminalID].flags & NonterminalFlags..string)
461             alias NonterminalType = string;
462         /*else static if (allNonterminals[nonterminalID - startNonterminalID].flags == NonterminalFlags.none
463             || allNonterminals[nonterminalID - startNonterminalID].flags == NonterminalFlags.empty)
464             alias NonterminalType = typeof(null);*/
465         else
466             alias NonterminalType = DynamicParseTree!(Location, LocationRangeImpl);
467     }
468 
469     /**
470     Determines if nonterminals with ID nonterminalID can be merged into
471     a single tree node for ambiguities with the GLR parser. Function
472     `mergeParseTrees` may be called for them.
473     */
474     template canMerge(SymbolID nonterminalID)
475     {
476         enum canMerge = is(NonterminalType!nonterminalID == DynamicParseTree!(Location,
477                     LocationRangeImpl))
478             || is(NonterminalType!nonterminalID == DynamicParseTreeTmpArray!(Location,
479                     LocationRangeImpl));
480     }
481 
482     /**
483     Tagged union for different nonterminals, which is used internally by
484     the parser. The union can allow more nonterminals than necessary,
485     which can reduce template bloat.
486     */
487     alias NonterminalUnion = GenericNonterminalUnion!(DynamicParseTreeCreator).Union;
488 
489     /**
490     Tagged union of all possible nonterminals, which is used internally by
491     the parser.
492     */
493     alias NonterminalUnionAny = GenericNonterminalUnion!(DynamicParseTreeCreator).Union!(
494             SymbolID.max, size_t.max);
495 
496     /**
497     Create a tree node for one production.
498 
499     Params:
500         productionID = ID of the production starting at GrammarModule.startProductionID.
501         firstParamStart = Location at the start of this subtree.
502         lastParamEnd = Location at the end of this subtree.
503         params = Childs for this subtree, which were previously also created by createParseTree.
504     */
505     NonterminalType!(allProductions[productionID - startProductionID].nonterminalID.id) createParseTree(
506             SymbolID productionID, T...)(Location firstParamStart, Location lastParamEnd, T params)
507             if (allProductions[productionID - startProductionID].symbols.length > 0)
508     {
509         enum nonterminalID = allProductions[productionID - startProductionID].nonterminalID.id;
510         enum nonterminalName = allNonterminals[nonterminalID - startNonterminalID].name;
511         enum nonterminalFlags = allNonterminals[nonterminalID - startNonterminalID].flags;
512         assert(firstParamStart <= lastParamEnd);
513 
514         size_t numChilds;
515         DynamicParseTree!(Location, LocationRangeImpl)[] childs;
516         foreach (i, p; params)
517         {
518             static if (is(typeof(p.val) : DynamicParseTree!(Location, LocationRangeImpl)))
519             {
520                 numChilds++;
521             }
522             else static if (is(typeof(p.val) : DynamicParseTreeTmpArray!(Location, LocationRangeImpl)))
523             {
524                 static if (nonterminalFlags & NonterminalFlags.array)
525                 {
526                     if (i == 0)
527                         childs = p.val.trees;
528                     numChilds += p.val.trees.length;
529                 }
530                 else
531                 {
532                     numChilds++;
533                 }
534             }
535             else
536             {
537                 numChilds++;
538             }
539         }
540         childs.reserve(numChilds);
541         foreach (i, p; params)
542         {
543             static if (is(typeof(p.val) : DynamicParseTree!(Location, LocationRangeImpl)))
544             {
545                 childs ~= p.val;
546                 if (childs[$ - 1]!is null)
547                 {
548                     static if (isLocationRangeStartDiffLength!(LocationRangeImpl!Location))
549                         childs[$ - 1].startFromParent = p.start - firstParamStart;
550                     //childs[$ - 1].symbolInstanceAnnotations = allProductions[productionID - startProductionID].symbols[i].annotations;
551                 }
552             }
553             else static if (is(typeof(p.val) : DynamicParseTreeTmpArray!(Location, LocationRangeImpl)))
554             {
555                 static if (nonterminalFlags & NonterminalFlags.array)
556                 {
557                     if (i != 0)
558                         childs ~= p.val.trees;
559                     static if (isLocationRangeStartDiffLength!(LocationRangeImpl!Location))
560                     {
561                         foreach (x; p.val.trees)
562                         {
563                             if (x !is null)
564                             {
565                                 x.startFromParent = x.startFromParent + (p.start - firstParamStart);
566                             }
567                         }
568                     }
569                 }
570                 else
571                 {
572                     childs ~= new DynamicParseTreeArray!(Location, LocationRangeImpl)(p.val.trees,
573                             &GrammarModule.grammarInfo);
574                     static if (isLocationRangeStartDiffLength!(LocationRangeImpl!Location))
575                     {
576                         childs[$ - 1].startFromParent = p.start - firstParamStart;
577                         if (p.val.end == Location.invalid || p.start > p.val.end)
578                             childs[$ - 1].inputLength = LocationDiff();
579                         else
580                             childs[$ - 1].inputLength = p.val.end - p.start;
581                     }
582                     else
583                     {
584                         childs[$ - 1].setStartEnd(p.start, p.val.end);
585                     }
586                 }
587             }
588             else
589             {
590                 string t = text(p.val);
591                 childs ~= new DynamicParseTreeToken!(Location, LocationRangeImpl)(t,
592                         &GrammarModule.grammarInfo);
593                 static if (isLocationRangeStartDiffLength!(LocationRangeImpl!Location))
594                 {
595                     childs[$ - 1].startFromParent = p.start - firstParamStart;
596                     childs[$ - 1].inputLength = LocationDiff.fromStr(t);
597                 }
598                 else
599                 {
600                     if (t == "")
601                         childs[$ - 1].setStartEnd(Location.invalid, Location.invalid);
602                     else
603                         childs[$ - 1].setStartEnd(p.start, p.end);
604                 }
605             }
606         }
607 
608         static if (nonterminalFlags & NonterminalFlags.array)
609         {
610             return DynamicParseTreeTmpArray!(Location, LocationRangeImpl)(childs, lastParamEnd);
611         }
612         else static if (nonterminalFlags & NonterminalFlags..string)
613         {
614             string r;
615             foreach (c; childs)
616                 r ~= c.content;
617             return r;
618         }
619         else
620         {
621             auto r = new DynamicParseTreeNonterminal!(Location, LocationRangeImpl)(
622                     nonterminalID, productionID,
623                     childs, &GrammarModule.grammarInfo);
624             static if (isLocationRangeStartDiffLength!(LocationRangeImpl!Location))
625             {
626                 // r.startFromParent will be set later
627                 r.inputLength = lastParamEnd - firstParamStart;
628             }
629             else
630             {
631                 r.setStartEnd(firstParamStart, lastParamEnd);
632             }
633             //r.annotations = nonterminalAnnotations;
634             return r;
635         }
636     }
637 
638     /// ditto
639     NonterminalType!(allProductions[productionID - startProductionID].nonterminalID.id) createParseTree(SymbolID productionID)(
640             Location firstParamStart, Location lastParamEnd)
641             if (allProductions[productionID - startProductionID].symbols.length == 0)
642     {
643         //enum nonterminalID = allProductions[productionID - startProductionID].nonterminalID;
644         //enum nonterminalName = allNonterminals[nonterminalID - startNonterminalID].name;
645 
646         static if (is(typeof(return) : DynamicParseTreeTmpArray!(Location, LocationRangeImpl)))
647         {
648             return DynamicParseTreeTmpArray!(Location, LocationRangeImpl)([], Location.invalid);
649         }
650         else
651             return null;
652     }
653 
654     /**
655     Create a tree node for multiple productions, which are treated as the
656     same. This is used with the generator option --combinedreduce.
657 
658     Params:
659         nonterminalID = ID of the nonterminal starting at GrammarModule.startNonterminalID.
660         productionIDs = IDs of the productions starting at GrammarModule.startProductionID.
661             all have to be for the nonterminal with ID nonterminalID.
662         firstParamStart = Location at the start of this subtree.
663         lastParamEnd = Location at the end of this subtree.
664         params = Childs for this subtree, which were previously also created by createParseTree.
665     */
666     template createParseTreeCombined(SymbolID nonterminalID, productionIDs...)
667     {
668         NonterminalType!(allProductions[productionIDs[0] - startProductionID].nonterminalID.id) createParseTreeCombined(
669                 T...)(Location firstParamStart, Location lastParamEnd, T params)
670         {
671             NonterminalType!(allProductions[productionIDs[0] - startProductionID].nonterminalID.id) r = createParseTree!(
672                     productionIDs[0])(firstParamStart, lastParamEnd, params);
673             static if (__traits(hasMember, r, "nonterminalID"))
674                 r.nonterminalID = nonterminalID;
675             return r;
676         }
677     }
678 
679     /**
680     Called for the outermost tree node after parsing. It adjusts
681     the start location if it is stored as the offset from the parent node.
682 
683     Params:
684         result = Outermost tree node.
685         start = Start location of the tree.
686     */
687     void adjustStart(T)(T result, Location start)
688     {
689         static if (!is(typeof(result.start)))
690             if (result !is null)
691                 result.startFromParent = start - Location();
692     }
693 
694     private DynamicParseTree!(Location, LocationRangeImpl) mergeParseTreesImpl(
695             SymbolID nonterminalID, Location firstParamStart, Location lastParamEnd, ParseStackElem!(Location,
696             DynamicParseTree!(Location, LocationRangeImpl))[] trees)
697     {
698         //enum nonterminalName = allNonterminals[nonterminalID - startNonterminalID].name;
699         //assert(firstParamStart <= lastParamEnd);
700 
701         DynamicParseTree!(Location, LocationRangeImpl)[] childs;
702         foreach (i, p; trees)
703         {
704             childs ~= p.val;
705             if (childs[$ - 1]!is null)
706             {
707                 static if (isLocationRangeStartDiffLength!(LocationRangeImpl!Location))
708                     childs[$ - 1].startFromParent = p.start - firstParamStart;
709             }
710         }
711 
712         auto r = new DynamicParseTreeMerged!(Location, LocationRangeImpl)(
713                 nonterminalID, childs, &GrammarModule.grammarInfo);
714         static if (isLocationRangeStartDiffLength!(LocationRangeImpl!Location))
715         {
716             // r.startFromParent will be set later
717             r.inputLength = lastParamEnd - firstParamStart;
718         }
719         else
720         {
721             r.setStartEnd(firstParamStart, lastParamEnd);
722         }
723         //r.annotations = allNonterminals[nonterminalID - startNonterminalID].annotations;
724         return r;
725     }
726 
727     private DynamicParseTreeTmpArray!(Location, LocationRangeImpl) mergeParseTreesImplArray(
728             SymbolID nonterminalID, Location firstParamStart, Location lastParamEnd, ParseStackElem!(Location,
729             DynamicParseTreeTmpArray!(Location, LocationRangeImpl))[] trees)
730     {
731         //enum nonterminalName = allNonterminals[nonterminalID - startNonterminalID].name;
732         //assert(firstParamStart <= lastParamEnd);
733 
734         size_t commonPrefix;
735         outer: while (true)
736         {
737             foreach (p; trees)
738             {
739                 if (p.val.trees.length <= commonPrefix)
740                     break outer;
741             }
742             foreach (i, p; trees[0 .. $ - 1])
743             {
744                 if (trees[i].val.trees[commonPrefix] !is trees[i + 1].val.trees[commonPrefix])
745                     break outer;
746             }
747             commonPrefix++;
748         }
749 
750         DynamicParseTree!(Location, LocationRangeImpl)[] childs;
751         foreach (i, p; trees)
752         {
753             childs ~= new DynamicParseTreeArray!(Location, LocationRangeImpl)(
754                     p.val.trees[commonPrefix .. $], &GrammarModule.grammarInfo);
755             Location start = p.start;
756             foreach (c; p.val.trees[commonPrefix .. $])
757             {
758                 if (c !is null)
759                 {
760                     static if (isLocationRangeStartDiffLength!(LocationRangeImpl!Location))
761                         start = p.start + c.startFromParent;
762                     else
763                         start = c.start;
764                     break;
765                 }
766             }
767             static if (isLocationRangeStartDiffLength!(LocationRangeImpl!Location))
768             {
769                 childs[$ - 1].startFromParent = start - firstParamStart;
770                 if (p.val.end == Location.invalid || start > p.val.end)
771                     childs[$ - 1].inputLength = LocationDiff();
772                 else
773                     childs[$ - 1].inputLength = p.val.end - p.start;
774             }
775             else
776             {
777                 childs[$ - 1].setStartEnd(p.start, p.val.end);
778             }
779         }
780 
781         auto r = new DynamicParseTreeMerged!(Location, LocationRangeImpl)(
782                 nonterminalID, childs, &GrammarModule.grammarInfo);
783         static if (isLocationRangeStartDiffLength!(LocationRangeImpl!Location))
784         {
785             // r.startFromParent will be set later
786             r.inputLength = lastParamEnd - firstParamStart;
787         }
788         else
789         {
790             r.setStartEnd(firstParamStart, lastParamEnd);
791         }
792         //r.annotations = allNonterminals[nonterminalID - startNonterminalID].annotations;
793         return DynamicParseTreeTmpArray!(Location, LocationRangeImpl)(
794                 trees[0].val.trees[0 .. commonPrefix] ~ r, lastParamEnd);
795     }
796 
797     /**
798     Creates a special tree node, which contains different ambiguous trees
799     as childs. This is used by GLR parsers. It will only be called by the
800     parser if canMerge!nonterminalID is true.
801 
802     Params:
803         nonterminalID = ID of the nonterminal starting at GrammarModule.startNonterminalID.
804         firstParamStart = Location at the start of this subtree.
805         lastParamEnd = Location at the end of this subtree.
806         trees = Childs for this subtree.
807         mergeInfo = Name for this tree node or empty for automatically
808             generated name.
809     */
810     NonterminalType!(nonterminalID) mergeParseTrees(SymbolID nonterminalID)(Location firstParamStart, Location lastParamEnd,
811             ParseStackElem!(Location, NonterminalType!nonterminalID)[] trees)
812     {
813         static if (is(NonterminalType!nonterminalID == DynamicParseTreeTmpArray!(Location,
814                 LocationRangeImpl)))
815             return mergeParseTreesImplArray(nonterminalID, firstParamStart, lastParamEnd, trees);
816         else
817             return mergeParseTreesImpl(nonterminalID, firstParamStart, lastParamEnd, trees);
818     }
819 }
820 
821 private void printTree(Location, alias LocationRangeImpl)
822     (imported!"std.stdio".File file, DynamicParseTree!(Location, LocationRangeImpl) tree, ref Appender!(char[]) appIndent, bool isLast, bool verbose)
823 {
824     import std.stdio;
825 
826     write(appIndent.data);
827     write("+-");
828     if (tree is null)
829     {
830         writeln("null");
831         return;
832     }
833     if (tree.nodeType == NodeType.token)
834         write("\"", tree.content.escapeD, "\"");
835     else
836     {
837         if (tree.nodeType == NodeType.merged)
838             write("Merged:");
839         write(tree.name);
840     }
841     if (tree.start == Location.invalid)
842         writeln(" <???>");
843     else
844         writeln(" <", tree.start.toPrettyString, ">");
845     size_t indentLength = appIndent.data.length;
846     if (!isLast)
847         appIndent.put("| ");
848     else
849         appIndent.put("  ");
850     if (verbose)
851     {
852         foreach (i, child; tree.childs)
853         {
854             printTree(file, child, appIndent, i + 1 >= tree.childs.length, verbose);
855         }
856     }
857     else
858     {
859         typeof(tree) lastChild;
860         foreach (child; tree.childs)
861         {
862             if (child is null)
863                 continue;
864             if (child.nodeType == NodeType.array)
865             {
866                 foreach (child2; child.childs)
867                     if (child2 !is null)
868                         lastChild = child2;
869             }
870             else
871                 lastChild = child;
872         }
873         foreach (i, child; tree.childs)
874         {
875             if (child is null)
876                 continue;
877             if (child.nodeType == NodeType.array)
878             {
879                 foreach (child2; child.childs)
880                     if (child2 !is null)
881                         printTree(file, child2, appIndent, child2 is lastChild, verbose);
882             }
883             else
884                 printTree(file, child, appIndent, child is lastChild, verbose);
885         }
886     }
887     appIndent.shrinkTo(indentLength);
888 }
889 
890 /**
891 Print the tree to a file.
892 
893 Params:
894     file = File for output.
895     tree = The tree to print.
896     verbose = Include all nodes in the tree.
897 */
898 void printTree(Location, alias LocationRangeImpl)
899     (imported!"std.stdio".File file, DynamicParseTree!(Location, LocationRangeImpl) tree, bool verbose = false)
900 {
901     Appender!(char[]) appIndent;
902     printTree(file, tree, appIndent, true, verbose);
903 }