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 assert(ifStmt !is null); 255 256 while (ifStmt.children[2] !is null) { 257 ifStmt = cast(IfStatement)ifStmt.children[2]; 258 if (ifStmt is null) 259 throw new ParserException(context.loc, "unexpected else statement"); 260 } 261 262 if (content.length) { 263 assert(ifStmt.children[2] is null); 264 265 auto elseIfStmt = create!IfStatement(Token(context.loc), parseExpr(Source(source_.id, source_.parent, content), context.loc), create!StatementBlock(Token(context.loc)), null); 266 ifStmt.children[2] = elseIfStmt; 267 268 insertStack_ ~= insert_; 269 insert_ = cast(StatementBlock)elseIfStmt.children[1]; 270 } else { 271 assert(ifStmt.children[2] is null); 272 273 auto elseBlock = create!StatementBlock(Token(context.loc)); 274 ifStmt.children[2] = elseBlock; 275 276 insertStack_ ~= insert_; 277 insert_ = cast(StatementBlock)ifStmt.children[2]; 278 } 279 } 280 281 void meta(Context context, string content) { 282 content = content[1..$].strip; 283 auto values = content.splitter(':'); 284 auto type = values.front.strip; 285 values.popFront; 286 287 switch (type) { 288 case "src": 289 auto id = values.front.to!uint; 290 values.popFront; 291 292 auto line = values.front.to!uint; 293 values.popFront; 294 295 auto column = values.front.splitter(' ').front.to!uint; 296 context.loc = SourceLoc(id, line, column); 297 break; 298 default: 299 throw new ParserException(context.loc, format("unknown meta type '%s'", type)); 300 } 301 } 302 303 void define(Context context, string content) { 304 auto lex = Lexer(Source(source_.id, source_.parent, content[1..$].strip)); 305 auto tok = lex.front; 306 lex.popFront; 307 308 switch (tok.kindKeyword) with (Token.KeywordKind) { 309 case Set: 310 auto name = lex.front; 311 lex.popFront; 312 313 auto value = lex.front; 314 lex.popFront; 315 316 settings_.set(context, name.value, value.value); 317 break; 318 case Push: 319 auto name = lex.front; 320 lex.popFront; 321 322 auto value = lex.front; 323 lex.popFront; 324 325 settings_.push(context, name.value, value.value); 326 break; 327 case Pop: 328 auto name = lex.front; 329 lex.popFront; 330 331 settings_.pop(context, name.value); 332 break; 333 default: 334 throw new ParserException(context.loc, format("unknown compile-time operation '%s'", tok.kindKeyword)); 335 } 336 } 337 338 void withs(Context context, string content) { 339 context.open(content[0..1], content[1..$]); 340 content = content[1..$].strip; 341 342 auto withStmt = parseWith(Source(source_.id, source_.parent, content), context.loc); 343 auto bodyBlock = create!StatementBlock(Token(context.loc)); 344 withStmt.children[$-1] = bodyBlock; 345 insert_.children ~= withStmt; 346 347 insertStack_ ~= insert_; 348 insert_ = bodyBlock; 349 } 350 351 Node escapeHTML(Node text, SourceLoc loc) { 352 auto escape = create!Identifier(Token(Token.Kind.Identifier, "__escape", loc)); 353 auto html = cast(Node)create!Constant(Token("html", Token.LiteralKind.String, 0, 0, loc)); 354 355 return cast(Node)create!FunctionCall(Token(loc), escape, [ text, html ]); 356 } 357 358 void translate(Context context, string content, bool autoEscape) { 359 content = content[1..$].strip; 360 361 auto args = cast(Node[])parseExprList(Source(source_.id, source_.parent, content), context.loc); 362 auto translateFunc = create!Identifier(Token(Token.Kind.Identifier, "__translate", context.loc)); 363 364 auto translated = cast(Node)create!FunctionCall(Token(context.loc), translateFunc, args); 365 if (autoEscape) 366 translated = escapeHTML(translated, context.loc); 367 insert_.children ~= create!Output(Token(context.loc), translated); 368 } 369 370 void interpolate(Context context, string content, bool autoEscape) { 371 auto args = cast(Node[])parseExprList(Source(source_.id, source_.parent, content), context.loc); 372 foreach (arg; args) { 373 if (autoEscape) 374 arg = escapeHTML(arg, context.loc); 375 insert_.children ~= create!Output(Token(context.loc), arg); 376 } 377 } 378 379 void text(Context context, string content) { 380 text_ ~= content; 381 } 382 383 void outputText(Context context) { 384 if (text_.length) { 385 auto compressionEnabled = settings_.get!bool(context, "compress"); 386 auto text = (compressionEnabled ? compress(cast(string)text_, options_.compress).idup : text_.idup).replace("\r", ""); 387 if (text.length) 388 insert_.children ~= create!Output(Token(context.loc), create!Constant(Token(text, Token.LiteralKind.String, 0, 0, context.loc))); 389 text_.length = 0; 390 } 391 } 392 393 Source source_; 394 395 StatementBlock insert_; 396 StatementBlock[] insertStack_; 397 398 SourceManager* mgr_; 399 ParserOptions options_; 400 401 ParserSettingsStack settings_; 402 403 char[] text_; 404 string[] errors_; 405 } 406 407 408 @property bool isFalsy(string x) { 409 return (x.empty || (x == "0") || (x.toLower == "no") || (x.toLower == "false")); 410 } 411 412 413 @property bool isTruthy(string x) { 414 return !x.isFalsy; 415 } 416 417 418 struct ParserSettingsStack { 419 auto get(T)(Context context, string name) { 420 if (auto pstack = name in stack_) { 421 static if (is(Unqual!T == bool)) { 422 return (*pstack).back.isTruthy; 423 } else { 424 return (*pstack).back.to!T; 425 } 426 } 427 428 throw new ParserException(context.loc, format("unknown compile-time setting '%s'", name)); 429 } 430 431 void init(string name, string value) { 432 stack_[name] = [ value ]; 433 } 434 435 void set(Context context, string name, string value) { 436 if (auto pstack = name in stack_) { 437 (*pstack).back = value; 438 return; 439 } 440 441 throw new ParserException(context.loc, format("unknown compile-time setting '%s'", name)); 442 } 443 444 void push(Context context, string name, string value) { 445 if (auto pstack = name in stack_) { 446 *pstack ~= value; 447 return; 448 } 449 450 throw new ParserException(context.loc, format("unknown compile-time setting '%s'", name)); 451 } 452 453 void pop(Context context, string name) { 454 if (auto pstack = name in stack_) { 455 if (pstack.length > 1) { 456 (*pstack).popBack; 457 return; 458 } else { 459 throw new ParserException(context.loc, format("compile-time setting stack underflow for '%s'", name)); 460 } 461 } 462 463 throw new ParserException(context.loc, format("unknown compile-time setting '%s'", name)); 464 } 465 466 private string[][string] stack_; 467 } 468 469 470 private class ExprParserException : Exception { 471 this(Token tok, string msg) { 472 super(msg); 473 this.tok = tok; 474 } 475 476 Token tok; 477 } 478 479 480 auto parseExpr(Source source, SourceLoc loc) { 481 auto parser = ExprParser(source, loc); 482 scope (success) parser.ensureEndOfInput(); 483 return parser.parseExpression(); 484 } 485 486 487 auto parseExprList(Source source, SourceLoc loc) { 488 auto parser = ExprParser(source, loc); 489 scope (success) parser.ensureEndOfInput(); 490 return parser.parseExpressionList(); 491 } 492 493 494 auto parseLoop(Source source, SourceLoc loc) { 495 auto parser = ExprParser(source, loc); 496 scope (success) parser.ensureEndOfInput(); 497 return parser.parseLoopStatement(loc); 498 } 499 500 501 auto parseWith(Source source, SourceLoc loc) { 502 auto parser = ExprParser(source, loc); 503 scope (success) parser.ensureEndOfInput(); 504 return parser.parseWithStatement(loc); 505 } 506 507 508 private struct ExprParser { 509 this(Source source, SourceLoc loc) { 510 lexer_ = Lexer(source, loc); 511 512 warmUp(); 513 } 514 515 Expression parseExpression() { 516 auto start = tok_; 517 518 if (auto left = parseExpressionPrimary()) { 519 auto expr = parseBinaryOp(left, 0); 520 if (!expr) 521 expr = left; 522 523 if (auto cond = parseConditional(expr)) 524 return create!Expression(start, cond); 525 return create!Expression(start, expr); 526 } 527 return null; 528 } 529 530 Expression[] parseExpressionList() { 531 if (auto expr = parseExpression) { 532 Expression[] exprs; 533 exprs ~= expr; 534 535 while (tok_.sep(',')) { 536 eat(); 537 if (auto next = parseExpression()) { 538 exprs ~= next; 539 continue; 540 } 541 throw new ExprParserException(tok_, format("expected an expression following ',', not '%s'", tok_)); 542 } 543 return exprs; 544 } 545 return null; 546 } 547 548 WithStatement parseWithStatement(SourceLoc loc) { 549 if (auto expr = parseWithExpression) { 550 WithExpression[] exprs; 551 exprs ~= expr; 552 553 while (tok_.sep(',')) { 554 eat(); 555 if (auto next = parseWithExpression()) { 556 exprs ~= next; 557 continue; 558 } 559 throw new ExprParserException(tok_, format("expected a with expression following ',', not '%s'", tok_)); 560 } 561 return create!WithStatement(Token(loc), cast(Node[])exprs, null); 562 } 563 return null; 564 } 565 566 WithExpression parseWithExpression() { 567 if (auto expr = parseExpression()) { 568 Token name; 569 if (tok_.keyword(Token.KeywordKind.As)) { 570 eat(); 571 if (!tok_.ident()) 572 throw new ExprParserException(tok_, format("expected an identifier following 'as', not '%s'", tok_)); 573 name = eat(); 574 } 575 return create!WithExpression(expr.tok, expr, name); 576 } 577 return null; 578 } 579 580 LoopStatement parseLoopStatement(SourceLoc loc) { 581 if (!tok_.ident) 582 throw new ExprParserException(tok_, format("expected an identifier, not '%s'", tok_)); 583 584 Token key = eat(); 585 Token name; 586 if (tok_.sep(',')) { 587 eat(); 588 if (!tok_.ident) 589 throw new ExprParserException(tok_, format("expected an identifier, not '%s'", tok_)); 590 name = eat(); 591 if (!tok_.sep(';')) 592 throw new ExprParserException(tok_, format("expected ';', not '%s'", tok_)); 593 eat(); 594 } else if (tok_.sep(';')) { 595 eat(); 596 swap(key, name); 597 } else { 598 throw new ExprParserException(tok_, format("expected ';' or ',' followed by an identifier, not '%s'", tok_)); 599 } 600 601 Expression obj; 602 Expression end; 603 604 obj = parseExpression(); 605 if (!obj) 606 throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_)); 607 608 if (tok_.name == "..") { 609 eat(); 610 end = parseExpression(); 611 if (!end) 612 throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_)); 613 } 614 615 return create!LoopStatement(Token(loc), key, name, obj, end, null); 616 } 617 618 Node parseExpressionPrimary() { 619 if (auto lexpr = parseExpressionPrimarySimple()) { 620 if (tok_.sep()) { 621 if (auto primary = parseSuffixOp(lexpr)) { 622 while (!tok_.eoi() && tok_.sep()) { 623 if (auto suffix = parseSuffixOp(primary)) { 624 primary = suffix; 625 continue; 626 } 627 return primary; 628 } 629 return primary; 630 } 631 } 632 return lexpr; 633 } 634 return null; 635 } 636 637 Node parseExpressionPrimarySimple() { 638 switch (tok_.kind) with (Token.Kind) { 639 case Identifier: 640 return parseIdentifierExpr(); 641 case Separator: 642 if (tok_.sep('(')) { 643 auto start = eat(); 644 auto expr = parseExpression(); 645 if (!expr) 646 throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_)); 647 648 close(')'); 649 return create!Expression(start, expr, true); 650 } 651 652 if (auto expr = parseUnaryOp()) 653 return expr; 654 if (auto expr = parsePrefixOp()) 655 return expr; 656 break; 657 case Literal: 658 return parseLiteralExpr(); 659 case Keyword: 660 switch (tok_.kindKeyword) with (Token.KeywordKind) { 661 case True: 662 return create!Constant(eat()); 663 case False: 664 return create!Constant(eat()); 665 case Null: 666 return create!Constant(eat()); 667 default: 668 throw new ExprParserException(tok_, format("unexpected '%s'", tok_)); 669 } 670 case Undefined: 671 case EndOfInput: 672 default: 673 break; 674 } 675 676 return null; 677 } 678 679 Node parseArrayConstructor() { 680 681 return null; 682 } 683 684 Node parseIdentifierExpr() { 685 assert(tok_.ident()); 686 return create!Identifier(eat()); 687 } 688 689 bool isUnaryOp(Token tok) const { 690 if (tok.sep()) { 691 switch (tok.length) { 692 case 1: 693 switch(tok.front) { 694 case '-': 695 case '+': 696 case '!': 697 case '~': 698 case '^': 699 case '*': 700 return true; 701 default: 702 break; 703 } 704 break; 705 default: 706 break; 707 } 708 } 709 return false; 710 } 711 712 UnaryOp parseUnaryOp() { 713 assert(tok_.sep()); 714 if (isUnaryOp(tok_)) { 715 auto op = eat(); 716 auto expr = parseExpressionPrimary(); 717 if (!expr) 718 throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_)); 719 return create!UnaryOp(op, expr); 720 } 721 return null; 722 } 723 724 Node parsePrefixOp() { 725 switch(tok_.name) { 726 case "--": 727 case "++": 728 auto op = eat(); 729 auto expr = parseExpressionPrimary(); 730 if (!expr) 731 throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_)); 732 return create!PrefixOp(op, expr); 733 default: 734 break; 735 } 736 return null; 737 } 738 739 Node parseSuffixOp(Node expr) { 740 switch(tok_.name) { 741 case "[": 742 auto op = eat(); 743 auto index = parseExpression(); 744 Expression end; 745 if (!index) 746 throw new ExprParserException(tok_, format("expected an index expression, not '%s'", tok_)); 747 748 if (tok_.sep("..")) { 749 eat(); 750 end = parseExpression; 751 if (!end) 752 throw new ExprParserException(tok_, format("expected an expression following '..', not '%s'", tok_)); 753 } 754 close(']'); 755 756 if (end is null) 757 return create!IndexOp(op, expr, index); 758 return create!SliceOp(op, expr, index, end); 759 case ".": 760 auto op = eat(); 761 if (!tok_.ident()) 762 throw new ExprParserException(tok_, format("expected an identifier following '.', not '%s'", tok_)); 763 auto ident = eat(); 764 return create!DispatchOp(op, expr, ident); 765 case "(": 766 auto op = eat(); 767 768 Node[] args; 769 770 if (!tok_.sep(')')) { 771 while (true) { 772 if (auto arg = parseExpression()) { 773 args ~= arg; 774 if (tok_.sep(')')) 775 break; 776 if (tok_.sep(',')) { 777 eat(); 778 continue; 779 } 780 781 throw new ExprParserException(tok_, format("expected ')' or ',' not '%s'", tok_)); 782 } 783 break; 784 } 785 } 786 787 close(')'); 788 return create!FunctionCall(op, expr, args); 789 case "!": 790 auto op = eat(); 791 792 Node[] args; 793 if (tok_.literal()) { 794 args ~= parseLiteralExpr(); 795 } else if (tok_.ident()) { 796 args ~= parseIdentifierExpr(); 797 } else { 798 throw new ExprParserException(tok_, format("expected a literal or an identifier following '!', not '%s'", tok_)); 799 } 800 801 return create!FunctionCall(op, expr, args); 802 case "--": 803 case "++": 804 auto op = eat(); 805 return create!SuffixOp(op, expr); 806 default: 807 break; 808 } 809 return null; 810 } 811 812 Constant parseLiteralExpr() { 813 assert(tok_.literal()); 814 return create!Constant(eat()); 815 } 816 817 Node parseUnaryExpr() { 818 return null; 819 } 820 821 enum OperatorPriority : size_t { 822 Logic = 10, 823 Compare = 20, 824 Arithmetic = 30, 825 Algebraic = 40, 826 Bitwise = 50, 827 } 828 829 size_t isBinaryOp(string name) const { 830 switch(name.front) with (OperatorPriority) { 831 case '&': 832 case '|': 833 return (name.length == 1) ? 0/*Bitwise*/ : Logic; 834 case '>': // >, >=, >>, >>= 835 case '<': // <, <=, <<, <<= 836 return ((name.length == 1) || (name[1] == '=')) ? Compare : 0/*Bitwise*/; 837 case '^': // ^, ^=, ^^, ^^= 838 return ((name.length == 1) || (name[1] == '=')) ? 0/*Bitwise*/ : Algebraic; 839 case '+': 840 case '-': 841 return (name.length == 1) ? Arithmetic : 0; 842 case '~': 843 case '*': 844 case '/': 845 case '%': 846 return (name.length == 1) ? Algebraic : 0; 847 case '=': 848 return (name.length == 2) ? Compare : 0; 849 case '!': 850 return (name.length == 2) ? Compare : 0; 851 default: 852 if ((name.length == 2) && (name == "in")) 853 return Compare/*Bitwise*/; 854 break; 855 } 856 return 0; 857 } 858 859 Node parseBinaryOp(Node left, size_t prioExpr) { 860 while (true) { 861 auto prio = isBinaryOp(tok_.name); 862 if (!prio || (prio < prioExpr)) 863 return left; 864 auto op = eat(); 865 auto right = parseExpressionPrimary(); 866 if (!right) 867 return null; 868 auto prioNext = isBinaryOp(tok_.name); 869 if (prio < prioNext) { 870 right = parseBinaryOp(right, prio + 1); 871 if (!right) 872 return null; 873 } 874 left = create!BinaryOp(op, left, right); 875 } 876 } 877 878 Node parseConditional(Node expr) { 879 if (tok_.sep('?')) { 880 auto op = eat(); 881 Node trueCase = parseExpression(); 882 Node falseCase = null; 883 if (trueCase) { 884 if (tok_.sep(':')) { 885 eat(); 886 falseCase = parseExpression(); 887 if (!falseCase) 888 throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_)); 889 } else { 890 throw new ExprParserException(tok_, format("expected ':', not '%s'", tok_)); 891 } 892 } else { 893 throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_)); 894 } 895 896 return create!ConditionalExpression(op, expr, trueCase, falseCase); 897 } 898 899 return null; 900 } 901 902 void warmUp() { 903 tok_ = lexer_.front; 904 lexer_.popFront; 905 906 if (!lexer_.empty) { 907 foreach (ref atok; ahead_) { 908 atok = lexer_.front; 909 lexer_.popFront; 910 if (lexer_.empty) 911 return; 912 } 913 } 914 } 915 916 Token eat() { 917 if (!tok_.eoi) { 918 foreach (i; 1..behind_.length) 919 behind_[i] = behind_[i - 1]; 920 921 behind_[0] = tok_; 922 tok_ = ahead_[0]; 923 foreach (i; 1..ahead_.length) 924 ahead_[i - 1] = ahead_[i]; 925 926 if (!lexer_.empty) { 927 ahead_[$ - 1] = lexer_.front; 928 lexer_.popFront; 929 } 930 931 return behind_[0]; 932 } 933 934 return tok_; 935 } 936 937 void open(char separator, Token by) { 938 if (!tok_.sep(separator)) { 939 throw new ExprParserException(tok_, format("expected '%s' following '%s', not '%s'", separator, by, tok_)); 940 } else { 941 eat(); 942 } 943 } 944 945 void close(char separator) { 946 if (!tok_.sep(separator)) { 947 throw new ExprParserException(tok_, format("expected '%s', not '%s'", separator, tok_)); 948 } else { 949 eat(); 950 } 951 } 952 953 void ensureEndOfInput() { 954 if (!tok_.eoi) 955 throw new ExprParserException(tok_, format("unexpected '%s'", tok_)); 956 } 957 958 private: 959 Token tok_; 960 Token[1] behind_; 961 Token[1] ahead_; 962 Lexer lexer_; 963 }