1 /* 2 * Hunt - A high-level D Programming Language Web framework that encourages rapid development and clean, pragmatic design. 3 * 4 * Copyright (C) 2015-2019, HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.framework.view.Parser; 13 14 public 15 { 16 import hunt.framework.view.Lexer : Position; 17 } 18 19 private 20 { 21 import std.array : appender; 22 import std.conv : to; 23 import std.file : exists, read; 24 import std.path : dirName,absolutePath,dirSeparator; 25 import std.format: fmt = format; 26 import std.range; 27 28 import hunt.logging; 29 import hunt.framework.view.ast; 30 import hunt.framework.view.Lexer; 31 import hunt.framework.view.Exception : TemplateParserException, 32 assertTemplate = assertTemplateParser; 33 } 34 35 36 struct Parser(Lexer) 37 { 38 struct ParserState 39 { 40 Token[] tokens; 41 BlockNode[string] blocks; 42 } 43 44 private 45 { 46 TemplateNode[string] _parsedFiles; 47 48 Token[] _tokens; 49 BlockNode[string] _blocks; 50 51 ParserState[] _states; 52 } 53 54 void preprocess() 55 { 56 import std.string : stripRight, stripLeft; 57 58 auto newTokens = appender!(Token[]); 59 60 for (int i = 0; i < _tokens.length;) 61 { 62 if (i < _tokens.length - 1 63 && _tokens[i] == Type.StmtBegin 64 && _tokens[i+1] == Operator.Minus) 65 { 66 newTokens.put(_tokens[i]); 67 i += 2; 68 } 69 else if(i < _tokens.length - 1 70 && _tokens[i] == Operator.Minus 71 && _tokens[i+1] == Type.StmtEnd) 72 { 73 newTokens.put(_tokens[i+1]); 74 i += 2; 75 } 76 else if (_tokens[i] == Type.Raw) 77 { 78 bool stripR = false; 79 bool stripL = false; 80 bool stripInlineR = false; 81 82 if (i >= 2 83 && _tokens[i-2] == Operator.Minus 84 && _tokens[i-1] == Type.StmtEnd 85 ) 86 stripL = true; 87 88 if (i < _tokens.length - 2 && _tokens[i+1] == Type.StmtBegin) 89 { 90 if (_tokens[i+2] == Operator.Minus) 91 stripR = true; 92 else if (_tokens[i+1].value == Lexer.stmtInline) 93 stripInlineR = true; 94 } 95 96 auto str = _tokens[i].value; 97 str = stripR ? str.stripRight : str; 98 str = stripL ? str.stripLeft : str; 99 str = stripInlineR ? str.stripOnceRight : str; 100 newTokens.put(Token(Type.Raw, str, _tokens[i].pos)); 101 i++; 102 } 103 else 104 { 105 newTokens.put(_tokens[i]); 106 i++; 107 } 108 } 109 _tokens = newTokens.data; 110 } 111 112 113 TemplateNode parseTree(string str, string filename ,string dirPath) 114 { 115 stashState(); 116 117 auto lexer = Lexer(str, filename); 118 auto newTokens = appender!(Token[]); 119 120 while (true) 121 { 122 auto tkn = lexer.nextToken; 123 newTokens.put(tkn); 124 if (tkn.type == Type.EOF) 125 break; 126 } 127 _tokens = newTokens.data; 128 129 preprocess(); 130 131 auto root = parseStatementBlock(dirPath); 132 auto blocks = _blocks; 133 134 if (front.type != Type.EOF) 135 assertTemplate(0, "Expected EOF found %s(%s)".fmt(front.type, front.value), front.pos); 136 137 popState(); 138 139 return new TemplateNode(Position(filename, 1, 1), root, blocks); 140 } 141 142 143 TemplateNode parseTreeFromFile(string path) 144 { 145 string dirPath = dirName(path) ~ dirSeparator; 146 // path = path.absolute(_path); 147 version(HUNT_FM_DEBUG) logDebug("parse file absolute path : ",path); 148 if (auto cached = path in _parsedFiles) 149 { 150 if (*cached is null) 151 assertTemplate(0, "Recursive imports/includes/extends not allowed: ".fmt(path), front.pos); 152 else 153 return *cached; 154 } 155 156 // Prevent recursive imports 157 auto str = cast(string)read(path); 158 _parsedFiles[path] = parseTree(str, path,dirPath); 159 160 return _parsedFiles[path]; 161 } 162 163 164 private: 165 /** 166 * exprblock = EXPRBEGIN expr (IF expr (ELSE expr)? )? EXPREND 167 */ 168 ExprNode parseExpression(string dirPath) 169 { 170 Node expr; 171 auto pos = front.pos; 172 173 pop(Type.ExprBegin); 174 expr = parseHighLevelExpression(dirPath); 175 pop(Type.ExprEnd); 176 177 return new ExprNode(pos, expr); 178 } 179 180 StmtBlockNode parseStatementBlock(string dirPath) 181 { 182 auto block = new StmtBlockNode(front.pos); 183 184 while (front.type != Type.EOF) 185 { 186 auto pos = front.pos; 187 switch(front.type) with (Type) 188 { 189 case Raw: 190 auto raw = pop.value; 191 if (raw.length) 192 block.children ~= new RawNode(pos, raw); 193 break; 194 195 case ExprBegin: 196 block.children ~= parseExpression(dirPath); 197 break; 198 199 case CmntBegin: 200 parseComment(dirPath); 201 break; 202 203 case CmntInline: 204 pop(); 205 break; 206 207 case StmtBegin: 208 if (next.type == Type.Keyword 209 && next.value.toKeyword.isBeginingKeyword) 210 block.children ~= parseStatement(dirPath); 211 else 212 return block; 213 break; 214 215 default: 216 return block; 217 } 218 } 219 220 return block; 221 } 222 223 224 Node parseStatement(string dirPath) 225 { 226 pop(Type.StmtBegin); 227 228 switch(front.value) with (Keyword) 229 { 230 case If: return parseIf(dirPath); 231 case For: return parseFor(dirPath); 232 case Set: return parseSet(dirPath); 233 case Macro: return parseMacro(dirPath); 234 case Call: return parseCall(dirPath); 235 case Filter: return parseFilterBlock(dirPath); 236 case With: return parseWith(dirPath); 237 case Import: return parseImport(dirPath); 238 case From: return parseImportFrom(dirPath); 239 case Include: return parseInclude(dirPath); 240 case Extends: return parseExtends(dirPath); 241 242 case Block: 243 auto block = parseBlock( dirPath); 244 _blocks[block.name] = block; 245 return block; 246 default: 247 assert(0, "Not implemented kw %s".fmt(front.value)); 248 } 249 } 250 251 252 ForNode parseFor(string dirPath) 253 { 254 string[] keys; 255 bool isRecursive = false; 256 Node cond = null; 257 auto pos = front.pos; 258 259 pop(Keyword.For); 260 261 keys ~= pop(Type.Ident).value; 262 while(front != Operator.In) 263 { 264 pop(Type.Comma); 265 keys ~= pop(Type.Ident).value; 266 } 267 268 pop(Operator.In); 269 270 Node iterable; 271 272 switch (front.type) with (Type) 273 { 274 case LParen: iterable = parseTuple(dirPath); break; 275 case LSParen: iterable = parseList(dirPath); break; 276 case LBrace: iterable = parseDict(dirPath); break; 277 default: iterable = parseIdent(dirPath); 278 } 279 280 if (front == Keyword.If) 281 { 282 pop(Keyword.If); 283 cond = parseHighLevelExpression(dirPath); 284 } 285 286 if (front == Keyword.Recursive) 287 { 288 pop(Keyword.Recursive); 289 isRecursive = true; 290 } 291 292 pop(Type.StmtEnd); 293 294 auto block = parseStatementBlock(dirPath); 295 296 pop(Type.StmtBegin); 297 298 switch (front.value) with (Keyword) 299 { 300 case EndFor: 301 pop(Keyword.EndFor); 302 pop(Type.StmtEnd); 303 return new ForNode(pos, keys, iterable, block, null, cond, isRecursive); 304 case Else: 305 pop(Keyword.Else); 306 pop(Type.StmtEnd); 307 auto other = parseStatementBlock(dirPath); 308 pop(Type.StmtBegin); 309 pop(Keyword.EndFor); 310 pop(Type.StmtEnd); 311 return new ForNode(pos, keys, iterable, block, other, cond, isRecursive); 312 default: 313 assertTemplate(0, "Unexpected token %s(%s)".fmt(front.type, front.value), front.pos); 314 assert(0); 315 } 316 } 317 318 319 IfNode parseIf(string dirPath) 320 { 321 auto pos = front.pos; 322 assertTemplate(front == Keyword.If || front == Keyword.ElIf, "Expected If/Elif", pos); 323 pop(); 324 auto cond = parseHighLevelExpression(dirPath); 325 pop(Type.StmtEnd); 326 327 auto then = parseStatementBlock(dirPath); 328 329 pop(Type.StmtBegin); 330 331 switch (front.value) with (Keyword) 332 { 333 case ElIf: 334 auto other = parseIf(dirPath); 335 return new IfNode(pos, cond, then, other); 336 case Else: 337 pop(Keyword.Else, Type.StmtEnd); 338 auto other = parseStatementBlock(dirPath); 339 pop(Type.StmtBegin, Keyword.EndIf, Type.StmtEnd); 340 return new IfNode(pos, cond, then, other); 341 case EndIf: 342 pop(Keyword.EndIf, Type.StmtEnd); 343 return new IfNode(pos, cond, then, null); 344 default: 345 assertTemplate(0, "Unexpected token %s(%s)".fmt(front.type, front.value), front.pos); 346 assert(0); 347 } 348 } 349 350 351 SetNode parseSet(string dirPath) 352 { 353 auto setPos = front.pos; 354 355 pop(Keyword.Set); 356 357 auto assigns = parseSequenceOf!parseAssignable(dirPath,Type.Operator); 358 359 pop(Operator.Assign); 360 361 auto listPos = front.pos; 362 auto exprs = parseSequenceOf!parseHighLevelExpression(dirPath,Type.StmtEnd); 363 Node expr = exprs.length == 1 ? exprs[0] : new ListNode(listPos, exprs); 364 365 pop(Type.StmtEnd); 366 367 return new SetNode(setPos, assigns, expr); 368 } 369 370 371 AssignableNode parseAssignable(string dirPath) 372 { 373 auto pos = front.pos; 374 string name = pop(Type.Ident).value; 375 Node[] subIdents = []; 376 377 while (true) 378 { 379 switch (front.type) with (Type) 380 { 381 case Dot: 382 pop(Dot); 383 auto strPos = front.pos; 384 subIdents ~= new StringNode(strPos, pop(Ident).value); 385 break; 386 case LSParen: 387 pop(LSParen); 388 subIdents ~= parseHighLevelExpression(dirPath); 389 pop(RSParen); 390 break; 391 default: 392 return new AssignableNode(pos, name, subIdents); 393 } 394 } 395 } 396 397 398 MacroNode parseMacro(string dirPath) 399 { 400 auto pos = front.pos; 401 pop(Keyword.Macro); 402 403 auto name = pop(Type.Ident).value; 404 Arg[] args; 405 406 if (front.type == Type.LParen) 407 { 408 pop(Type.LParen); 409 args = parseFormalArgs( dirPath); 410 pop(Type.RParen); 411 } 412 413 pop(Type.StmtEnd); 414 415 auto block = parseStatementBlock( dirPath); 416 417 pop(Type.StmtBegin, Keyword.EndMacro); 418 419 bool ret = false; 420 if (front.type == Type.Keyword && front.value == Keyword.Return) 421 { 422 pop(Keyword.Return); 423 block.children ~= parseHighLevelExpression(dirPath); 424 ret = true; 425 } 426 else 427 block.children ~= new NilNode; // void return 428 429 pop(Type.StmtEnd); 430 431 return new MacroNode(pos, name, args, block, ret); 432 } 433 434 435 CallNode parseCall(string dirPath) 436 { 437 auto pos = front.pos; 438 pop(Keyword.Call); 439 440 Arg[] formalArgs; 441 442 if (front.type == Type.LParen) 443 { 444 pop(Type.LParen); 445 formalArgs = parseFormalArgs( dirPath); 446 pop(Type.RParen); 447 } 448 449 auto macroName = front.value; 450 auto factArgs = parseCallExpr( dirPath); 451 452 pop(Type.StmtEnd); 453 454 auto block = parseStatementBlock( dirPath); 455 block.children ~= new NilNode; // void return 456 457 pop(Type.StmtBegin, Keyword.EndCall, Type.StmtEnd); 458 459 return new CallNode(pos, macroName, formalArgs, factArgs, block); 460 } 461 462 463 FilterBlockNode parseFilterBlock(string dirPath) 464 { 465 auto pos = front.pos; 466 pop(Keyword.Filter); 467 468 auto filterName = front.value; 469 auto args = parseCallExpr(dirPath); 470 471 pop(Type.StmtEnd); 472 473 auto block = parseStatementBlock(dirPath); 474 475 pop(Type.StmtBegin, Keyword.EndFilter, Type.StmtEnd); 476 477 return new FilterBlockNode(pos, filterName, args, block); 478 } 479 480 StmtBlockNode parseWith(string dirPath) 481 { 482 pop(Keyword.With, Type.StmtEnd); 483 auto block = parseStatementBlock(dirPath); 484 pop(Type.StmtBegin, Keyword.EndWith, Type.StmtEnd); 485 486 return block; 487 } 488 489 490 ImportNode parseImport(string dirPath) 491 { 492 auto pos = front.pos; 493 pop(Keyword.Import); 494 auto path = pop(Type.String).value.absolute(dirPath); 495 bool withContext = false; 496 497 if (front == Keyword.With) 498 { 499 withContext = true; 500 pop(Keyword.With, Keyword.Context); 501 } 502 503 if (front == Keyword.Without) 504 { 505 withContext = false; 506 pop(Keyword.Without, Keyword.Context); 507 } 508 509 pop(Type.StmtEnd); 510 511 assertTemplate(path.fileExist(dirPath), "Non existing file `%s`".fmt(path), pos); 512 513 auto stmtBlock = parseTreeFromFile(path); 514 515 return new ImportNode(pos, path, cast(ImportNode.Rename[])[], stmtBlock, withContext); 516 } 517 518 519 ImportNode parseImportFrom(string dirPath) 520 { 521 auto pos = front.pos; 522 pop(Keyword.From); 523 auto path = pop(Type.String).value.absolute(dirPath); 524 pop(Keyword.Import); 525 526 ImportNode.Rename[] macros; 527 528 bool firstName = true; 529 while (front == Type.Comma || firstName) 530 { 531 if (!firstName) 532 pop(Type.Comma); 533 534 auto was = pop(Type.Ident).value; 535 auto become = was; 536 537 if (front == Keyword.As) 538 { 539 pop(Keyword.As); 540 become = pop(Type.Ident).value; 541 } 542 543 macros ~= ImportNode.Rename(was, become); 544 545 firstName = false; 546 } 547 548 bool withContext = false; 549 550 if (front == Keyword.With) 551 { 552 withContext = true; 553 pop(Keyword.With, Keyword.Context); 554 } 555 556 if (front == Keyword.Without) 557 { 558 withContext = false; 559 pop(Keyword.Without, Keyword.Context); 560 } 561 562 pop(Type.StmtEnd); 563 564 assertTemplate(path.fileExist(dirPath), "Non existing file `%s`".fmt(path), pos); 565 566 auto stmtBlock = parseTreeFromFile(path); 567 568 return new ImportNode(pos, path, macros, stmtBlock, withContext); 569 } 570 571 572 IncludeNode parseInclude(string dirPath) 573 { 574 auto pos = front.pos; 575 pop(Keyword.Include); 576 577 string[] names; 578 579 if (front == Type.LSParen) 580 { 581 pop(Type.LSParen); 582 583 names ~= pop(Type.String).value; 584 while (front == Type.Comma) 585 { 586 pop(Type.Comma); 587 names ~= pop(Type.String).value; 588 } 589 590 pop(Type.RSParen); 591 } 592 else 593 names ~= pop(Type.String).value; 594 595 596 bool ignoreMissing = false; 597 if (front == Keyword.Ignore) 598 { 599 pop(Keyword.Ignore, Keyword.Missing); 600 ignoreMissing = true; 601 } 602 603 bool withContext = true; 604 605 if (front == Keyword.With) 606 { 607 withContext = true; 608 pop(Keyword.With, Keyword.Context); 609 } 610 611 if (front == Keyword.Without) 612 { 613 withContext = false; 614 pop(Keyword.Without, Keyword.Context); 615 } 616 617 pop(Type.StmtEnd); 618 619 foreach (name; names) 620 if (name.fileExist(dirPath)) 621 return new IncludeNode(pos, name, parseTreeFromFile(dirPath ~ name), withContext); 622 623 assertTemplate(ignoreMissing, "No existing files `%s`".fmt(names), pos); 624 625 return new IncludeNode(pos, "", null, withContext); 626 } 627 628 629 ExtendsNode parseExtends(string dirPath) 630 { 631 auto pos = front.pos; 632 pop(Keyword.Extends); 633 auto path = pop(Type.String).value.absolute(dirPath); 634 pop(Type.StmtEnd); 635 636 assertTemplate(path.fileExist(dirPath), "Non existing file `%s`".fmt(path), pos); 637 638 auto stmtBlock = parseTreeFromFile(path); 639 640 return new ExtendsNode(pos, path, stmtBlock); 641 } 642 643 644 BlockNode parseBlock(string dirPath) 645 { 646 auto pos = front.pos; 647 pop(Keyword.Block); 648 auto name = pop(Type.Ident).value; 649 pop(Type.StmtEnd); 650 651 auto stmt = parseStatementBlock( dirPath); 652 653 pop(Type.StmtBegin, Keyword.EndBlock); 654 655 auto posNameEnd = front.pos; 656 if (front == Type.Ident) 657 assertTemplate(pop.value == name, "Missmatching block's begin/end names", posNameEnd); 658 659 pop(Type.StmtEnd); 660 661 return new BlockNode(pos, name, stmt); 662 } 663 664 Arg[] parseFormalArgs(string dirPath) 665 { 666 Arg[] args = []; 667 bool isVarargs = true; 668 669 while(front.type != Type.EOF && front.type != Type.RParen) 670 { 671 auto name = pop(Type.Ident).value; 672 Node def = null; 673 674 if (!isVarargs || front.type == Type.Operator && front.value == Operator.Assign) 675 { 676 isVarargs = false; 677 pop(Operator.Assign); 678 def = parseHighLevelExpression(dirPath); 679 } 680 681 args ~= Arg(name, def); 682 683 if (front.type != Type.RParen) 684 pop(Type.Comma); 685 } 686 return args; 687 } 688 689 690 Node parseHighLevelExpression(string dirPath) 691 { 692 return parseInlineIf(dirPath); 693 } 694 695 696 /** 697 * inlineif = orexpr (IF orexpr (ELSE orexpr)? )? 698 */ 699 Node parseInlineIf(string dirPath) 700 { 701 Node expr; 702 Node cond = null; 703 Node other = null; 704 705 auto pos = front.pos; 706 expr = parseOrExpr( dirPath); 707 708 if (front == Keyword.If) 709 { 710 pop(Keyword.If); 711 cond = parseOrExpr( dirPath); 712 713 if (front == Keyword.Else) 714 { 715 pop(Keyword.Else); 716 other = parseOrExpr(dirPath); 717 } 718 719 return new InlineIfNode(pos, expr, cond, other); 720 } 721 722 return expr; 723 } 724 725 /** 726 * Parse Or Expression 727 * or = and (OR or)? 728 */ 729 Node parseOrExpr(string dirPath) 730 { 731 auto lhs = parseAndExpr(dirPath); 732 733 while(true) 734 { 735 if (front.type == Type.Operator && front.value == Operator.Or) 736 { 737 auto pos = front.pos; 738 pop(Operator.Or); 739 auto rhs = parseAndExpr( dirPath); 740 lhs = new BinOpNode(pos, Operator.Or, lhs, rhs); 741 } 742 else 743 return lhs; 744 } 745 } 746 747 /** 748 * Parse And Expression: 749 * and = inis (AND inis)* 750 */ 751 Node parseAndExpr(string dirPath) 752 { 753 auto lhs = parseInIsExpr( dirPath); 754 755 while(true) 756 { 757 if (front.type == Type.Operator && front.value == Operator.And) 758 { 759 auto pos = front.pos; 760 pop(Operator.And); 761 auto rhs = parseInIsExpr( dirPath); 762 lhs = new BinOpNode(pos, Operator.And, lhs, rhs); 763 } 764 else 765 return lhs; 766 } 767 } 768 769 /** 770 * Parse inis: 771 * inis = cmp ( (NOT)? (IN expr |IS callexpr) )? 772 */ 773 Node parseInIsExpr(string dirPath) 774 { 775 auto inis = parseCmpExpr( dirPath); 776 777 auto notPos = front.pos; 778 bool hasNot = false; 779 if (front == Operator.Not && (next == Operator.In || next == Operator.Is)) 780 { 781 pop(Operator.Not); 782 hasNot = true; 783 } 784 785 auto inisPos = front.pos; 786 787 if (front == Operator.In) 788 { 789 auto op = pop().value; 790 auto rhs = parseHighLevelExpression( dirPath); 791 inis = new BinOpNode(inisPos, op, inis, rhs); 792 } 793 794 if (front == Operator.Is) 795 { 796 auto op = pop().value; 797 auto rhs = parseCallExpr( dirPath); 798 inis = new BinOpNode(inisPos, op, inis, rhs); 799 } 800 801 if (hasNot) 802 inis = new UnaryOpNode(notPos, Operator.Not, inis); 803 804 return inis; 805 } 806 807 808 /** 809 * Parse compare expression: 810 * cmp = concatexpr (CMPOP concatexpr)? 811 */ 812 Node parseCmpExpr(string dirPath) 813 { 814 auto lhs = parseConcatExpr( dirPath); 815 816 if (front.type == Type.Operator && front.value.toOperator.isCmpOperator) 817 { 818 auto pos = front.pos; 819 auto op = pop(Type.Operator).value; 820 return new BinOpNode(pos, op, lhs, parseConcatExpr( dirPath)); 821 } 822 823 return lhs; 824 } 825 826 /** 827 * Parse expression: 828 * concatexpr = filterexpr (CONCAT filterexpr)* 829 */ 830 Node parseConcatExpr(string dirPath) 831 { 832 auto lhsTerm = parseFilterExpr( dirPath); 833 834 while (front == Operator.Concat) 835 { 836 auto pos = front.pos; 837 auto op = pop(Operator.Concat).value; 838 lhsTerm = new BinOpNode(pos, op, lhsTerm, parseFilterExpr( dirPath)); 839 } 840 841 return lhsTerm; 842 } 843 844 /** 845 * filterexpr = mathexpr (FILTER callexpr)* 846 */ 847 Node parseFilterExpr(string dirPath) 848 { 849 auto filterexpr = parseMathExpr( dirPath); 850 851 while (front == Operator.Filter) 852 { 853 auto pos = front.pos; 854 auto op = pop(Operator.Filter).value; 855 filterexpr = new BinOpNode(pos, op, filterexpr, parseCallExpr( dirPath)); 856 } 857 858 return filterexpr; 859 } 860 861 /** 862 * Parse math expression: 863 * mathexpr = term((PLUS|MINUS)term)* 864 */ 865 Node parseMathExpr(string dirPath) 866 { 867 auto lhsTerm = parseTerm( dirPath); 868 869 while (true) 870 { 871 if (front.type != Type.Operator) 872 return lhsTerm; 873 874 auto pos = front.pos; 875 switch (front.value) with (Operator) 876 { 877 case Plus: 878 case Minus: 879 auto op = pop.value; 880 lhsTerm = new BinOpNode(pos, op, lhsTerm, parseTerm( dirPath)); 881 break; 882 default: 883 return lhsTerm; 884 } 885 } 886 } 887 888 /** 889 * Parse term: 890 * term = unary((MUL|DIVI|DIVF|REM)unary)* 891 */ 892 Node parseTerm(string dirPath) 893 { 894 auto lhsFactor = parseUnary( dirPath); 895 896 while(true) 897 { 898 if (front.type != Type.Operator) 899 return lhsFactor; 900 901 auto pos = front.pos; 902 switch (front.value) with (Operator) 903 { 904 case DivInt: 905 case DivFloat: 906 case Mul: 907 case Rem: 908 auto op = pop.value; 909 lhsFactor = new BinOpNode(pos, op, lhsFactor, parseUnary( dirPath)); 910 break; 911 default: 912 return lhsFactor; 913 } 914 } 915 } 916 917 /** 918 * Parse unary: 919 * unary = (pow | (PLUS|MINUS|NOT)unary) 920 */ 921 Node parseUnary(string dirPath) 922 { 923 if (front.type != Type.Operator) 924 return parsePow( dirPath); 925 926 auto pos = front.pos; 927 switch (front.value) with (Operator) 928 { 929 case Plus: 930 case Minus: 931 case Not: 932 auto op = pop.value; 933 return new UnaryOpNode(pos, op, parseUnary( dirPath)); 934 default: 935 assertTemplate(0, "Unexpected operator `%s`".fmt(front.value), front.pos); 936 assert(0); 937 } 938 } 939 940 /** 941 * Parse pow: 942 * pow = factor (POW pow)? 943 */ 944 Node parsePow(string dirPath) 945 { 946 auto lhs = parseFactor(dirPath); 947 948 if (front.type == Type.Operator && front.value == Operator.Pow) 949 { 950 auto pos = front.pos; 951 auto op = pop(Operator.Pow).value; 952 return new BinOpNode(pos, op, lhs, parsePow( dirPath)); 953 } 954 955 return lhs; 956 } 957 958 959 /** 960 * Parse factor: 961 * factor = (ident|(tuple|LPAREN HighLevelExpr RPAREN)|literal) 962 */ 963 Node parseFactor(string dirPath) 964 { 965 switch (front.type) with (Type) 966 { 967 case Ident: 968 return parseIdent( dirPath); 969 970 case LParen: 971 auto pos = front.pos; 972 pop(LParen); 973 bool hasCommas; 974 auto exprList = parseSequenceOf!parseHighLevelExpression( dirPath,RParen, hasCommas); 975 pop(RParen); 976 return hasCommas ? new ListNode(pos, exprList) : exprList[0]; 977 978 default: 979 return parseLiteral( dirPath); 980 } 981 } 982 983 /** 984 * Parse ident: 985 * ident = IDENT (LPAREN ARGS RPAREN)? (DOT IDENT (LP ARGS RP)?| LSPAREN STR LRPAREN)* 986 */ 987 Node parseIdent(string dirPath) 988 { 989 string name = ""; 990 Node[] subIdents = []; 991 auto pos = front.pos; 992 993 if (next.type == Type.LParen) 994 subIdents ~= parseCallExpr( dirPath); 995 else 996 name = pop(Type.Ident).value; 997 998 while (true) 999 { 1000 switch (front.type) with (Type) 1001 { 1002 case Dot: 1003 pop(Dot); 1004 auto posStr = front.pos; 1005 if (next.type == Type.LParen) 1006 subIdents ~= parseCallExpr( dirPath); 1007 else 1008 subIdents ~= new StringNode(posStr, pop(Ident).value); 1009 break; 1010 case LSParen: 1011 pop(LSParen); 1012 subIdents ~= parseHighLevelExpression( dirPath); 1013 pop(RSParen); 1014 break; 1015 default: 1016 return new IdentNode(pos, name, subIdents); 1017 } 1018 } 1019 } 1020 1021 1022 IdentNode parseCallIdent(string dirPath) 1023 { 1024 auto pos = front.pos; 1025 return new IdentNode(pos, "", [parseCallExpr( dirPath)]); 1026 } 1027 1028 1029 DictNode parseCallExpr(string dirPath) 1030 { 1031 auto pos = front.pos; 1032 string name = pop(Type.Ident).value; 1033 Node[] varargs; 1034 Node[string] kwargs; 1035 1036 bool parsingKwargs = false; 1037 void parse(string dirPath) 1038 { 1039 if (parsingKwargs || front.type == Type.Ident && next.value == Operator.Assign) 1040 { 1041 parsingKwargs = true; 1042 auto name = pop(Type.Ident).value; 1043 pop(Operator.Assign); 1044 kwargs[name] = parseHighLevelExpression( dirPath); 1045 } 1046 else 1047 varargs ~= parseHighLevelExpression( dirPath); 1048 } 1049 1050 if (front.type == Type.LParen) 1051 { 1052 pop(Type.LParen); 1053 1054 while (front.type != Type.EOF && front.type != Type.RParen) 1055 { 1056 parse( dirPath); 1057 1058 if (front.type != Type.RParen) 1059 pop(Type.Comma); 1060 } 1061 1062 pop(Type.RParen); 1063 } 1064 1065 Node[string] callDict; 1066 callDict["name"] = new StringNode(pos, name); 1067 callDict["varargs"] = new ListNode(pos, varargs); 1068 callDict["kwargs"] = new DictNode(pos, kwargs); 1069 1070 return new DictNode(pos, callDict); 1071 } 1072 1073 /** 1074 * literal = string|number|list|tuple|dict 1075 */ 1076 Node parseLiteral(string dirPath) 1077 { 1078 auto pos = front.pos; 1079 switch (front.type) with (Type) 1080 { 1081 case Integer: return new NumNode(pos, pop.value.to!long); 1082 case Float: return new NumNode(pos, pop.value.to!double); 1083 case String: return new StringNode(pos, pop.value); 1084 case Boolean: return new BooleanNode(pos, pop.value.to!bool); 1085 case LParen: return parseTuple( dirPath); 1086 case LSParen: return parseList( dirPath); 1087 case LBrace: return parseDict( dirPath); 1088 default: 1089 assertTemplate(0, "Unexpected token while parsing expression: %s(%s)".fmt(front.type, front.value), front.pos); 1090 assert(0); 1091 } 1092 } 1093 1094 1095 Node parseTuple(string dirPath) 1096 { 1097 //Literally array right now 1098 1099 auto pos = front.pos; 1100 pop(Type.LParen); 1101 auto tuple = parseSequenceOf!parseHighLevelExpression( dirPath,Type.RParen); 1102 pop(Type.RParen); 1103 1104 return new ListNode(pos, tuple); 1105 } 1106 1107 1108 Node parseList(string dirPath) 1109 { 1110 auto pos = front.pos; 1111 pop(Type.LSParen); 1112 auto list = parseSequenceOf!parseHighLevelExpression( dirPath,Type.RSParen); 1113 pop(Type.RSParen); 1114 1115 return new ListNode(pos, list); 1116 } 1117 1118 1119 Node[] parseSequenceOf(alias parser)(string dirPath,Type stopSymbol) 1120 { 1121 bool hasCommas; 1122 return parseSequenceOf!parser(dirPath,stopSymbol, hasCommas); 1123 } 1124 1125 1126 Node[] parseSequenceOf(alias parser)(string dirPath,Type stopSymbol, ref bool hasCommas) 1127 { 1128 Node[] seq; 1129 1130 hasCommas = false; 1131 while (front.type != stopSymbol && front.type != Type.EOF) 1132 { 1133 seq ~= parser(dirPath); 1134 1135 if (front.type != stopSymbol) 1136 { 1137 pop(Type.Comma); 1138 hasCommas = true; 1139 } 1140 } 1141 1142 return seq; 1143 } 1144 1145 1146 Node parseDict(string dirPath) 1147 { 1148 Node[string] dict; 1149 auto pos = front.pos; 1150 1151 pop(Type.LBrace); 1152 1153 bool isFirst = true; 1154 while (front.type != Type.RBrace && front.type != Type.EOF) 1155 { 1156 if (!isFirst) 1157 pop(Type.Comma); 1158 1159 string key; 1160 if (front.type == Type.Ident) 1161 key = pop(Type.Ident).value; 1162 else 1163 key = pop(Type.String).value; 1164 1165 pop(Type.Colon); 1166 dict[key] = parseHighLevelExpression( dirPath); 1167 isFirst = false; 1168 } 1169 1170 if (front.type == Type.Comma) 1171 pop(Type.Comma); 1172 1173 pop(Type.RBrace); 1174 1175 return new DictNode(pos, dict); 1176 } 1177 1178 1179 void parseComment(string dirPath) 1180 { 1181 pop(Type.CmntBegin); 1182 while (front.type != Type.CmntEnd && front.type != Type.EOF) 1183 pop(); 1184 pop(Type.CmntEnd); 1185 } 1186 1187 1188 Token front() 1189 { 1190 if (_tokens.length) 1191 return _tokens[0]; 1192 return Token.EOF; 1193 } 1194 1195 Token next() 1196 { 1197 if (_tokens.length > 1) 1198 return _tokens[1]; 1199 return Token.EOF; 1200 } 1201 1202 1203 Token pop() 1204 { 1205 auto tkn = front(); 1206 if (_tokens.length) 1207 _tokens = _tokens[1 .. $]; 1208 return tkn; 1209 } 1210 1211 1212 Token pop(Type t) 1213 { 1214 if (front.type != t) 1215 assertTemplate(0, "Unexpected token %s(%s), expected: `%s`".fmt(front.type, front.value, t), front.pos); 1216 return pop(); 1217 } 1218 1219 1220 Token pop(Keyword kw) 1221 { 1222 if (front.type != Type.Keyword || front.value != kw) 1223 assertTemplate(0, "Unexpected token %s(%s), expected kw: %s".fmt(front.type, front.value, kw), front.pos); 1224 return pop(); 1225 } 1226 1227 1228 Token pop(Operator op) 1229 { 1230 if (front.type != Type.Operator || front.value != op) 1231 assertTemplate(0, "Unexpected token %s(%s), expected op: %s".fmt(front.type, front.value, op), front.pos); 1232 return pop(); 1233 } 1234 1235 1236 void pop(T...)(T args) 1237 if (args.length > 1) 1238 { 1239 foreach(arg; args) 1240 pop(arg); 1241 } 1242 1243 1244 void stashState() 1245 { 1246 ParserState old; 1247 old.tokens = _tokens; 1248 old.blocks = _blocks; 1249 _states ~= old; 1250 _tokens = []; 1251 _blocks = (BlockNode[string]).init; 1252 } 1253 1254 1255 void popState() 1256 { 1257 assertTemplate(_states.length > 0, "Unexpected empty state stack"); 1258 1259 auto state = _states.back; 1260 _states.popBack; 1261 _tokens = state.tokens; 1262 _blocks = state.blocks; 1263 } 1264 } 1265 1266 1267 private: 1268 1269 1270 string absolute(string file,string path) 1271 { 1272 //TODO 1273 // return path; 1274 import std.path : absolutePath; 1275 return (path ~ file); 1276 } 1277 1278 bool fileExist(string file,string path) 1279 { 1280 import std.path : absolutePath; 1281 version(HUNT_FM_DEBUG) logDebug("path.absolutePath : ",(path ~ file).absolutePath); 1282 return (file.exists) || ((path ~ file).absolutePath.exists); 1283 } 1284 1285 string stripOnceRight(string str) 1286 { 1287 import std.uni; 1288 import std.utf : codeLength; 1289 1290 import std.traits; 1291 alias C = Unqual!(ElementEncodingType!string); 1292 1293 bool stripped = false; 1294 foreach_reverse (i, dchar c; str) 1295 { 1296 if (!isWhite(c)) 1297 return str[0 .. i + codeLength!C(c)]; 1298 1299 if (c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029) 1300 { 1301 return str[0 .. i]; 1302 } 1303 } 1304 1305 return str[0 .. 0]; 1306 } 1307 1308 unittest 1309 { 1310 assert(stripOnceRight("\n") == "", stripOnceRight("\n")); 1311 }