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