1 module vayne.source.parser; 2 3 4 import std.algorithm; 5 import std.array; 6 import std.conv; 7 import std.format; 8 import std.string; 9 10 import vayne.ast.node; 11 import vayne.source.compress; 12 import vayne.source.context; 13 import vayne.source.lexer; 14 import vayne.source.source; 15 import vayne.source.token; 16 17 18 struct ParserOptions { 19 CompressOptions compress; 20 } 21 22 23 class ParserErrorsException : Exception { 24 this(string[] errors) { 25 assert(!errors.empty); 26 super(errors[0]); 27 28 this.errors = errors; 29 } 30 31 string[] errors; 32 } 33 34 35 private class ParserException : Exception { 36 this(SourceLoc loc, string msg) { 37 super(msg); 38 39 this.loc = loc; 40 } 41 42 SourceLoc loc; 43 } 44 45 46 Node parse(ref SourceManager mgr, uint id, ParserOptions options) { 47 return Parser(mgr, id, options)(); 48 } 49 50 51 private struct Parser { 52 this(ref SourceManager mgr, uint id, ParserOptions options) { 53 source_ = mgr.get(id); 54 mgr_ = &mgr; 55 options_ = options; 56 57 settings_.init("compress", (options.compress != CompressOptions.none).to!string); 58 } 59 60 Node opCall() { 61 return parse(); 62 } 63 64 private: 65 Node parse() { 66 try { 67 if (auto root = parse(source_)) 68 return root; 69 } catch(Exception error) { 70 if (auto ctxError = cast(ContextException)error) { 71 errors_ ~= format("%s: %s", mgr_.loc(ctxError.loc), error.msg); 72 } else if (auto parserError = cast(ParserException)error) { 73 errors_ ~= format("%s: %s", mgr_.loc(parserError.loc), error.msg); 74 } else { 75 errors_ ~= error.msg; 76 } 77 } 78 79 throw new ParserErrorsException(errors_); 80 } 81 82 Node parse(Source source) { 83 auto context = new Context(source); 84 85 insert_ = new StatementBlock(Token(context.loc)); 86 87 const end = source.buffer.length - 2; 88 89 while (context.cursor < end) { 90 auto remaining = context.remaining(); 91 auto indexOpen = remaining.indexOf("{{"); 92 if (indexOpen == -1) 93 break; 94 95 text(context, remaining[0..indexOpen]); 96 context.advance(indexOpen); 97 98 const triple = ((indexOpen + 1 < remaining.length) && (remaining[indexOpen + 2] == '{')); 99 const open = triple ? "{{{" : "{{"; 100 const contentStart = indexOpen + open.length; 101 const close = triple ? "}}}" : "}}"; 102 auto indexClose = remaining.indexOf(close, contentStart); 103 while (indexClose != -1) { 104 if (balancedQuotes(remaining[contentStart..indexClose])) 105 break; 106 107 indexClose = remaining.indexOf(close, indexClose + close.length); 108 } 109 110 if (indexClose == -1) 111 throw new ParserException(context.loc, format("missing '%s' to close tag '%s'", close, open)); 112 113 context.advance(close.length); 114 indexClose -= contentStart; 115 116 try { 117 compile(context, source.buffer[context.cursor..context.cursor + indexClose], open, close); 118 } catch (Exception error) { 119 errors_ ~= format("%s: %s", mgr_.loc(context.loc), error.msg); 120 } 121 122 context.advance(indexClose + close.length); 123 } 124 context.expectClosed(); 125 126 if (context.cursor > 0) { 127 text(context, context.remaining()); 128 } else { 129 text(context, source.buffer); 130 } 131 132 outputText(context); 133 134 context.advance(context.remaining.length); 135 136 assert(insertStack_.empty); 137 138 if (!errors_.empty) 139 return null; 140 141 return new Module(Token(context.loc), [ insert_ ]); 142 } 143 144 void compile(Context context, string content, string tagOpen, string tagClose) { 145 void ensureSimpleTag(string tag) { 146 if (tagOpen.length != 2) 147 throw new ParserException(context.loc, format("'%s' not supported for tag '%s'", tagOpen, tag)); 148 } 149 150 if (content.length > 0) { 151 auto tag = content[0..1]; 152 switch(tag) { 153 case "*": 154 ensureSimpleTag(tag); 155 outputText(context); 156 iterate(context, content); 157 break; 158 case "/": 159 ensureSimpleTag(tag); 160 if (context.open().tag != "@") 161 outputText(context); 162 close(context, content); 163 break; 164 case "?": 165 ensureSimpleTag(tag); 166 outputText(context); 167 conditional(context, content); 168 break; 169 case ":": 170 ensureSimpleTag(tag); 171 outputText(context); 172 orElse(context, content); 173 break; 174 case "~": 175 outputText(context); 176 translate(context, content, tagOpen.length == 2); 177 break; 178 case ";": 179 ensureSimpleTag(tag); 180 meta(context, content); 181 break; 182 case "!": 183 break; 184 case "#": 185 ensureSimpleTag(tag); 186 outputText(context); 187 define(context, content); 188 break; 189 case "@": 190 ensureSimpleTag(tag); 191 withs(context, content); 192 break; 193 case "&": 194 ensureSimpleTag(tag); 195 break; 196 default: 197 outputText(context); 198 interpolate(context, content, tagOpen.length == 2); 199 break; 200 } 201 } 202 } 203 204 void iterate(Context context, string content) { 205 context.open(content[0..1], content[1..$]); 206 content = content[1..$].strip(); 207 208 auto loopStmt = parseLoop(Source(source_.id, source_.parent, content), context.loc); 209 auto bodyBlock = create!StatementBlock(Token(context.loc)); 210 loopStmt.children[2] = bodyBlock; 211 insert_.children ~= loopStmt; 212 213 insertStack_ ~= insert_; 214 insert_ = bodyBlock; 215 } 216 217 void close(Context context, string content) { 218 context.close(); 219 220 if (insertStack_.length) { 221 insert_ = insertStack_.back; 222 insertStack_.popBack; 223 } else { 224 assert(errors_.length); 225 } 226 } 227 228 void conditional(Context context, string content) { 229 context.open(content[0..1], content[1..$]); 230 content = content[1..$].strip; 231 232 auto ifStmt = create!IfStatement(Token(context.loc), parseExpr(Source(source_.id, source_.parent, content), context.loc), create!StatementBlock(Token(context.loc)), null); 233 insert_.children ~= ifStmt; 234 235 insertStack_ ~= insert_; 236 insert_ = cast(StatementBlock)ifStmt.children[1]; 237 } 238 239 void orElse(Context context, string content) { 240 context.expectOpen("?", ":"); 241 content = content[1..$].strip; 242 243 if (insertStack_.length) { 244 insert_ = insertStack_.back; 245 insertStack_.popBack; 246 } else { 247 assert(errors_.length); 248 auto ifStmt = cast(IfStatement)insert_.children.back; 249 if (!ifStmt) 250 insert_.children ~= create!IfStatement(Token(context.loc), null, create!StatementBlock(Token(context.loc)), null); 251 } 252 253 auto ifStmt = cast(IfStatement)insert_.children.back; 254 if (ifStmt is null) 255 ifStmt = create!IfStatement(Token(context.loc), null, create!StatementBlock(Token(context.loc)), null); 256 257 while (ifStmt.children[2] !is null) { 258 ifStmt = cast(IfStatement)ifStmt.children[2]; 259 if (ifStmt is null) 260 throw new ParserException(context.loc, "unexpected else statement"); 261 } 262 263 if (content.length) { 264 assert(ifStmt.children[2] is null); 265 266 auto elseIfStmt = create!IfStatement(Token(context.loc), parseExpr(Source(source_.id, source_.parent, content), context.loc), create!StatementBlock(Token(context.loc)), null); 267 ifStmt.children[2] = elseIfStmt; 268 269 insertStack_ ~= insert_; 270 insert_ = cast(StatementBlock)elseIfStmt.children[1]; 271 } else { 272 assert(ifStmt.children[2] is null); 273 274 auto elseBlock = create!StatementBlock(Token(context.loc)); 275 ifStmt.children[2] = elseBlock; 276 277 insertStack_ ~= insert_; 278 insert_ = cast(StatementBlock)ifStmt.children[2]; 279 } 280 } 281 282 void meta(Context context, string content) { 283 content = content[1..$].strip; 284 auto values = content.splitter(':'); 285 auto type = values.front.strip; 286 values.popFront; 287 288 switch (type) { 289 case "src": 290 auto id = values.front.to!uint; 291 values.popFront; 292 293 auto line = values.front.to!uint; 294 values.popFront; 295 296 auto column = values.front.splitter(' ').front.to!uint; 297 context.loc = SourceLoc(id, line, column); 298 break; 299 default: 300 throw new ParserException(context.loc, format("unknown meta type '%s'", type)); 301 } 302 } 303 304 void define(Context context, string content) { 305 auto lex = Lexer(Source(source_.id, source_.parent, content[1..$].strip)); 306 auto tok = lex.front; 307 lex.popFront; 308 309 switch (tok.kindKeyword) with (Token.KeywordKind) { 310 case Set: 311 auto name = lex.front; 312 lex.popFront; 313 314 auto value = lex.front; 315 lex.popFront; 316 317 settings_.set(context, name.value, value.value); 318 break; 319 case Push: 320 auto name = lex.front; 321 lex.popFront; 322 323 auto value = lex.front; 324 lex.popFront; 325 326 settings_.push(context, name.value, value.value); 327 break; 328 case Pop: 329 auto name = lex.front; 330 lex.popFront; 331 332 settings_.pop(context, name.value); 333 break; 334 default: 335 throw new ParserException(context.loc, format("unknown compile-time operation '%s'", tok.kindKeyword)); 336 } 337 } 338 339 void withs(Context context, string content) { 340 context.open(content[0..1], content[1..$]); 341 content = content[1..$].strip; 342 343 auto withStmt = parseWith(Source(source_.id, source_.parent, content), context.loc); 344 auto bodyBlock = create!StatementBlock(Token(context.loc)); 345 withStmt.children[$-1] = bodyBlock; 346 insert_.children ~= withStmt; 347 348 insertStack_ ~= insert_; 349 insert_ = bodyBlock; 350 } 351 352 Node escapeHTML(Node text, SourceLoc loc) { 353 auto escape = create!Identifier(Token(Token.Kind.Identifier, "__escape", loc)); 354 auto html = cast(Node)create!Constant(Token("html", Token.LiteralKind.String, 0, 0, loc)); 355 356 return cast(Node)create!FunctionCall(Token(loc), escape, [ text, html ]); 357 } 358 359 void translate(Context context, string content, bool autoEscape) { 360 content = content[1..$].strip; 361 362 auto args = cast(Node[])parseExprList(Source(source_.id, source_.parent, content), context.loc); 363 auto translateFunc = create!Identifier(Token(Token.Kind.Identifier, "__translate", context.loc)); 364 365 auto translated = cast(Node)create!FunctionCall(Token(context.loc), translateFunc, args); 366 if (autoEscape) 367 translated = escapeHTML(translated, context.loc); 368 insert_.children ~= create!Output(Token(context.loc), translated); 369 } 370 371 void interpolate(Context context, string content, bool autoEscape) { 372 auto args = cast(Node[])parseExprList(Source(source_.id, source_.parent, content), context.loc); 373 foreach (arg; args) { 374 if (autoEscape) 375 arg = escapeHTML(arg, context.loc); 376 insert_.children ~= create!Output(Token(context.loc), arg); 377 } 378 } 379 380 void text(Context context, string content) { 381 text_ ~= content; 382 } 383 384 void outputText(Context context) { 385 if (text_.length) { 386 auto compressionEnabled = settings_.get!bool(context, "compress"); 387 auto text = (compressionEnabled ? compress(cast(string)text_, options_.compress).idup : text_.idup).replace("\r", ""); 388 if (text.length) 389 insert_.children ~= create!Output(Token(context.loc), create!Constant(Token(text, Token.LiteralKind.String, 0, 0, context.loc))); 390 text_.length = 0; 391 } 392 } 393 394 Source source_; 395 396 StatementBlock insert_; 397 StatementBlock[] insertStack_; 398 399 SourceManager* mgr_; 400 ParserOptions options_; 401 402 ParserSettingsStack settings_; 403 404 char[] text_; 405 string[] errors_; 406 } 407 408 409 @property bool isFalsy(string x) { 410 return (x.empty || (x == "0") || (x.toLower == "no") || (x.toLower == "false")); 411 } 412 413 414 @property bool isTruthy(string x) { 415 return !x.isFalsy; 416 } 417 418 419 struct ParserSettingsStack { 420 import std.traits : Unqual; 421 auto get(T)(Context context, string name) { 422 if (auto pstack = name in stack_) { 423 static if (is(Unqual!T == bool)) { 424 return (*pstack).back.isTruthy; 425 } else { 426 return (*pstack).back.to!T; 427 } 428 } 429 430 throw new ParserException(context.loc, format("unknown compile-time setting '%s'", name)); 431 } 432 433 void init(string name, string value) { 434 stack_[name] = [ value ]; 435 } 436 437 void set(Context context, string name, string value) { 438 if (auto pstack = name in stack_) { 439 (*pstack).back = value; 440 return; 441 } 442 443 throw new ParserException(context.loc, format("unknown compile-time setting '%s'", name)); 444 } 445 446 void push(Context context, string name, string value) { 447 if (auto pstack = name in stack_) { 448 *pstack ~= value; 449 return; 450 } 451 452 throw new ParserException(context.loc, format("unknown compile-time setting '%s'", name)); 453 } 454 455 void pop(Context context, string name) { 456 if (auto pstack = name in stack_) { 457 if (pstack.length > 1) { 458 (*pstack).popBack; 459 return; 460 } else { 461 throw new ParserException(context.loc, format("compile-time setting stack underflow for '%s'", name)); 462 } 463 } 464 465 throw new ParserException(context.loc, format("unknown compile-time setting '%s'", name)); 466 } 467 468 private string[][string] stack_; 469 } 470 471 472 private class ExprParserException : Exception { 473 this(Token tok, string msg) { 474 super(msg); 475 this.tok = tok; 476 } 477 478 Token tok; 479 } 480 481 482 auto parseExpr(Source source, SourceLoc loc) { 483 auto parser = ExprParser(source, loc); 484 scope (success) parser.ensureEndOfInput(); 485 return parser.parseExpression(); 486 } 487 488 489 auto parseExprList(Source source, SourceLoc loc) { 490 auto parser = ExprParser(source, loc); 491 scope (success) parser.ensureEndOfInput(); 492 return parser.parseExpressionList(); 493 } 494 495 496 auto parseLoop(Source source, SourceLoc loc) { 497 auto parser = ExprParser(source, loc); 498 scope (success) parser.ensureEndOfInput(); 499 return parser.parseLoopStatement(loc); 500 } 501 502 503 auto parseWith(Source source, SourceLoc loc) { 504 auto parser = ExprParser(source, loc); 505 scope (success) parser.ensureEndOfInput(); 506 return parser.parseWithStatement(loc); 507 } 508 509 510 private struct ExprParser { 511 this(Source source, SourceLoc loc) { 512 lexer_ = Lexer(source, loc); 513 514 warmUp(); 515 } 516 517 Expression parseExpression() { 518 auto start = tok_; 519 520 if (auto left = parseExpressionPrimary()) { 521 auto expr = parseBinaryOp(left, 0); 522 if (!expr) 523 expr = left; 524 525 if (auto cond = parseConditional(expr)) 526 return create!Expression(start, cond); 527 return create!Expression(start, expr); 528 } 529 return null; 530 } 531 532 Expression[] parseExpressionList() { 533 if (auto expr = parseExpression) { 534 Expression[] exprs; 535 exprs ~= expr; 536 537 while (tok_.sep(',')) { 538 eat(); 539 if (auto next = parseExpression()) { 540 exprs ~= next; 541 continue; 542 } 543 throw new ExprParserException(tok_, format("expected an expression following ',', not '%s'", tok_)); 544 } 545 return exprs; 546 } 547 return null; 548 } 549 550 WithStatement parseWithStatement(SourceLoc loc) { 551 if (auto expr = parseWithExpression) { 552 WithExpression[] exprs; 553 exprs ~= expr; 554 555 while (tok_.sep(',')) { 556 eat(); 557 if (auto next = parseWithExpression()) { 558 exprs ~= next; 559 continue; 560 } 561 throw new ExprParserException(tok_, format("expected a with expression following ',', not '%s'", tok_)); 562 } 563 return create!WithStatement(Token(loc), cast(Node[])exprs, null); 564 } 565 return null; 566 } 567 568 WithExpression parseWithExpression() { 569 if (auto expr = parseExpression()) { 570 Token name; 571 if (tok_.keyword(Token.KeywordKind.As)) { 572 eat(); 573 if (!tok_.ident()) 574 throw new ExprParserException(tok_, format("expected an identifier following 'as', not '%s'", tok_)); 575 name = eat(); 576 } 577 return create!WithExpression(expr.tok, expr, name); 578 } 579 return null; 580 } 581 582 LoopStatement parseLoopStatement(SourceLoc loc) { 583 if (!tok_.ident) 584 throw new ExprParserException(tok_, format("expected an identifier, not '%s'", tok_)); 585 586 Token key = eat(); 587 Token name; 588 if (tok_.sep(',')) { 589 eat(); 590 if (!tok_.ident) 591 throw new ExprParserException(tok_, format("expected an identifier, not '%s'", tok_)); 592 name = eat(); 593 if (!tok_.sep(';')) 594 throw new ExprParserException(tok_, format("expected ';', not '%s'", tok_)); 595 eat(); 596 } else if (tok_.sep(';')) { 597 eat(); 598 swap(key, name); 599 } else { 600 throw new ExprParserException(tok_, format("expected ';' or ',' followed by an identifier, not '%s'", tok_)); 601 } 602 603 Expression obj; 604 Expression end; 605 606 obj = parseExpression(); 607 if (!obj) 608 throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_)); 609 610 if (tok_.name == "..") { 611 eat(); 612 end = parseExpression(); 613 if (!end) 614 throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_)); 615 } 616 617 return create!LoopStatement(Token(loc), key, name, obj, end, null); 618 } 619 620 Node parseExpressionPrimary() { 621 if (auto lexpr = parseExpressionPrimarySimple()) { 622 if (tok_.sep()) { 623 if (auto primary = parseSuffixOp(lexpr)) { 624 while (!tok_.eoi() && tok_.sep()) { 625 if (auto suffix = parseSuffixOp(primary)) { 626 primary = suffix; 627 continue; 628 } 629 return primary; 630 } 631 return primary; 632 } 633 } 634 return lexpr; 635 } 636 return null; 637 } 638 639 Node parseExpressionPrimarySimple() { 640 switch (tok_.kind) with (Token.Kind) { 641 case Identifier: 642 return parseIdentifierExpr(); 643 case Separator: 644 if (tok_.sep('(')) { 645 auto start = eat(); 646 auto expr = parseExpression(); 647 if (!expr) 648 throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_)); 649 650 close(')'); 651 return create!Expression(start, expr, true); 652 } 653 654 if (auto expr = parseUnaryOp()) 655 return expr; 656 if (auto expr = parsePrefixOp()) 657 return expr; 658 break; 659 case Literal: 660 return parseLiteralExpr(); 661 case Keyword: 662 switch (tok_.kindKeyword) with (Token.KeywordKind) { 663 case True: 664 return create!Constant(eat()); 665 case False: 666 return create!Constant(eat()); 667 case Null: 668 return create!Constant(eat()); 669 default: 670 throw new ExprParserException(tok_, format("unexpected '%s'", tok_)); 671 } 672 case Undefined: 673 case EndOfInput: 674 default: 675 break; 676 } 677 678 return null; 679 } 680 681 Node parseIdentifierExpr() { 682 assert(tok_.ident()); 683 return create!Identifier(eat()); 684 } 685 686 bool isUnaryOp(Token tok) const { 687 if (tok.sep()) { 688 switch (tok.length) { 689 case 1: 690 switch(tok.front) { 691 case '-': 692 case '+': 693 case '!': 694 case '~': 695 case '^': 696 case '*': 697 return true; 698 default: 699 break; 700 } 701 break; 702 default: 703 break; 704 } 705 } 706 return false; 707 } 708 709 UnaryOp parseUnaryOp() { 710 assert(tok_.sep()); 711 if (isUnaryOp(tok_)) { 712 auto op = eat(); 713 auto expr = parseExpressionPrimary(); 714 if (!expr) 715 throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_)); 716 return create!UnaryOp(op, expr); 717 } 718 return null; 719 } 720 721 Node parsePrefixOp() { 722 switch(tok_.name) { 723 case "--": 724 case "++": 725 auto op = eat(); 726 auto expr = parseExpressionPrimary(); 727 if (!expr) 728 throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_)); 729 return create!PrefixOp(op, expr); 730 default: 731 break; 732 } 733 return null; 734 } 735 736 Node parseSuffixOp(Node expr) { 737 switch(tok_.name) { 738 case "[": 739 auto op = eat(); 740 auto index = parseExpression(); 741 Expression end; 742 if (!index) 743 throw new ExprParserException(tok_, format("expected an index expression, not '%s'", tok_)); 744 745 if (tok_.sep("..")) { 746 eat(); 747 end = parseExpression; 748 if (!end) 749 throw new ExprParserException(tok_, format("expected an expression following '..', not '%s'", tok_)); 750 } 751 close(']'); 752 753 if (end is null) 754 return create!IndexOp(op, expr, index); 755 return create!SliceOp(op, expr, index, end); 756 case ".": 757 auto op = eat(); 758 if (!tok_.ident()) 759 throw new ExprParserException(tok_, format("expected an identifier following '.', not '%s'", tok_)); 760 auto ident = eat(); 761 return create!DispatchOp(op, expr, ident); 762 case "(": 763 auto op = eat(); 764 765 Node[] args; 766 767 if (!tok_.sep(')')) { 768 while (true) { 769 if (auto arg = parseExpression()) { 770 args ~= arg; 771 if (tok_.sep(')')) 772 break; 773 if (tok_.sep(',')) { 774 eat(); 775 continue; 776 } 777 778 throw new ExprParserException(tok_, format("expected ')' or ',' not '%s'", tok_)); 779 } 780 break; 781 } 782 } 783 784 close(')'); 785 return create!FunctionCall(op, expr, args); 786 case "!": 787 auto op = eat(); 788 789 Node[] args; 790 if (tok_.literal()) { 791 args ~= parseLiteralExpr(); 792 } else if (tok_.ident()) { 793 args ~= parseIdentifierExpr(); 794 } else { 795 throw new ExprParserException(tok_, format("expected a literal or an identifier following '!', not '%s'", tok_)); 796 } 797 798 return create!FunctionCall(op, expr, args); 799 case "--": 800 case "++": 801 auto op = eat(); 802 return create!SuffixOp(op, expr); 803 default: 804 break; 805 } 806 return null; 807 } 808 809 Constant parseLiteralExpr() { 810 assert(tok_.literal()); 811 return create!Constant(eat()); 812 } 813 814 Node parseUnaryExpr() { 815 return null; 816 } 817 818 enum OperatorPriority : size_t { 819 Logic = 10, 820 Compare = 20, 821 Arithmetic = 30, 822 Algebraic = 40, 823 Bitwise = 50, 824 } 825 826 size_t isBinaryOp(string name) const { 827 switch(name.front) with (OperatorPriority) { 828 case '&': 829 case '|': 830 return (name.length == 1) ? 0/*Bitwise*/ : Logic; 831 case '>': // >, >=, >>, >>= 832 case '<': // <, <=, <<, <<= 833 return ((name.length == 1) || (name[1] == '=')) ? Compare : 0/*Bitwise*/; 834 case '^': // ^, ^=, ^^, ^^= 835 return ((name.length == 1) || (name[1] == '=')) ? 0/*Bitwise*/ : Algebraic; 836 case '+': 837 case '-': 838 return (name.length == 1) ? Arithmetic : 0; 839 case '~': 840 case '*': 841 case '/': 842 case '%': 843 return (name.length == 1) ? Algebraic : 0; 844 case '=': 845 return (name.length == 2) ? Compare : 0; 846 case '!': 847 return (name.length == 2) ? Compare : 0; 848 default: 849 if ((name.length == 2) && (name == "in")) 850 return Compare/*Bitwise*/; 851 break; 852 } 853 return 0; 854 } 855 856 Node parseBinaryOp(Node left, size_t prioExpr) { 857 while (true) { 858 auto prio = isBinaryOp(tok_.name); 859 if (!prio || (prio < prioExpr)) 860 return left; 861 auto op = eat(); 862 auto right = parseExpressionPrimary(); 863 if (!right) 864 return null; 865 auto prioNext = isBinaryOp(tok_.name); 866 if (prio < prioNext) { 867 right = parseBinaryOp(right, prio + 1); 868 if (!right) 869 return null; 870 } 871 left = create!BinaryOp(op, left, right); 872 } 873 } 874 875 Node parseConditional(Node expr) { 876 if (tok_.sep('?')) { 877 auto op = eat(); 878 Node trueCase = parseExpression(); 879 Node falseCase = null; 880 if (trueCase) { 881 if (tok_.sep(':')) { 882 eat(); 883 falseCase = parseExpression(); 884 if (!falseCase) 885 throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_)); 886 } else { 887 throw new ExprParserException(tok_, format("expected ':', not '%s'", tok_)); 888 } 889 } else { 890 throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_)); 891 } 892 893 return create!ConditionalExpression(op, expr, trueCase, falseCase); 894 } 895 896 return null; 897 } 898 899 void warmUp() { 900 tok_ = lexer_.front; 901 lexer_.popFront; 902 903 if (!lexer_.empty) { 904 foreach (ref atok; ahead_) { 905 atok = lexer_.front; 906 lexer_.popFront; 907 if (lexer_.empty) 908 return; 909 } 910 } 911 } 912 913 Token eat() { 914 if (!tok_.eoi) { 915 foreach (i; 1..behind_.length) 916 behind_[i] = behind_[i - 1]; 917 918 behind_[0] = tok_; 919 tok_ = ahead_[0]; 920 foreach (i; 1..ahead_.length) 921 ahead_[i - 1] = ahead_[i]; 922 923 if (!lexer_.empty) { 924 ahead_[$ - 1] = lexer_.front; 925 lexer_.popFront; 926 } 927 928 return behind_[0]; 929 } 930 931 return tok_; 932 } 933 934 void open(char separator, Token by) { 935 if (!tok_.sep(separator)) { 936 throw new ExprParserException(tok_, format("expected '%s' following '%s', not '%s'", separator, by, tok_)); 937 } else { 938 eat(); 939 } 940 } 941 942 void close(char separator) { 943 if (!tok_.sep(separator)) { 944 throw new ExprParserException(tok_, format("expected '%s', not '%s'", separator, tok_)); 945 } else { 946 eat(); 947 } 948 } 949 950 void ensureEndOfInput() { 951 if (!tok_.eoi) 952 throw new ExprParserException(tok_, format("unexpected '%s'", tok_)); 953 } 954 955 private: 956 Token tok_; 957 Token[1] behind_; 958 Token[1] ahead_; 959 Lexer lexer_; 960 }