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 }