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 module dparsergen.generator.glrparsercodegen; 8 import dparsergen.core.utils; 9 import dparsergen.generator.codewriter; 10 import dparsergen.generator.grammar; 11 import dparsergen.generator.parser; 12 import dparsergen.generator.parsercodegencommon; 13 import dparsergen.generator.production; 14 import std.algorithm; 15 import std.conv; 16 import std.range; 17 import std.typecons; 18 19 bool needsExtraStateData(LRGraph graph, size_t stateNr, const LRGraphNode node) 20 { 21 auto grammar = graph.grammar; 22 foreach (e; node.elements) 23 { 24 if (e.isNextValid(grammar) && e.next(grammar).negLookaheads.length > 0) 25 return true; 26 } 27 return false; 28 } 29 30 void createExtraStateData(ref CodeWriter code, LRGraph graph, size_t stateNr, const LRGraphNode node) 31 { 32 auto grammar = graph.grammar; 33 bool[Symbol] usedNegLookahead; 34 mixin(genCode("code", q{ 35 static struct $(parseFunctionName(graph, stateNr, "StateData")) 36 { 37 $$foreach (e; node.elements) { 38 $$if (e.isNextValid(grammar)) { 39 $$foreach (n; e.next(grammar).negLookaheads) { 40 $$if (n !in usedNegLookahead) { 41 bool disallow$(symbolNameCode(grammar, n)); 42 $$usedNegLookahead[n] = true; 43 $$} 44 $$} 45 $$} 46 $$} 47 } 48 })); 49 } 50 51 bool isSingleReduceState(LRGraph graph, size_t stateNr) 52 { 53 auto grammar = graph.grammar; 54 const LRGraphNode node = graph.states[stateNr]; 55 return node.elements.length == 1 && node.elements[0].isFinished(grammar) 56 && !node.elements[0].isStartElement; 57 } 58 59 void createParseFunction(ref CodeWriter code, LRGraph graph, size_t stateNr, const LRGraphNode node) 60 { 61 auto grammar = graph.grammar; 62 immutable endTok = grammar.tokens.getID("$end"); 63 64 createParseStateComments(code, graph, stateNr, node); 65 66 if (node.hasSetStackSymbols && !graph.globalOptions.directUnwrap) 67 { 68 code.writeln("// hasSetStackSymbols => skipped ", parseFunctionName(graph, stateNr)); 69 code.writeln(); 70 return; 71 } 72 73 ActionTable actionTable = genActionTable(graph, node); 74 75 void actionCode(Action bestAction) 76 { 77 assert(bestAction.type != ActionType.none); 78 79 code.writeln("// ", bestAction.type); 80 81 if (bestAction.type == ActionType.shift) 82 { 83 //code.writeln("writeln(\"shift token \", input.front);"); 84 85 if (!isSingleReduceState(graph, bestAction.newState)) 86 { 87 code.writeln("StackNode *nextNode = shift!true(stackNode, start, ", 88 bestAction.newState, ", tokenContent);"); 89 } 90 else 91 { 92 code.writeln("StackNode *nextNode = shift!false(stackNode, start, ", 93 bestAction.newState, ", tokenContent);"); 94 } 95 96 auto nextNode = graph.states[bestAction.newState]; 97 if (isSingleReduceState(graph, bestAction.newState)) 98 { 99 foreach (e; nextNode.elements) 100 { 101 if (e.isFinished(grammar) && !e.isStartElement) 102 { 103 code.writeln("{").incIndent; 104 code.writeln("// early reduce ", grammar.productionString(e.production)); 105 code.writeln(reduceFunctionName(graph, e.production), 106 "(nextNode, start, end, end, true);"); 107 code.decIndent.writeln("}"); 108 } 109 } 110 } 111 } 112 else if (bestAction.type == ActionType.reduce) 113 { 114 code.writeln(reduceFunctionName(graph, bestAction.production), 115 "(stackNode, start, end, lastTokenEnd, false);"); 116 } 117 else if (bestAction.type == ActionType.delayedReduce) 118 { 119 assert(false); 120 } 121 else if (bestAction.type == ActionType.accept) 122 { 123 code.writeln("acceptedStackTops.addOnce(stackNode);"); 124 } 125 else if (bestAction.type == ActionType.descent) 126 { 127 assert(false); 128 } 129 else if (bestAction.type == ActionType.conflict) 130 { 131 mixin(genCode("code", q{ 132 bool anyGood = false; 133 ParseException[] exceptions; 134 $$foreach (a2; bestAction.conflictActions) { 135 try 136 { 137 $$actionCode(a2); 138 anyGood = true; 139 } 140 catch(ParseException e) 141 { 142 exceptions ~= e; 143 } 144 $$} 145 if (!anyGood) 146 { 147 if (exceptions.length == 1) 148 throw exceptions[0]; 149 else 150 throw new MultiParseException("", exceptions); 151 } 152 })); 153 } 154 else 155 assert(false); 156 } 157 158 static struct ActionX 159 { 160 Action action; 161 Tuple!(TokenID, string)[] conditions; 162 } 163 164 ActionX[] actions; 165 size_t[Action] actionsMap; 166 167 foreach (t; actionTable.actions.sortedKeys) 168 { 169 auto a = actionTable.actions[t]; 170 foreach (subToken; a.sortedKeys) 171 { 172 auto a2 = a[subToken]; 173 auto x = tuple!(TokenID, string)(t, subToken); 174 if (a2 !in actionsMap) 175 { 176 actionsMap[a2] = actions.length; 177 actions ~= ActionX(a2); 178 } 179 actions[actionsMap[a2]].conditions ~= x; 180 } 181 } 182 183 bool[Symbol] usedNegLookahead; 184 foreach (e; node.elements) 185 { 186 if (e.isNextValid(grammar)) 187 foreach (n; e.next(grammar).negLookaheads) 188 if (n !in usedNegLookahead) 189 { 190 usedNegLookahead[n] = true; 191 } 192 } 193 194 mixin(genCode("code", q{ 195 private void $(parseFunctionName(graph, stateNr, "pushTokenState"))( _ 196 StackNode *stackNode, size_t tokenId, Token tokenContent, Location start, Location end) 197 { 198 $$if (node.elements.simpleLLState) { 199 assert(false, "Not used for GLR parser"); 200 $$} else if (node.type == LRGraphNodeType.backtrack) { 201 assert(false, "Not used for GLR parser"); 202 $$} else if (node.type == LRGraphNodeType.lookahead) { 203 assert(false, "Not used for GLR parser"); 204 $$} else { 205 $$string ifPrefix=""; 206 $$foreach (l; usedNegLookahead.sortedKeys) { 207 $$if (l.isToken) { 208 if (tokenId == getTokenID!$(grammar.tokens[l.toTokenID].tokenDCode)) 209 stackNode.$(parseFunctionName(graph, stateNr, "stateData")).disallow$(symbolNameCode(grammar, l)) = true; 210 $$} 211 $$} 212 $$foreach (a; actions) { 213 $(ifPrefix)if ( _ 214 $$CommaGen orCode = CommaGen(" || "); 215 $$foreach (c; a.conditions) { 216 $$if (c[1] == "") { 217 $(orCode())tokenId == getTokenID!$(grammar.tokens[c[0]].tokenDCode) _ 218 $$} else { 219 $(orCode())(tokenId == getTokenID!$(grammar.tokens[c[0]].tokenDCode) && tokenContent == $(c[1])) _ 220 $$} 221 $$} 222 ) 223 { 224 $$actionCode(a.action); 225 } 226 $$ifPrefix = "else "; 227 $$} 228 $$if (actionTable.defaultReduceAction.type != ActionType.none) { 229 $(ifPrefix == "else " ? "else" : ifPrefix) 230 { 231 $$actionCode(actionTable.defaultReduceAction); 232 } 233 $$} else if (endTok in actionTable.actions) { 234 $(ifPrefix == "else " ? "else" : ifPrefix) 235 { 236 $$actionCode(actionTable.actions[endTok][""]); 237 } 238 $$} else { 239 $(ifPrefix == "else " ? "else" : ifPrefix) 240 { 241 $$if (endTok in actionTable.actions) { 242 throw new SingleParseException!Location(text("unexpected Token \"", tokenContent, "\" \"", getTokenName(tokenId), "\""), 243 start, end); 244 $$} else { 245 if (tokenId == getTokenID!$(grammar.tokens[endTok].tokenDCode)) 246 throw new SingleParseException!Location("EOF", 247 start, end); 248 else 249 throw new SingleParseException!Location(text("unexpected Token \"", tokenContent, "\" \"", getTokenName(tokenId), "\""), 250 start, end); 251 $$} 252 assert(0); 253 } 254 $$} 255 $$} 256 } 257 })); 258 259 if (!needsJumps(graph, stateNr, node, actionTable)) 260 return; 261 262 mixin(genCode("code", q{ 263 private void $(parseFunctionName(graph, stateNr, "pushTokenState"))( _ 264 StackNode *stackNode, PendingReduce pendingReduce, Location tokenStart, Location tokenEnd, bool alreadyShifted$(grammar.tags.vals.length ? ", Tag tags" : "")) 265 { 266 size_t newState; 267 bool setStackTops = true; 268 switch (pendingReduce.nonterminalID) 269 { 270 $$foreach (nonterminalID; actionTable.jumps.sortedKeys) { 271 $$auto jumps2 = actionTable.jumps[nonterminalID]; 272 case $(grammar.nonterminalIDCode(nonterminalID)): 273 $$if (nonterminalID in usedNegLookahead) { 274 stackNode.$(parseFunctionName(graph, stateNr, "stateData")).disallow$(symbolNameCode(grammar, nonterminalID)) = true; 275 $$} 276 $$CommaGen elseCode = CommaGen("else "); 277 $$bool hasDisallowCheck; 278 $$foreach (checkDisallowedSymbols; jumps2.sortedKeys) { 279 $$auto newState = jumps2[checkDisallowedSymbols]; 280 $$if (checkDisallowedSymbols.length == 0) { 281 $$if (newState == size_t.max) { 282 $$assert(false); 283 $$} else { 284 $$auto nextNode = graph.states[newState]; 285 $$if (isSingleReduceState(graph, newState)) { 286 newState = $(newState); 287 setStackTops = !alreadyShifted; 288 $$} else { 289 newState = $(newState); 290 $$} 291 $$if (isSingleReduceState(graph, newState)) { 292 setStackTops = false; 293 $$} 294 $$} 295 $$} else { 296 $$CommaGen and = CommaGen(" && "); 297 $$hasDisallowCheck = true; 298 $(elseCode())if ( _ 299 $$foreach (x; checkDisallowedSymbols) { 300 $(and())stackNode.$(parseFunctionName(graph, stateNr, "stateData")).disallow$(symbolNameCode(grammar, x[0])) == $(x[1]) _ 301 $$} 302 ) 303 { 304 $$if (newState == size_t.max) { 305 $$assert(false); 306 $$} else { 307 $$auto nextNode = graph.states[newState]; 308 $$if (isSingleReduceState(graph, newState)) { 309 newState = $(newState); 310 setStackTops = !alreadyShifted; 311 $$} else { 312 newState = $(newState); 313 $$} 314 $$if (isSingleReduceState(graph, newState)) { 315 setStackTops = false; 316 $$} 317 $$} 318 } 319 $$} 320 $$} 321 $$if (hasDisallowCheck) { 322 else assert(false); 323 $$} 324 break; 325 $$} 326 default: 327 assert(0, text("no jump ", pendingReduce.nonterminalID)); 328 } 329 StackNode* stackNodeNew = shift(stackNode, newState, pendingReduce, alreadyShifted, setStackTops$(grammar.tags.vals.length ? ", tags" : "")); 330 } 331 })); 332 } 333 334 void createReduceFunction(ref CodeWriter code, LRGraph graph, const Production* production) 335 { 336 auto grammar = graph.grammar; 337 const RewriteRule[] rewriteRules = grammar.getRewriteRules(production); 338 assert(rewriteRules.length > 0); 339 340 string[] rewriteParameters; 341 foreach (k; 0 .. production.symbols.length) 342 { 343 if (!production.symbols[k].dropNode) 344 rewriteParameters ~= text("stack", k); 345 else 346 rewriteParameters ~= "undefinedIdentifier/*error*/"; 347 } 348 349 size_t[] prevStateSet(const size_t[] states) 350 { 351 size_t[] r; 352 foreach (s; states) 353 foreach (edge; graph.states[s].reverseEdges) 354 r.addOnce(edge.next); 355 r.sort(); 356 return r; 357 } 358 359 mixin(genCode("code", q{ 360 private static StackEdgeData* $(reduceFunctionName(graph, production, "reduceImpl"))(ref PushParser this_, _ 361 StackEdge*[] parameterEdges, Location lastTokenEnd) 362 { 363 assert(parameterEdges.length == $(production.symbols.length)); 364 StackEdgeData*[] parameterEdgesData; 365 parameterEdgesData.length = $(production.symbols.length); 366 $$foreach (k; 0 .. production.symbols.length) { 367 parameterEdgesData[$(k)] = parameterEdges[$(k)].data; 368 $$} 369 StackEdgeData* r = new StackEdgeData(false); 370 $$foreach (k; 1 .. production.symbols.length + 1) { 371 StackEdgeData *stackEdge$(k) = parameterEdges[$ - $(k)].data; // $(grammar.symbolInstanceToString(production.symbols[$ - k])) 372 $$} 373 $$foreach (k; 1 .. production.symbols.length + 2) { 374 $$if (k <= production.symbols.length) { 375 $$if (production.symbols[$ - k].isToken) { 376 assert(stackEdge$(k).isToken); 377 $$} else if (graph.globalOptions.directUnwrap) { 378 assert(!stackEdge$(k).isToken && stackEdge$(k).nonterminal.nonterminalID.among( _ 379 $$foreach (n; grammar.directUnwrapClosure(production.symbols[$ - k])) { 380 $(grammar.nonterminalIDCode(n.nonterminalID)), _ 381 $$} 382 ), text(stackEdge$(k).isToken, " ", stackEdge$(k).nonterminal.nonterminalID, " ", cast(void*) stackEdge$(k))); 383 $$} else { 384 assert(!stackEdge$(k).isToken && stackEdge$(k).nonterminal.nonterminalID == $(grammar.nonterminalIDCode(production.symbols[$ - k].toNonterminalID))); 385 $$} 386 assert(stackEdge$(k).start.bytePos != size_t.max); 387 $$} 388 $$} 389 $$if (grammar.isSimpleProduction(*production) && !grammar.nonterminals[production.nonterminalID].annotations.contains!"array"()) { 390 $$size_t nrNotDropped=size_t.max; 391 $$foreach (k, s; production.symbols) { 392 $$if (!s.dropNode) { 393 $$nrNotDropped = k; 394 $$} 395 $$} 396 NonterminalType!($(grammar.nonterminalIDCode(nonterminalOutForProduction(production)))) pt; 397 $$if (nrNotDropped != size_t.max) { 398 $$if (production.symbols[nrNotDropped].isToken) { 399 pt = stackEdge$(production.symbols.length - nrNotDropped).token; 400 $$} else if (graph.globalOptions.directUnwrap) { 401 pt = stackEdge$(production.symbols.length - nrNotDropped).nonterminal.get!( _ 402 $$foreach (n; grammar.directUnwrapClosure(production.symbols[nrNotDropped])) { 403 $(grammar.nonterminalIDCode(n.nonterminalID)), _ 404 $$} 405 ); 406 $$} else { 407 pt = stackEdge$(production.symbols.length - nrNotDropped).nonterminal.get!$(grammar.nonterminalIDCode(production.symbols[nrNotDropped].toNonterminalID)); 408 $$} 409 Location newTreeStart = stackEdge$(production.symbols.length - nrNotDropped).start; 410 $$} else { 411 pt = typeof(pt).init; 412 Location newTreeStart = Location.invalid; 413 $$} 414 415 r.nonterminal = Creator.NonterminalUnionAny.create($(grammar.nonterminalIDCode(nonterminalOutForProduction(production))), pt); 416 r.start = newTreeStart; 417 $$} else { 418 $$if (production.symbols.length > 0) { 419 Location newTreeStart = stackEdge$(production.symbols.length).start; 420 $$for (size_t i=production.symbols.length - 1; i > 0; i--) { 421 if (!newTreeStart.isValid) 422 newTreeStart = stackEdge$(i).start; 423 $$} 424 $$} else { 425 Location newTreeStart = Location.invalid; 426 $$} 427 $$string stackStartExpr = "newTreeStart"; 428 Location reduceEnd = lastTokenEnd; 429 if (reduceEnd < newTreeStart) 430 reduceEnd = newTreeStart; 431 $$foreach (k; 0 .. production.symbols.length) { 432 $$if (production.symbols[k].isToken) { 433 ParseStackElem!(Location, Token) stack$(k) 434 = ParseStackElem!(Location, Token) _ 435 (stackEdge$(production.symbols.length - k).start, stackEdge$(production.symbols.length - k).token); 436 $$} else if (graph.globalOptions.directUnwrap) { 437 ParseStackElem!(Location, NonterminalType!$(grammar.nonterminalIDCode(production.symbols[k].toNonterminalID))) stack$(k) 438 = ParseStackElem!(Location, NonterminalType!$(grammar.nonterminalIDCode(production.symbols[k].toNonterminalID))) _ 439 (stackEdge$(production.symbols.length - k).start, stackEdge$(production.symbols.length - k).nonterminal.get!( _ 440 $$foreach (n; grammar.directUnwrapClosure(production.symbols[k])) { 441 $(grammar.nonterminalIDCode(n.nonterminalID)), _ 442 $$} 443 )); 444 $$} else { 445 ParseStackElem!(Location, NonterminalType!$(grammar.nonterminalIDCode(production.symbols[k].toNonterminalID))) stack$(k) 446 = ParseStackElem!(Location, NonterminalType!$(grammar.nonterminalIDCode(production.symbols[k].toNonterminalID))) _ 447 (stackEdge$(production.symbols.length - k).start, stackEdge$(production.symbols.length - k).nonterminal.get!$(grammar.nonterminalIDCode(production.symbols[k].toNonterminalID))); 448 $$} 449 $$} 450 451 NonterminalType!($(grammar.nonterminalIDCode(nonterminalOutForProduction(rewriteRules[$ - 1].applyProduction)))) pt _ 452 = this_.creator.createParseTree!($(rewriteRules[$ - 1].applyProduction.productionID + grammar.startProductionID)) _ 453 (newTreeStart, reduceEnd _ 454 $$size_t iParam; 455 $$foreach (k; 0 .. rewriteRules[$ - 1].applyProduction.symbols.length) { 456 $$if (!rewriteRules[$ - 1].applyProduction.symbols[k].dropNode) { 457 $$if (rewriteRules[$ - 1].parameters[iParam] == size_t.max) { 458 , ParseStackElem!(Location, NonterminalType!$(rewriteRules[$ - 1].applyProduction.symbols[k].id + grammar.startNonterminalID))(Location.invalid, NonterminalType!$(rewriteRules[$ - 1].applyProduction.symbols[k].id + grammar.startNonterminalID).init/*null*/) _ 459 $$iParam++; 460 $$} else { 461 , $(rewriteParameters[rewriteRules[$ - 1].parameters[iParam]]) _ 462 $$stackStartExpr = text("Location.init/*stackEdge", production.symbols.length - k, ".end*/"); 463 $$iParam++; 464 $$} 465 $$} else { 466 $$} 467 $$} 468 ); 469 470 r.nonterminal = Creator.NonterminalUnionAny.create($(grammar.nonterminalIDCode(nonterminalOutForProduction(rewriteRules[$ - 1].applyProduction))), pt); 471 r.start = newTreeStart; 472 $$} 473 return r; 474 } 475 $$if (tuple!(size_t, size_t)(production.productionID, production.symbols.length) in graph.statesWithProduction) { 476 private _ 477 void _ 478 $(reduceFunctionName(graph, production))( _ 479 StackNode *stackNode, Location tokenStart, Location tokenEnd, Location lastTokenEnd, bool alreadyShifted _ 480 ) 481 { 482 ParseException[] exceptions; 483 bool anyGood; 484 // $(grammar.productionString(production)) $(production.tags) 485 $$string currentStackNode = "stackNode"; 486 $$const(size_t)[] possibleStates; 487 $$possibleStates = graph.statesWithProduction[tuple!(size_t, size_t)(production.productionID, production.symbols.length)]; 488 $$foreach (k; 1 .. production.symbols.length + 1) { 489 assert($(currentStackNode).previous.length > 0); 490 assert($(currentStackNode).state.among( _ 491 $$foreach (prevState; possibleStates) { 492 $(prevState), _ 493 $$} 494 ), text("state = ", $(currentStackNode).state)); 495 foreach (StackEdge* stackEdge$(k); $(currentStackNode).previous) // $(grammar.symbolInstanceToString(production.symbols[$ - k])) 496 { 497 $$code.incIndent; 498 $$foreach (t; production.symbols[$ - k].tags) { 499 $$} 500 $$currentStackNode = text("stackEdge", k, ".node"); 501 $$possibleStates = prevStateSet(possibleStates); 502 $$} 503 $$if (grammar.tags.vals.length) { 504 Tag tags = $(reduceTagsCode!((k) => text("stackEdge", k, ".tags"))(grammar, production)); 505 $$if (checkTagsCode!((k) => text("stackEdge", k, ".tags"))(code, grammar, production)) { 506 continue; 507 $$} 508 $$} 509 try 510 { 511 StackEdge*[] parameterEdges = tmpData.stackEdgePointerAllocator.allocate(null, $(production.symbols.length)); 512 $$foreach (k; iota(production.symbols.length, 0, -1)) { 513 parameterEdges[$(production.symbols.length - k)] = stackEdge$(k); 514 $$} 515 pushToken( _ 516 $$if (production.symbols.length == 0) { 517 stackNode, _ 518 $$} else { 519 stackEdge$(production.symbols.length).node, _ 520 $$} 521 PendingReduce($(grammar.nonterminalIDCode(nonterminalOutForProduction(rewriteRules[$ - 1].applyProduction))), _ 522 parameterEdges, $(production.productionID), &$(reduceFunctionName(graph, production, "reduceImpl"))), tokenStart, tokenEnd, alreadyShifted$(grammar.tags.vals.length ? ", tags" : "")); 523 anyGood = true; 524 } 525 catch(ParseException e) 526 { 527 exceptions ~= e; 528 } 529 $$foreach (k; 1 .. production.symbols.length + 1) { 530 $$code.decIndent; 531 } 532 $$} 533 if (!anyGood) 534 { 535 if (exceptions.length == 1) 536 throw exceptions[0]; 537 else if (exceptions.length == 0) 538 throw new SingleParseException!Location("no reduce", tokenStart, tokenEnd); 539 else 540 throw new MultiParseException("", exceptions); 541 } 542 } 543 $$} 544 })); 545 } 546 547 void createStartParseFunction(ref CodeWriter code, LRGraph graph, size_t stateNr, 548 const LRGraphNode node) 549 { 550 auto grammar = graph.grammar; 551 immutable endTok = grammar.tokens.getID("$end"); 552 553 assert(node.stackSymbols.length == 0); 554 555 mixin(genCode("code", q{ 556 /*private*/ _ 557 void _ 558 $(parseFunctionName(graph, stateNr, "startParse"))( _ 559 ) 560 { 561 lastTokenEnd = Location.init; 562 stackTops = [new StackNode($(stateNr))]; 563 acceptedStackTops = []; 564 clearTmpData(); 565 pushToken(getTokenID!"$flushreduces", Token.init, lastTokenEnd, lastTokenEnd); 566 } 567 })); 568 } 569 570 void createWrapperParseFunction(ref CodeWriter code, LRGraph graph, size_t stateNr, 571 const LRGraphNode node) 572 { 573 auto grammar = graph.grammar; 574 575 assert(node.stackSymbols.length == 0); 576 577 mixin(genCode("code", q{ 578 /*private*/ _ 579 ParseStackElem!(Location, NonterminalType!($(node.elements[0].production.symbols[0].id + grammar.startNonterminalID)))[] _ 580 $(parseFunctionName(graph, stateNr, "multiParse"))( _ 581 ) 582 { 583 scope(failure) 584 { 585 stackTops = []; 586 acceptedStackTops = []; 587 } 588 589 alias ThisParseResult = typeof(return); 590 pushParser.$(parseFunctionName(graph, stateNr, "startParse"))(); 591 while (!lexer.empty) 592 { 593 pushParser.pushToken(translateTokenIdFromLexer!Lexer(lexer.front.symbol), lexer.front.content, lexer.front.currentLocation, lexer.front.currentTokenEnd); 594 if (acceptedStackTops.length) 595 { 596 if (stackTops.length == 0) 597 break; 598 else 599 acceptedStackTops = []; 600 } 601 lexer.popFront(); 602 } 603 if (acceptedStackTops.length == 0) 604 { 605 pushParser.pushEnd(); 606 } 607 assert(stackTops.length == 0); 608 ParseStackElem!(Location, NonterminalType!($(node.elements[0].production.symbols[0].id + grammar.startNonterminalID)))[] r; 609 assert(acceptedStackTops.length >= 1); 610 foreach (t; acceptedStackTops) 611 { 612 foreach (prev; t.previous) 613 { 614 assert(prev.node.previous.length == 0); 615 NonterminalType!($(node.elements[0].production.symbols[0].id + grammar.startNonterminalID)) pt; 616 switch (prev.data.nonterminal.nonterminalID) 617 { 618 $$foreach (n; grammar.directUnwrapClosure(node.elements[0].production.symbols[0].toNonterminalID, [], [])) { 619 case $(grammar.nonterminalIDCode(n.nonterminalID)): 620 pt = prev.data.nonterminal.get!($(grammar.nonterminalIDCode(n.nonterminalID))); 621 break; 622 $$} 623 default: 624 assert(false); 625 } 626 r ~= ParseStackElem!(Location, NonterminalType!($(grammar.nonterminalIDCode(node.elements[0].production.symbols[0].toNonterminalID))))(prev.data.start, pt); 627 } 628 } 629 stackTops = []; 630 acceptedStackTops = []; 631 return r; 632 } 633 634 /*private*/ _ 635 ParseStackElem!(Location, NonterminalType!($(node.elements[0].production.symbols[0].id + grammar.startNonterminalID))) _ 636 $(parseFunctionName(graph, stateNr))( _ 637 ) 638 do 639 { 640 auto r = $(parseFunctionName(graph, stateNr, "multiParse"))(); 641 642 if (r.length == 0) 643 { 644 return ParseStackElem!(Location, NonterminalType!($(grammar.nonterminalIDCode(node.elements[0].production.symbols[0].toNonterminalID)))).init; 645 } 646 647 static if (pushParser.canMerge!($(grammar.nonterminalIDCode(node.elements[0].production.symbols[0].toNonterminalID)))) 648 if (r.length != 1) 649 { 650 NonterminalType!($(grammar.nonterminalIDCode(node.elements[0].production.symbols[0].toNonterminalID))) pt = creator.mergeParseTrees!($(grammar.nonterminalIDCode(node.elements[0].production.symbols[0].toNonterminalID)))(Location.init, lastTokenEnd, r); 651 return ParseStackElem!(Location, NonterminalType!($(grammar.nonterminalIDCode(node.elements[0].production.symbols[0].toNonterminalID))))(Location.init, pt); 652 } 653 654 assert(r.length == 1, "Root node $(grammar.getSymbolName(node.elements[0].production.symbols[0])) is not mergeable."); 655 return r[0]; 656 } 657 })); 658 } 659 660 void createShiftFunctions(ref CodeWriter code, LRGraph graph) 661 { 662 EBNFGrammar grammar = graph.grammar; 663 664 mixin(genCode("code", q{ 665 StackNode *shift(bool onStack)(StackNode *stackNode, Location start, size_t state, Token token) 666 { 667 StackNode* n = null; 668 n = tmpData.stackNodeByState[state]; 669 if (n is null) 670 { 671 n = new StackNode(state); 672 tmpData.stackNodeByState[state] = n; 673 static if (onStack) 674 stackTops ~= n; 675 } 676 foreach (ref edge; n.previous) 677 { 678 if (edge.node is stackNode) 679 { 680 assert(edge.data.isToken); 681 assert(edge.data.token == token); 682 return n; 683 } 684 } 685 StackEdgeData* data = new StackEdgeData(true, start); 686 data.token = token; 687 n.previous ~= new StackEdge(stackNode, data); 688 return n; 689 } 690 691 StackNode* shift(StackNode *stackNode, size_t state, PendingReduce pendingReduce, bool alreadyShifted, bool setStackTops$(grammar.tags.vals.length?", Tag tags":"")) 692 { 693 assert(pendingReduce.next is null); 694 695 bool allowEdgeReduce = true; 696 697 switch (pendingReduce.nonterminalID) 698 { 699 static foreach (nonterminalID; startNonterminalID .. endNonterminalID) 700 static if (!canMerge!nonterminalID) 701 { 702 case nonterminalID: 703 } 704 allowEdgeReduce = false; 705 break; 706 default: 707 } 708 709 size_t stateHash = (state + stackNode.state * 31) % tmpData.pendingReduceIds.length; 710 711 size_t count; 712 for (size_t k = tmpData.pendingReduceIds[stateHash]; k != size_t.max; k = tmpData.pendingReduces.data[k].nextPendingReduce2) 713 { 714 auto pendingReduce2 = &tmpData.pendingReduces.data[k]; 715 count++; 716 if (pendingReduce2.stackNode.state == state && pendingReduce2.stackEdge.node is stackNode && pendingReduce2.alreadyShifted == alreadyShifted) 717 { 718 for (PendingReduce *pendingReduce2x = pendingReduce2.pendingReduces; pendingReduce2x !is null; pendingReduce2x = pendingReduce2x.next) 719 { 720 if (pendingReduce2x.nonterminalID == pendingReduce.nonterminalID 721 && pendingReduce2x.productionID is pendingReduce.productionID 722 && pendingReduce2x.parameterEdges.length == pendingReduce.parameterEdges.length) 723 { 724 bool eq = true; 725 foreach (i, p; pendingReduce2x.parameterEdges) 726 if (p !is pendingReduce.parameterEdges[i]) 727 eq = false; 728 if (eq) 729 { 730 return pendingReduce2.stackNode; 731 } 732 } 733 } 734 } 735 } 736 737 PendingReduce *pendingReducePtr = tmpData.pendingReduceAllocator.allocate(pendingReduce, 1).ptr; 738 739 if (allowEdgeReduce) 740 { 741 for (size_t k = tmpData.pendingReduceIds[stateHash]; k != size_t.max; k = tmpData.pendingReduces.data[k].nextPendingReduce2) 742 { 743 auto pendingReduce2 = &tmpData.pendingReduces.data[k]; 744 if (pendingReduce2.stackNode.state == state 745 $$if (grammar.tags.vals.length) { 746 && pendingReduce2.stackEdge.tags == tags 747 $$} 748 && pendingReduce2.stackEdge.node is stackNode 749 && pendingReduce2.alreadyShifted == alreadyShifted) 750 { 751 pendingReducePtr.next = pendingReduce2.pendingReduces; 752 pendingReduce2.pendingReduces = pendingReducePtr; 753 hasAddedPendingReduce = true; 754 return pendingReduce2.stackNode; 755 } 756 } 757 } 758 759 if (tmpData.stackNodeByState[state + (!alreadyShifted) * $(graph.states.length)] !is null) 760 { 761 StackNode* n = tmpData.stackNodeByState[state + (!alreadyShifted) * $(graph.states.length)]; 762 n.previous ~= new StackEdge(stackNode); 763 $$if (grammar.tags.vals.length) { 764 n.previous[$ - 1].tags = tags; 765 $$} 766 tmpData.pendingReduces.put(PendingReduce2(n, n.previous[$ - 1], alreadyShifted, pendingReducePtr, tmpData.pendingReduceIds[stateHash])); 767 tmpData.pendingReduceIds[stateHash] = tmpData.pendingReduces.data.length - 1; 768 hasAddedPendingReduce = true; 769 return n; 770 } 771 772 StackNode* n = new StackNode(state); 773 n.previous ~= new StackEdge(stackNode, null); 774 $$if (grammar.tags.vals.length) { 775 n.previous[$ - 1].tags = tags; 776 $$} 777 tmpData.stackNodeByState[state + (!alreadyShifted) * $(graph.states.length)] = n; 778 779 hasAddedPendingReduce = true; 780 tmpData.pendingReduces.put(PendingReduce2(n, n.previous[$ - 1], alreadyShifted, pendingReducePtr, tmpData.pendingReduceIds[stateHash])); 781 tmpData.pendingReduceIds[stateHash] = tmpData.pendingReduces.data.length - 1; 782 783 if (alreadyShifted && setStackTops) 784 stackTops ~= n; 785 786 return n; 787 } 788 })); 789 } 790 791 void createParseFunctions(ref CodeWriter code, LRGraph graph) 792 { 793 EBNFGrammar grammar = graph.grammar; 794 795 mixin(genCode("code", q{ 796 private void pushToken(StackNode *stackNode, size_t tokenId, Token tokenContent, Location start, Location end) 797 { 798 switch (stackNode.state) 799 { 800 $$foreach (i, n; graph.states) { 801 $$if (!n.hasSetStackSymbols || graph.globalOptions.directUnwrap) { 802 case $(i): $(parseFunctionName(graph, i, "pushTokenState")) _ 803 (stackNode, tokenId, tokenContent, start, end); break; 804 $$} 805 $$} 806 default: assert(false, text("state=", stackNode.state)); 807 } 808 } 809 void pushToken(size_t tokenId, Token tokenContent, Location start, Location end) 810 in 811 { 812 assert(start.isValid); 813 assert(end.isValid); 814 assert(lastTokenEnd.isValid); 815 } 816 do 817 { 818 if (stackTops.length == 0) 819 { 820 if (acceptedStackTops.length == 0) 821 throw new SingleParseException!Location("no more stackTops", 822 start, end); 823 else 824 throw new SingleParseException!Location("no more stackTops (but acceptedStackTops)", 825 start, end); 826 } 827 StackNode*[] savedStackTops = stackTops; 828 assert(savedStackTops); 829 stackTops = []; 830 acceptedStackTops = []; 831 ParseException[] exceptions; 832 assert(tmpData.pendingReduces.data.length == 0); 833 834 assert(!tmpData.inUse); 835 tmpData.inUse = true; 836 837 scope(exit) 838 { 839 clearTmpData(); 840 tmpData.inUse = false; 841 } 842 843 if (tokenId == getTokenID!"$flushreduces") 844 { 845 foreach (stackNode; savedStackTops) 846 { 847 switch (stackNode.state) 848 { 849 $$foreach (i, n; graph.states) { 850 $$if (!isSingleReduceState(graph, i)) { 851 case $(i): stackTops ~= stackNode; break; 852 $$} 853 $$} 854 default: break; 855 } 856 } 857 foreach (stackNode; savedStackTops) 858 { 859 switch (stackNode.state) 860 { 861 $$foreach (i, n; graph.states) { 862 $$if (isSingleReduceState(graph, i)) { 863 case $(i): 864 $$foreach (e; n.elements) { 865 $$if (e.isFinished(grammar) && !e.isStartElement) { 866 $$if (e.production.symbols.length && e.production.symbols[$ - 1].isToken) { 867 // Nothing to do for token 868 $$} else { 869 $(reduceFunctionName(graph, e.production))(stackNode, start, end, end, true); 870 $$} 871 $$} 872 $$} 873 break; 874 $$} 875 $$} 876 default: break; 877 } 878 } 879 } 880 else 881 { 882 foreach (stackNode; savedStackTops) 883 { 884 try 885 { 886 pushToken(stackNode, tokenId, tokenContent, start, end); 887 } 888 catch(ParseException e) 889 { 890 exceptions ~= e; 891 } 892 } 893 } 894 895 do 896 { 897 hasAddedPendingReduce = false; 898 for (size_t i=0; i < tmpData.pendingReduces.data.length; i++) 899 { 900 if (tmpData.pendingReduces.data[i].alreadyShifted) 901 { 902 try 903 { 904 switch (tmpData.pendingReduces.data[i].stackNode.state) 905 { 906 $$foreach (i, n; graph.states) { 907 $$if (isSingleReduceState(graph, i)) { 908 case $(i): 909 $$foreach (e; n.elements) { 910 $$if (e.isFinished(grammar) && !e.isStartElement) { 911 $$if (e.production.symbols.length && e.production.symbols[$ - 1].isToken) { 912 // Nothing to do for token 913 $$} else { 914 $(reduceFunctionName(graph, e.production))(tmpData.pendingReduces.data[i].stackNode, start, end, end, true); 915 $$} 916 $$} 917 $$} 918 break; 919 $$} 920 $$} 921 default: break; 922 } 923 } 924 catch(ParseException e) 925 { 926 exceptions ~= e; 927 } 928 929 continue; 930 } 931 932 try 933 { 934 pushToken(tmpData.pendingReduces.data[i].stackNode, tokenId, tokenContent, start, end); 935 } 936 catch(ParseException e) 937 { 938 exceptions ~= e; 939 } 940 } 941 } while (hasAddedPendingReduce); 942 943 foreach (ref pendingReduce; tmpData.pendingReduces.data) 944 { 945 onPendingReduce(pendingReduce, end); 946 } 947 948 lastTokenEnd = end; 949 if (stackTops.length == 0 && acceptedStackTops.length == 0) 950 { 951 if (exceptions.length > 0) 952 if (exceptions.length == 1) 953 throw exceptions[0]; 954 else 955 throw new MultiParseException("", exceptions); 956 else 957 throw new SingleParseException!Location(text("no stackTops ", tmpData.pendingReduces.data.length), 958 start, end); 959 } 960 } 961 })); 962 963 mixin(genCode("code", q{ 964 void onPendingReduce(ref PendingReduce2 pendingReduce, Location end) 965 { 966 assert(pendingReduce.stackEdge.data is null || !pendingReduce.stackEdge.data.isToken); 967 if (pendingReduce.stackEdge.reduceDone) 968 return; 969 pendingReduce.stackEdge.reduceDone = true; 970 for (PendingReduce *pendingReducex = pendingReduce.pendingReduces; pendingReducex !is null; pendingReducex = pendingReducex.next) 971 foreach (edge; pendingReducex.parameterEdges) 972 foreach (ref pendingReduce2x; tmpData.pendingReduces.data) 973 if (pendingReduce2x.stackEdge is edge) 974 onPendingReduce(pendingReduce2x, end); 975 Location reduceEnd = pendingReduce.alreadyShifted ? end : lastTokenEnd; 976 size_t num; 977 for (PendingReduce *pendingReducePtr = pendingReduce.pendingReduces; pendingReducePtr !is null; pendingReducePtr = pendingReducePtr.next) 978 num++; 979 if (num == 1) 980 { 981 pendingReduce.stackEdge.data = pendingReduce.pendingReduces[0].dg(this, pendingReduce.pendingReduces.parameterEdges, reduceEnd); 982 assert(pendingReduce.stackEdge.data.nonterminal.nonterminalID == pendingReduce.pendingReduces.nonterminalID); 983 } 984 else 985 { 986 static Appender!(PendingReduce*[]) appPendingReduce; 987 scope(exit) 988 appPendingReduce.clear; 989 for (PendingReduce *pendingReducePtr = pendingReduce.pendingReduces; pendingReducePtr !is null; pendingReducePtr = pendingReducePtr.next) 990 appPendingReduce.put(pendingReducePtr); 991 PendingReduce*[] pendingReducesHere = appPendingReduce.data; 992 foreach (i; 0 .. pendingReducesHere.length/2) 993 { 994 auto tmp = pendingReducesHere[i]; 995 pendingReducesHere[i] = pendingReducesHere[$ - 1-i]; 996 pendingReducesHere[$ - 1-i] = tmp; 997 } 998 pendingReducesHere.sort!((a, b) => a.productionID < b.productionID); 999 1000 Creator.NonterminalUnionAny[] nonterminals; 1001 Location[] nonterminalStarts; 1002 nonterminals.length = pendingReducesHere.length; 1003 nonterminalStarts.length = pendingReducesHere.length; 1004 foreach (i; 0 .. pendingReducesHere.length) 1005 { 1006 StackEdgeData* data = pendingReducesHere[i].dg(this, pendingReducesHere[i].parameterEdges, reduceEnd); 1007 nonterminals[i] = data.nonterminal; 1008 nonterminalStarts[i] = data.start; 1009 assert(nonterminals[i].nonterminalID == pendingReducesHere[i].nonterminalID); 1010 } 1011 Location nonterminalStart = nonterminalStarts[0]; 1012 foreach (i; 1 .. pendingReducesHere.length) 1013 if (nonterminalStarts[i] < nonterminalStart) 1014 nonterminalStart = nonterminalStarts[i]; 1015 1016 $$if (graph.globalOptions.directUnwrap) { 1017 switch (pendingReduce.stackNode.state) 1018 { 1019 $$foreach (i, n; graph.states) { 1020 $$if (n.stackSize() >= 0) { 1021 $$NonterminalID[] possible = n.directUnwrapNonterminalsOnStack(graph.grammar, 1); 1022 $$if (possible.length) { 1023 static if (canMerge!($(grammar.nonterminalIDCode(possible[0])))) 1024 { 1025 case $(i): 1026 { 1027 onPendingReduce$(i)(pendingReduce, end, nonterminals, nonterminalStarts, nonterminalStart); 1028 } 1029 break; 1030 } 1031 $$} 1032 $$} 1033 $$} 1034 default: assert(false); 1035 } 1036 1037 $$} else { 1038 switchlabel: switch (pendingReducesHere[0].nonterminalID) 1039 { 1040 static foreach (nonterminalID; startNonterminalID .. endNonterminalID) 1041 { 1042 static if (canMerge!nonterminalID) 1043 { 1044 case nonterminalID: 1045 { 1046 ParseStackElem!(Location, NonterminalType!nonterminalID)[] pts; 1047 pts.length = nonterminals.length; 1048 foreach (i; 0 .. nonterminals.length) 1049 $$if (graph.globalOptions.directUnwrap) { 1050 pts[i] = ParseStackElem!(Location, NonterminalType!nonterminalID)(nonterminalStarts[i], nonterminals[i].get!(arrayToAliasSeq!(NonterminalClosures[nonterminalID]))); 1051 $$} else { 1052 pts[i] = ParseStackElem!(Location, NonterminalType!nonterminalID)(nonterminalStarts[i], nonterminals[i].get!nonterminalID); 1053 $$} 1054 NonterminalType!nonterminalID pt = creator.mergeParseTrees!nonterminalID(nonterminalStart, pendingReduce.alreadyShifted?end:lastTokenEnd, pts); 1055 pendingReduce.stackEdge.data = new StackEdgeData(false); 1056 pendingReduce.stackEdge.data.start = nonterminalStart; 1057 pendingReduce.stackEdge.data.nonterminal = 1058 Creator.NonterminalUnionAny.create(nonterminalID, pt); 1059 } 1060 break switchlabel; 1061 } 1062 } 1063 default: 1064 assert(0); 1065 } 1066 $$} 1067 } 1068 } 1069 1070 $$if (graph.globalOptions.directUnwrap) { 1071 $$foreach (i, n; graph.states) { 1072 $$if (n.stackSize() >= 0) { 1073 $$NonterminalID[] possible = n.directUnwrapNonterminalsOnStack(graph.grammar, 1); 1074 $$if (possible.length) { 1075 void onPendingReduce$(i)(ref PendingReduce2 pendingReduce, Location end, Creator.NonterminalUnionAny[] nonterminals, Location[] nonterminalStarts, Location nonterminalStart) 1076 { 1077 static if (canMerge!($(grammar.nonterminalIDCode(possible[0])))) 1078 { 1079 ParseStackElem!(Location, NonterminalType!$(grammar.nonterminalIDCode(possible[0])))[] pts; 1080 pts.length = nonterminals.length; 1081 foreach (i; 0 .. nonterminals.length) 1082 { 1083 bool found; 1084 static foreach (nonterminalID; [ _ 1085 $$foreach (nonterminal; possible) { 1086 $(grammar.nonterminalIDCode(nonterminal)), _ 1087 $$} 1088 ]) 1089 { 1090 if (nonterminals[i].nonterminalID == nonterminalID) 1091 { 1092 pts[i] = ParseStackElem!(Location, NonterminalType!nonterminalID)(nonterminalStarts[i], nonterminals[i].get!(nonterminalID)); 1093 found = true; 1094 } 1095 } 1096 assert(found); 1097 } 1098 NonterminalType!$(grammar.nonterminalIDCode(possible[0])) pt = creator.mergeParseTrees!$(grammar.nonterminalIDCode(possible[0]))(nonterminalStart, pendingReduce.alreadyShifted?end:lastTokenEnd, pts); 1099 pendingReduce.stackEdge.data = new StackEdgeData(false); 1100 pendingReduce.stackEdge.data.start = nonterminalStart; 1101 pendingReduce.stackEdge.data.nonterminal = 1102 Creator.NonterminalUnionAny.create($(grammar.nonterminalIDCode(possible[0])), pt); 1103 } 1104 } 1105 $$} 1106 $$} 1107 $$} 1108 $$} 1109 1110 void pushToken(StackNode *stackNode, PendingReduce pendingReduce, Location tokenStart, Location tokenEnd, bool alreadyShifted$(grammar.tags.vals.length?", Tag tags":"")) 1111 in 1112 { 1113 assert(lastTokenEnd.isValid); 1114 } 1115 do 1116 { 1117 switch (stackNode.state) 1118 { 1119 $$foreach (i, n; graph.states) { 1120 $$if (!trivialState(n) && needsJumps(graph, i, n, genActionTable(graph, n))) { 1121 $$if (!n.hasSetStackSymbols || graph.globalOptions.directUnwrap) { 1122 case $(i): $(parseFunctionName(graph, i, "pushTokenState")) _ 1123 (stackNode, pendingReduce, tokenStart, tokenEnd, alreadyShifted$(grammar.tags.vals.length?", tags":"")); break; 1124 $$} 1125 $$} 1126 $$} 1127 default: assert(false); 1128 } 1129 } 1130 1131 void pushEnd() 1132 in 1133 { 1134 assert(lastTokenEnd.bytePos != size_t.max); 1135 } 1136 do 1137 { 1138 pushToken(getTokenID!"$end", Token.init, lastTokenEnd, lastTokenEnd); 1139 } 1140 })); 1141 } 1142 1143 const(char)[] createParserModule(LRGraph graph, string modulename) 1144 { 1145 EBNFGrammar grammar = graph.grammar; 1146 1147 if (graph.states.length == 0) 1148 return "// no states\n"; 1149 1150 CodeWriter code; 1151 code.indentStr = " "; 1152 1153 string nonterminalNameCode(const Nonterminal n) 1154 { 1155 return n.name.escapeD; 1156 } 1157 1158 string allTokensCode = createAllTokensCode(grammar); 1159 string allNonterminalsCode = createAllNonterminalsCode(grammar); 1160 1161 bool needsDelayedParseTreeConstructor; 1162 foreach (node; graph.states) 1163 { 1164 if (node.hasSetStackSymbols && graph.globalOptions.delayedReduce) 1165 needsDelayedParseTreeConstructor = true; 1166 } 1167 1168 mixin(genCode("code", q{ 1169 // Generated with DParserGen. 1170 module $(modulename); 1171 import dparsergen.core.grammarinfo; 1172 import dparsergen.core.parseexception; 1173 import dparsergen.core.parsestackelem; 1174 import dparsergen.core.utils; 1175 import std.algorithm; 1176 import std.array; 1177 import std.conv; 1178 import std.meta; 1179 import std.stdio; 1180 1181 enum SymbolID startTokenID = $(grammar.startTokenID); 1182 static assert(allTokens.length < SymbolID.max - startTokenID); 1183 enum SymbolID endTokenID = startTokenID + allTokens.length; 1184 1185 enum SymbolID startNonterminalID = $(grammar.startNonterminalID); 1186 static assert(allNonterminals.length < SymbolID.max - startNonterminalID); 1187 enum SymbolID endNonterminalID = startNonterminalID + allNonterminals.length; 1188 1189 enum ProductionID startProductionID = $(grammar.startProductionID); 1190 static assert(allProductions.length < ProductionID.max - startProductionID); 1191 enum ProductionID endProductionID = startProductionID + allProductions.length; 1192 1193 enum nonterminalIDFor(string name) = startNonterminalID + staticIndexOf!(name, 1194 $$foreach (i, n; grammar.nonterminals.vals) { 1195 "$(nonterminalNameCode(n))", 1196 $$} 1197 ); 1198 1199 size_t getTokenIDImpl(string tok) 1200 { 1201 auto r = allTokens.countUntil!(t=>t.name == tok); 1202 assert(r >= 0 && r < allTokens.length, "token not found " ~ tok); 1203 return r + startTokenID; 1204 } 1205 enum size_t getTokenID(string tok) = getTokenIDImpl(tok); 1206 string getTokenName(size_t id) 1207 { 1208 if (id == size_t.max) 1209 return text("???"); 1210 if (id < startTokenID) 1211 return text("???:", id); 1212 if (id >= endTokenID) 1213 return text("???:", id); 1214 return allTokens[id - startTokenID].name; 1215 } 1216 string getNonterminalName(size_t id) 1217 { 1218 if (id == size_t.max) 1219 return text("???"); 1220 if (id < startNonterminalID || id > endNonterminalID) 1221 return text("???:", id); 1222 return allNonterminals[id - startNonterminalID].name; 1223 } 1224 $$if (needsDelayedParseTreeConstructor) { 1225 class DelayedParseTreeConstructor(alias Creator) 1226 { 1227 alias Location = Creator.Location; 1228 alias LocationDiff = typeof(Location.init - Location.init); 1229 1230 immutable size_t[] nonterminalIDs; 1231 LocationDiff inputLength; 1232 this(immutable size_t[] nonterminalIDs, LocationDiff inputLength) 1233 { 1234 this.nonterminalIDs = nonterminalIDs; 1235 this.inputLength = inputLength; 1236 } 1237 } 1238 $$} 1239 $$if (grammar.tags.vals.length) { 1240 enum Tag 1241 { 1242 none = 0, 1243 $$foreach (i, t; grammar.tags.vals) { 1244 $(t.name) = 1 << $(i), 1245 $$} 1246 } 1247 $$} 1248 1249 struct PushParser(alias Creator, Token) 1250 { 1251 alias Location = Creator.Location; 1252 Creator creator; 1253 1254 alias LocationDiff = typeof(Location.init - Location.init); 1255 1256 $$foreach (i, n; graph.states) { 1257 $$if (needsExtraStateData(graph, i, n)) { 1258 $$createExtraStateData(code, graph, i, n); 1259 1260 $$} 1261 $$} 1262 static struct StackNode 1263 { 1264 size_t state; 1265 StackEdge*[] previous; 1266 union 1267 { 1268 $$foreach (i, n; graph.states) { 1269 $$if (needsExtraStateData(graph, i, n)) { 1270 $(parseFunctionName(graph, i, "StateData")) $(parseFunctionName(graph, i, "stateData")); 1271 $$} 1272 $$} 1273 } 1274 } 1275 static struct StackEdgeData 1276 { 1277 bool isToken; 1278 Location start; 1279 union 1280 { 1281 Creator.NonterminalUnionAny nonterminal; 1282 Token token; 1283 } 1284 } 1285 static struct StackEdge 1286 { 1287 StackNode *node; 1288 StackEdgeData *data; 1289 bool reduceDone; 1290 $$if (grammar.tags.vals.length) { 1291 Tag tags; 1292 $$} 1293 } 1294 1295 static struct PendingReduce 1296 { 1297 SymbolID nonterminalID; 1298 StackEdge*[] parameterEdges; 1299 ProductionID productionID; 1300 StackEdgeData* function(ref PushParser this_, StackEdge*[] parameterEdges, Location lastTokenEnd) dg; 1301 PendingReduce* next; 1302 } 1303 static struct PendingReduce2 1304 { 1305 StackNode* stackNode; 1306 StackEdge* stackEdge; 1307 bool alreadyShifted; 1308 PendingReduce* pendingReduces; 1309 size_t nextPendingReduce2; 1310 } 1311 1312 static struct TmpData 1313 { 1314 bool inUse; 1315 StackNode*[$(graph.states.length * 2)] stackNodeByState; 1316 size_t[$(graph.states.length * 2)] pendingReduceIds; 1317 SimpleArrayAllocator!(PendingReduce, 4 * 1024 - 32) pendingReduceAllocator; 1318 SimpleArrayAllocator!(StackEdge*, 4 * 1024 - 32) stackEdgePointerAllocator; 1319 Appender!(PendingReduce2[]) pendingReduces; 1320 } 1321 TmpData tmpData; 1322 1323 void clearTmpData() 1324 { 1325 tmpData.stackNodeByState[] = null; 1326 tmpData.pendingReduceIds[] = size_t.max; 1327 tmpData.pendingReduceAllocator.clearAll(); 1328 tmpData.stackEdgePointerAllocator.clearAll(); 1329 tmpData.pendingReduces.clear(); 1330 } 1331 1332 StackNode*[] stackTops; 1333 StackNode*[] acceptedStackTops; 1334 1335 Location lastTokenEnd; 1336 bool hasAddedPendingReduce; 1337 1338 $$createShiftFunctions(code, graph); 1339 1340 void dumpStates(string indent="") 1341 { 1342 write(indent, "stackTops:"); 1343 foreach (stackNode; stackTops) 1344 { 1345 write(" ", stackNode.state); 1346 } 1347 writeln(); 1348 write(indent, "acceptedStackTops:"); 1349 foreach (stackNode; acceptedStackTops) 1350 { 1351 write(" ", stackNode.state); 1352 } 1353 writeln(); 1354 } 1355 1356 alias NonterminalType(size_t nonterminalID) = Creator.NonterminalType!nonterminalID; 1357 enum canMerge(size_t nonterminalID) = Creator.canMerge!nonterminalID; 1358 1359 $$foreach (production; graph.grammar.productions) { 1360 $$createReduceFunction(code, graph, production); 1361 1362 $$} 1363 1364 $$foreach (i, n; graph.states) { 1365 $$if (true || !trivialState(n)) { 1366 $$createParseFunction(code, graph, i, n); 1367 1368 $$} 1369 $$} 1370 1371 $$foreach (i, n; graph.states) { 1372 $$if (n.isStartNode) { 1373 $$createStartParseFunction(code, graph, i, n); 1374 1375 $$} 1376 $$} 1377 1378 $$createParseFunctions(code, graph); 1379 1380 Creator.Type getParseTree(string startNonterminal = "$(graph.grammar.getSymbolName(graph.grammar.startNonterminals[0].nonterminal).escapeD)")() 1381 { 1382 assert(stackTops.length == 0, text(stackTops)); 1383 assert(acceptedStackTops.length <= 1, text(acceptedStackTops)); 1384 if (acceptedStackTops.length) 1385 { 1386 assert(acceptedStackTops[0].previous.length == 1); 1387 assert(acceptedStackTops[0].previous[0].node.previous.length == 0); 1388 return acceptedStackTops[0].previous[0].data.nonterminal.get!(nonterminalIDFor!startNonterminal); 1389 } 1390 return Creator.Type.init; 1391 } 1392 } 1393 })); 1394 mixin(genCode("code", q{ 1395 SymbolID translateTokenIdFromLexer(alias Lexer)(SymbolID t) 1396 { 1397 $$foreach (t2; grammar.tokens.allIDs) { 1398 if (t == Lexer.tokenID!$(tokenDCode(grammar.tokens[t2]))) 1399 return $(t2.id + grammar.startTokenID); 1400 $$} 1401 return SymbolID.max; 1402 } 1403 1404 struct Parser(alias Creator, alias L) 1405 { 1406 alias Lexer = L; 1407 alias Location = typeof(Lexer.init.front.currentLocation); 1408 alias LocationDiff = typeof(Location.init - Location.init); 1409 1410 Lexer *lexer; 1411 1412 PushParser!(Creator, typeof(Lexer.init.front.content)) pushParser; 1413 1414 alias pushParser this; 1415 1416 template NonterminalType(size_t nonterminalID) if (nonterminalID >= startNonterminalID && nonterminalID < endNonterminalID) 1417 { 1418 alias NonterminalType = Creator.NonterminalType!nonterminalID; 1419 } 1420 1421 this(Creator creator, Lexer *lexer) 1422 { 1423 pushParser.creator = creator; 1424 this.lexer = lexer; 1425 } 1426 1427 $$foreach (i, n; graph.states) { 1428 $$if (n.isStartNode) { 1429 $$createWrapperParseFunction(code, graph, i, n); 1430 $$} 1431 $$} 1432 } 1433 1434 immutable allTokens = [ 1435 $(allTokensCode)]; 1436 1437 immutable allNonterminals = [ 1438 $(allNonterminalsCode)]; 1439 1440 immutable allProductions = [ 1441 $(createAllProductionsCode(grammar))]; 1442 1443 $$if (graph.globalOptions.directUnwrap) { 1444 immutable nonterminalClosures = [ 1445 $$foreach (i, n; grammar.nonterminals.vals) { 1446 /*$(i) $(grammar.getSymbolName(NonterminalID(i.to!SymbolID)))*/ [ _ 1447 $$foreach (m2; grammar.directUnwrapClosure(NonterminalID(i.to!SymbolID), [], [])) { 1448 $(grammar.nonterminalIDCode(m2.nonterminalID)), _ 1449 $$} 1450 ], 1451 $$} 1452 ]; 1453 1454 $$} 1455 immutable GrammarInfo grammarInfo = immutable(GrammarInfo)( 1456 startTokenID, startNonterminalID, startProductionID, 1457 allTokens, allNonterminals, allProductions); 1458 1459 Creator.Type parse(Creator, alias Lexer, string startNonterminal = "$(graph.grammar.getSymbolName(graph.grammar.startNonterminals[0].nonterminal).escapeD)")(ref Lexer lexer, Creator creator) 1460 { 1461 alias Location = typeof(Lexer.init.front.currentLocation); 1462 auto parser = Parser!(Creator, Lexer)( 1463 creator, 1464 &lexer); 1465 auto parseResult = parser.$(parseFunctionName(graph, 0))(); 1466 Creator.Type result = parseResult.val; 1467 creator.adjustStart(result, parseResult.start); 1468 return result; 1469 } 1470 1471 Creator.Type parse(Creator, alias Lexer, string startNonterminal = "$(graph.grammar.getSymbolName(graph.grammar.startNonterminals[0].nonterminal).escapeD)")(string input, Creator creator, typeof(Lexer.init.front.currentLocation) startLocation = typeof(Lexer.init.front.currentLocation).init) 1472 { 1473 alias Location = typeof(Lexer.init.front.currentLocation); 1474 Lexer lexer = Lexer(input, startLocation); 1475 auto result = parse!(Creator, Lexer, startNonterminal)(lexer, creator); 1476 if (!lexer.empty) 1477 { 1478 throw new SingleParseException!Location("input left after parse", 1479 lexer.front.currentLocation, lexer.front.currentTokenEnd); 1480 } 1481 return result; 1482 } 1483 })); 1484 1485 return code.data; 1486 }