| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // Utilities for building JS ASTs at runtime. Contains a builder class | 5 // Utilities for building JS ASTs at runtime. Contains a builder class |
| 6 // and a parser that parses part of the language. | 6 // and a parser that parses part of the language. |
| 7 | 7 |
| 8 part of js; | 8 part of js; |
| 9 | 9 |
| 10 class JsBuilder { | 10 class JsBuilder { |
| 11 const JsBuilder(); | 11 const JsBuilder(); |
| 12 | 12 |
| 13 // Parse a bit of JavaScript, and return an expression. | 13 /** |
| 14 // See the MiniJsParser class. | 14 * Parses a bit of JavaScript, and returns an expression. |
| 15 // You can provide an expression or a list of expressions, which will be | 15 * |
| 16 // interpolated into the source at the '#' signs. | 16 * See the MiniJsParser class. |
| 17 * |
| 18 * [expression] can be an [Expression] or a list of [Expression]s, which will |
| 19 * be interpolated into the source at the '#' signs. |
| 20 */ |
| 17 Expression call(String source, [var expression]) { | 21 Expression call(String source, [var expression]) { |
| 18 var result = new MiniJsParser(source).expression(); | 22 var result = new MiniJsParser(source).expression(); |
| 19 if (expression == null) return result; | 23 if (expression == null) return result; |
| 20 | 24 |
| 21 List<Expression> expressions; | 25 List<Node> nodes; |
| 22 if (expression is List) { | 26 if (expression is List) { |
| 23 expressions = expression; | 27 nodes = expression; |
| 24 } else { | 28 } else { |
| 25 expressions = <Expression>[expression]; | 29 nodes = <Node>[expression]; |
| 26 } | 30 } |
| 27 if (expressions.length != result.interpolatedExpressions.length) { | 31 if (nodes.length != result.interpolatedNodes.length) { |
| 28 throw "Unmatched number of interpolated expressions"; | 32 throw 'Unmatched number of interpolated expressions given ${nodes.length}' |
| 33 ' expected ${result.interpolatedNodes.length}'; |
| 29 } | 34 } |
| 30 for (int i = 0; i < expressions.length; i++) { | 35 for (int i = 0; i < nodes.length; i++) { |
| 31 result.interpolatedExpressions[i].value = expressions[i]; | 36 result.interpolatedNodes[i].assign(nodes[i]); |
| 32 } | 37 } |
| 33 | 38 |
| 34 return result.value; | 39 return result.value; |
| 35 } | 40 } |
| 36 | 41 |
| 42 Statement statement(String source) { |
| 43 var result = new MiniJsParser(source).statement(); |
| 44 // TODO(sra): Interpolation. |
| 45 return result; |
| 46 } |
| 47 |
| 37 // Parse JavaScript written in the JS foreign instruction. | 48 // Parse JavaScript written in the JS foreign instruction. |
| 38 Expression parseForeignJS(String source, [var expression]) { | 49 Expression parseForeignJS(String source, [var expression]) { |
| 39 // We can parse simple JS with the mini parser. At the moment we can't | 50 // We can parse simple JS with the mini parser. At the moment we can't |
| 40 // handle JSON literals and function literals, both of which contain "{". | 51 // handle JSON literals and function literals, both of which contain "{". |
| 41 if (source.contains("{") || source.startsWith("throw ")) { | 52 if (source.contains("{") || source.startsWith("throw ")) { |
| 42 assert(expression == null); | 53 assert(expression == null); |
| 43 return new LiteralExpression(source); | 54 return new LiteralExpression(source); |
| 44 } | 55 } |
| 45 return call(source, expression); | 56 return call(source, expression); |
| 46 } | 57 } |
| (...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 254 position = 0 { | 265 position = 0 { |
| 255 getToken(); | 266 getToken(); |
| 256 } | 267 } |
| 257 | 268 |
| 258 int lastCategory = NONE; | 269 int lastCategory = NONE; |
| 259 String lastToken = null; | 270 String lastToken = null; |
| 260 int lastPosition = 0; | 271 int lastPosition = 0; |
| 261 int position = 0; | 272 int position = 0; |
| 262 bool skippedNewline = false; // skipped newline in last getToken? | 273 bool skippedNewline = false; // skipped newline in last getToken? |
| 263 final String src; | 274 final String src; |
| 264 final List<InterpolatedExpression> interpolatedValues = | 275 final List<InterpolatedNode> interpolatedValues = <InterpolatedNode>[]; |
| 265 <InterpolatedExpression>[]; | |
| 266 | 276 |
| 267 static const NONE = -1; | 277 static const NONE = -1; |
| 268 static const ALPHA = 0; | 278 static const ALPHA = 0; |
| 269 static const NUMERIC = 1; | 279 static const NUMERIC = 1; |
| 270 static const STRING = 2; | 280 static const STRING = 2; |
| 271 static const SYMBOL = 3; | 281 static const SYMBOL = 3; |
| 272 static const ASSIGNMENT = 4; | 282 static const ASSIGNMENT = 4; |
| 273 static const DOT = 5; | 283 static const DOT = 5; |
| 274 static const LPAREN = 6; | 284 static const LPAREN = 6; |
| 275 static const RPAREN = 7; | 285 static const RPAREN = 7; |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 365 } | 375 } |
| 366 | 376 |
| 367 String getDelimited(int startPosition) { | 377 String getDelimited(int startPosition) { |
| 368 position = startPosition; | 378 position = startPosition; |
| 369 int delimiter = src.codeUnitAt(startPosition); | 379 int delimiter = src.codeUnitAt(startPosition); |
| 370 int currentCode; | 380 int currentCode; |
| 371 do { | 381 do { |
| 372 position++; | 382 position++; |
| 373 if (position >= src.length) error("Unterminated literal"); | 383 if (position >= src.length) error("Unterminated literal"); |
| 374 currentCode = src.codeUnitAt(position); | 384 currentCode = src.codeUnitAt(position); |
| 385 if (currentCode == charCodes.$LF) error("Unterminated literal"); |
| 375 if (currentCode == charCodes.$BACKSLASH) { | 386 if (currentCode == charCodes.$BACKSLASH) { |
| 376 if (++position >= src.length) error("Unterminated literal"); | 387 if (++position >= src.length) error("Unterminated literal"); |
| 377 int escaped = src.codeUnitAt(position); | 388 int escaped = src.codeUnitAt(position); |
| 378 if (escaped == charCodes.$x || escaped == charCodes.$X || | 389 if (escaped == charCodes.$x || escaped == charCodes.$X || |
| 379 escaped == charCodes.$u || escaped == charCodes.$U || | 390 escaped == charCodes.$u || escaped == charCodes.$U || |
| 380 category(escaped) == NUMERIC) { | 391 category(escaped) == NUMERIC) { |
| 381 error('Numeric and hex escapes are not allowed in literals'); | 392 error('Numeric and hex escapes are not allowed in literals'); |
| 382 } | 393 } |
| 383 } | 394 } |
| 384 } while (currentCode != delimiter); | 395 } while (currentCode != delimiter); |
| 385 position++; | 396 position++; |
| 386 return src.substring(lastPosition, position); | 397 return src.substring(lastPosition, position); |
| 387 } | 398 } |
| 388 | 399 |
| 389 void getToken() { | 400 void getToken() { |
| 390 skippedNewline = false; | 401 skippedNewline = false; |
| 391 for(;;) { | 402 for (;;) { |
| 392 if (position >= src.length) break; | 403 if (position >= src.length) break; |
| 393 int code = src.codeUnitAt(position); | 404 int code = src.codeUnitAt(position); |
| 394 if (category(code) != WHITESPACE) break; | 405 // Skip '//' style comment. |
| 395 if (code == charCodes.$LF) skippedNewline = true; | 406 if (code == charCodes.$SLASH && |
| 396 ++position; | 407 position + 1 < src.length && |
| 408 src.codeUnitAt(position + 1) == charCodes.$SLASH) { |
| 409 int nextPosition = src.indexOf('\n', position); |
| 410 if (nextPosition == -1) nextPosition = src.length; |
| 411 position = nextPosition; |
| 412 } else { |
| 413 if (category(code) != WHITESPACE) break; |
| 414 if (code == charCodes.$LF) skippedNewline = true; |
| 415 ++position; |
| 416 } |
| 397 } | 417 } |
| 398 | 418 |
| 399 if (position == src.length) { | 419 if (position == src.length) { |
| 400 lastCategory = NONE; | 420 lastCategory = NONE; |
| 401 lastToken = null; | 421 lastToken = null; |
| 402 lastPosition = position; | 422 lastPosition = position; |
| 403 return; | 423 return; |
| 404 } | 424 } |
| 405 int code = src.codeUnitAt(position); | 425 int code = src.codeUnitAt(position); |
| 406 lastPosition = position; | 426 lastPosition = position; |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 481 } | 501 } |
| 482 | 502 |
| 483 void expectSemicolon() { | 503 void expectSemicolon() { |
| 484 if (acceptSemicolon()) return; | 504 if (acceptSemicolon()) return; |
| 485 error('Expected SEMICOLON'); | 505 error('Expected SEMICOLON'); |
| 486 } | 506 } |
| 487 | 507 |
| 488 bool acceptSemicolon() { | 508 bool acceptSemicolon() { |
| 489 // Accept semicolon or automatically inserted semicolon before close brace. | 509 // Accept semicolon or automatically inserted semicolon before close brace. |
| 490 // Miniparser forbids other kinds of semicolon insertion. | 510 // Miniparser forbids other kinds of semicolon insertion. |
| 511 if (RBRACE == lastCategory) return true; |
| 491 if (skippedNewline) { | 512 if (skippedNewline) { |
| 492 error('No automatic semicolon insertion at preceding newline'); | 513 error('No automatic semicolon insertion at preceding newline'); |
| 493 } | 514 } |
| 494 return acceptCategory(SEMICOLON) || RBRACE == lastCategory; | 515 return acceptCategory(SEMICOLON); |
| 495 } | 516 } |
| 496 | 517 |
| 497 bool acceptString(String string) { | 518 bool acceptString(String string) { |
| 498 if (lastToken == string) { | 519 if (lastToken == string) { |
| 499 getToken(); | 520 getToken(); |
| 500 return true; | 521 return true; |
| 501 } | 522 } |
| 502 return false; | 523 return false; |
| 503 } | 524 } |
| 504 | 525 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 522 } | 543 } |
| 523 } else if (acceptCategory(LPAREN)) { | 544 } else if (acceptCategory(LPAREN)) { |
| 524 Expression expression = parseExpression(); | 545 Expression expression = parseExpression(); |
| 525 expectCategory(RPAREN); | 546 expectCategory(RPAREN); |
| 526 return expression; | 547 return expression; |
| 527 } else if (acceptCategory(STRING)) { | 548 } else if (acceptCategory(STRING)) { |
| 528 return new LiteralString(last); | 549 return new LiteralString(last); |
| 529 } else if (acceptCategory(NUMERIC)) { | 550 } else if (acceptCategory(NUMERIC)) { |
| 530 return new LiteralNumber(last); | 551 return new LiteralNumber(last); |
| 531 } else if (acceptCategory(LBRACE)) { | 552 } else if (acceptCategory(LBRACE)) { |
| 532 expectCategory(RBRACE); | 553 return parseObjectInitializer(); |
| 533 return new ObjectInitializer([]); | |
| 534 } else if (acceptCategory(LSQUARE)) { | 554 } else if (acceptCategory(LSQUARE)) { |
| 535 var values = <ArrayElement>[]; | 555 var values = <ArrayElement>[]; |
| 536 if (!acceptCategory(RSQUARE)) { | 556 if (!acceptCategory(RSQUARE)) { |
| 537 do { | 557 do { |
| 538 values.add(new ArrayElement(values.length, parseAssignment())); | 558 values.add(new ArrayElement(values.length, parseAssignment())); |
| 539 } while (acceptCategory(COMMA)); | 559 } while (acceptCategory(COMMA)); |
| 540 expectCategory(RSQUARE); | 560 expectCategory(RSQUARE); |
| 541 } | 561 } |
| 542 return new ArrayInitializer(values.length, values); | 562 return new ArrayInitializer(values.length, values); |
| 543 } else if (last.startsWith("/")) { | 563 } else if (last.startsWith("/")) { |
| 544 String regexp = getDelimited(lastPosition); | 564 String regexp = getDelimited(lastPosition); |
| 545 getToken(); | 565 getToken(); |
| 546 String flags = lastToken; | 566 String flags = lastToken; |
| 547 if (!acceptCategory(ALPHA)) flags = ""; | 567 if (!acceptCategory(ALPHA)) flags = ""; |
| 548 Expression expression = new RegExpLiteral(regexp + flags); | 568 Expression expression = new RegExpLiteral(regexp + flags); |
| 549 return expression; | 569 return expression; |
| 550 } else if (acceptCategory(HASH)) { | 570 } else if (acceptCategory(HASH)) { |
| 551 InterpolatedExpression expression = new InterpolatedExpression(null); | 571 InterpolatedExpression expression = new InterpolatedExpression(null); |
| 552 interpolatedValues.add(expression); | 572 interpolatedValues.add(expression); |
| 553 return expression; | 573 return expression; |
| 554 } else { | 574 } else { |
| 555 error("Expected primary expression"); | 575 error("Expected primary expression"); |
| 556 return null; | 576 return null; |
| 557 } | 577 } |
| 558 } | 578 } |
| 559 | 579 |
| 560 Expression parseFunctionExpression() { | 580 Expression parseFunctionExpression() { |
| 561 String functionName = null; | |
| 562 String last = lastToken; | 581 String last = lastToken; |
| 563 if (acceptCategory(ALPHA)) functionName = last; | 582 if (acceptCategory(ALPHA)) { |
| 583 String functionName = last; |
| 584 return new NamedFunction(new VariableDeclaration(functionName), |
| 585 parseFun()); |
| 586 } |
| 587 return parseFun(); |
| 588 } |
| 589 |
| 590 Expression parseFun() { |
| 564 List<Parameter> params = <Parameter>[]; | 591 List<Parameter> params = <Parameter>[]; |
| 565 expectCategory(LPAREN); | 592 expectCategory(LPAREN); |
| 566 String argumentName = lastToken; | 593 String argumentName = lastToken; |
| 567 if (acceptCategory(ALPHA)) { | 594 if (acceptCategory(ALPHA)) { |
| 568 params.add(new Parameter(argumentName)); | 595 params.add(new Parameter(argumentName)); |
| 569 while (acceptCategory(COMMA)) { | 596 while (acceptCategory(COMMA)) { |
| 570 argumentName = lastToken; | 597 argumentName = lastToken; |
| 571 expectCategory(ALPHA); | 598 expectCategory(ALPHA); |
| 572 params.add(new Parameter(argumentName)); | 599 params.add(new Parameter(argumentName)); |
| 573 } | 600 } |
| 574 } | 601 } |
| 575 expectCategory(RPAREN); | 602 expectCategory(RPAREN); |
| 576 expectCategory(LBRACE); | 603 expectCategory(LBRACE); |
| 577 Block block = parseBlock(); | 604 Block block = parseBlock(); |
| 578 Fun fun = new Fun(params, block); | 605 return new Fun(params, block); |
| 579 if (functionName == null) { | 606 } |
| 580 return fun; | 607 |
| 581 } else { | 608 Expression parseObjectInitializer() { |
| 582 return new NamedFunction(new VariableDeclaration(functionName), fun); | 609 List<Property> properties = <Property>[]; |
| 610 for (;;) { |
| 611 if (acceptCategory(RBRACE)) break; |
| 612 // Limited subset: keys are identifiers, no 'get' or 'set' properties. |
| 613 Literal propertyName; |
| 614 String identifier = lastToken; |
| 615 if (acceptCategory(ALPHA)) { |
| 616 propertyName = new LiteralString('"$identifier"'); |
| 617 } else if (acceptCategory(STRING)) { |
| 618 propertyName = new LiteralString(identifier); |
| 619 } else { |
| 620 error('Expected property name'); |
| 621 } |
| 622 expectCategory(COLON); |
| 623 Expression value = parseAssignment(); |
| 624 properties.add(new Property(propertyName, value)); |
| 625 if (acceptCategory(RBRACE)) break; |
| 626 expectCategory(COMMA); |
| 583 } | 627 } |
| 628 return new ObjectInitializer(properties); |
| 584 } | 629 } |
| 585 | 630 |
| 586 Expression parseMember() { | 631 Expression parseMember() { |
| 587 Expression receiver = parsePrimary(); | 632 Expression receiver = parsePrimary(); |
| 588 while (true) { | 633 while (true) { |
| 589 if (acceptCategory(DOT)) { | 634 if (acceptCategory(DOT)) { |
| 590 receiver = getDotRhs(receiver); | 635 receiver = getDotRhs(receiver); |
| 591 } else if (acceptCategory(LSQUARE)) { | 636 } else if (acceptCategory(LSQUARE)) { |
| 592 Expression inBraces = parseExpression(); | 637 Expression inBraces = parseExpression(); |
| 593 expectCategory(RSQUARE); | 638 expectCategory(RSQUARE); |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 783 Expression expression = parseVarDeclarationOrExpression(); | 828 Expression expression = parseVarDeclarationOrExpression(); |
| 784 if (lastCategory != NONE || position != src.length) { | 829 if (lastCategory != NONE || position != src.length) { |
| 785 error("Unparsed junk: ${categoryToString(lastCategory)}"); | 830 error("Unparsed junk: ${categoryToString(lastCategory)}"); |
| 786 } | 831 } |
| 787 if (!interpolatedValues.isEmpty) { | 832 if (!interpolatedValues.isEmpty) { |
| 788 return new JSExpression(expression, interpolatedValues); | 833 return new JSExpression(expression, interpolatedValues); |
| 789 } | 834 } |
| 790 return expression; | 835 return expression; |
| 791 } | 836 } |
| 792 | 837 |
| 838 Statement statement() { |
| 839 Statement statement = parseStatement(); |
| 840 if (lastCategory != NONE || position != src.length) { |
| 841 error("Unparsed junk: ${categoryToString(lastCategory)}"); |
| 842 } |
| 843 // TODO(sra): interpolated capture here? |
| 844 return statement; |
| 845 } |
| 846 |
| 793 Block parseBlock() { | 847 Block parseBlock() { |
| 794 List<Statement> statements = <Statement>[]; | 848 List<Statement> statements = <Statement>[]; |
| 795 | 849 |
| 796 while (!acceptCategory(RBRACE)) { | 850 while (!acceptCategory(RBRACE)) { |
| 797 Statement statement = parseStatement(); | 851 Statement statement = parseStatement(); |
| 798 statements.add(statement); | 852 statements.add(statement); |
| 799 } | 853 } |
| 800 return new Block(statements); | 854 return new Block(statements); |
| 801 } | 855 } |
| 802 | 856 |
| 803 Statement parseStatement() { | 857 Statement parseStatement() { |
| 804 if (acceptCategory(LBRACE)) return parseBlock(); | 858 if (acceptCategory(LBRACE)) return parseBlock(); |
| 805 | 859 |
| 806 if (lastCategory == ALPHA) { | 860 if (lastCategory == ALPHA) { |
| 807 if (acceptString('return')) return parseReturn(); | 861 if (acceptString('return')) return parseReturn(); |
| 808 | 862 |
| 863 if (acceptString('throw')) return parseThrow(); |
| 864 |
| 809 if (acceptString('break')) { | 865 if (acceptString('break')) { |
| 810 return parseBreakOrContinue((label) => new Break(label)); | 866 return parseBreakOrContinue((label) => new Break(label)); |
| 811 } | 867 } |
| 812 | 868 |
| 813 if (acceptString('continue')) { | 869 if (acceptString('continue')) { |
| 814 return parseBreakOrContinue((label) => new Continue(label)); | 870 return parseBreakOrContinue((label) => new Continue(label)); |
| 815 } | 871 } |
| 816 | 872 |
| 817 if (acceptString('if')) return parseIfThenElse(); | 873 if (acceptString('if')) return parseIfThenElse(); |
| 818 | 874 |
| 819 if (acceptString('for')) return parseFor(); | 875 if (acceptString('for')) return parseFor(); |
| 820 | 876 |
| 877 if (acceptString('function')) return parseFunctionDeclaration(); |
| 878 |
| 821 if (acceptString('var')) { | 879 if (acceptString('var')) { |
| 822 Expression declarations = parseVariableDeclarationList(); | 880 Expression declarations = parseVariableDeclarationList(); |
| 823 expectSemicolon(); | 881 expectSemicolon(); |
| 824 return new ExpressionStatement(declarations); | 882 return new ExpressionStatement(declarations); |
| 825 } | 883 } |
| 884 |
| 885 if (lastToken == 'case' || |
| 886 lastToken == 'do' || |
| 887 lastToken == 'while' || |
| 888 lastToken == 'switch' || |
| 889 lastToken == 'try' || |
| 890 lastToken == 'with') { |
| 891 error('Not implemented in mini parser'); |
| 892 } |
| 893 } |
| 894 |
| 895 if (acceptCategory(HASH)) { |
| 896 InterpolatedStatement statement = new InterpolatedStatement(null); |
| 897 interpolatedValues.add(statement); |
| 898 return statement; |
| 826 } | 899 } |
| 827 | 900 |
| 828 // TODO: label: statement | 901 // TODO: label: statement |
| 829 | 902 |
| 830 Expression expression = parseExpression(); | 903 Expression expression = parseExpression(); |
| 831 expectSemicolon(); | 904 expectSemicolon(); |
| 832 return new ExpressionStatement(expression); | 905 return new ExpressionStatement(expression); |
| 833 } | 906 } |
| 834 | 907 |
| 835 Statement parseReturn() { | 908 Statement parseReturn() { |
| 836 if (acceptSemicolon()) return new Return(); | 909 if (acceptSemicolon()) return new Return(); |
| 837 Expression expression = parseExpression(); | 910 Expression expression = parseExpression(); |
| 838 expectSemicolon(); | 911 expectSemicolon(); |
| 839 return new Return(expression); | 912 return new Return(expression); |
| 840 } | 913 } |
| 841 | 914 |
| 915 Statement parseThrow() { |
| 916 if (skippedNewline) error('throw expression must be on same line'); |
| 917 Expression expression = parseExpression(); |
| 918 expectSemicolon(); |
| 919 return new Throw(expression); |
| 920 } |
| 921 |
| 842 Statement parseBreakOrContinue(constructor) { | 922 Statement parseBreakOrContinue(constructor) { |
| 843 var identifier = lastToken; | 923 var identifier = lastToken; |
| 844 if (!skippedNewline && acceptCategory(ALPHA)) { | 924 if (!skippedNewline && acceptCategory(ALPHA)) { |
| 845 expectSemicolon(); | 925 expectSemicolon(); |
| 846 return constructor(identifier); | 926 return constructor(identifier); |
| 847 } | 927 } |
| 848 expectSemicolon(); | 928 expectSemicolon(); |
| 849 return constructor(null); | 929 return constructor(null); |
| 850 } | 930 } |
| 851 | 931 |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 902 Expression declarations = finishVariableDeclarationList(identifier); | 982 Expression declarations = finishVariableDeclarationList(identifier); |
| 903 expectCategory(SEMICOLON); | 983 expectCategory(SEMICOLON); |
| 904 return finishFor(declarations); | 984 return finishFor(declarations); |
| 905 } | 985 } |
| 906 | 986 |
| 907 Expression init = parseExpression(); | 987 Expression init = parseExpression(); |
| 908 expectCategory(SEMICOLON); | 988 expectCategory(SEMICOLON); |
| 909 return finishFor(init); | 989 return finishFor(init); |
| 910 } | 990 } |
| 911 | 991 |
| 992 Statement parseFunctionDeclaration() { |
| 993 String name = lastToken; |
| 994 expectCategory(ALPHA); |
| 995 Expression fun = parseFun(); |
| 996 return new FunctionDeclaration(new VariableDeclaration(name), fun); |
| 997 } |
| 912 } | 998 } |
| 913 | 999 |
| 914 /** | 1000 /** |
| 915 * Clone a JSExpression node into an expression where all children | 1001 * Clone a JSExpression node into an expression where all children |
| 916 * have been cloned, and [InterpolatedExpression]s have been replaced | 1002 * have been cloned, and [InterpolatedExpression]s have been replaced |
| 917 * with real [Expression]. | 1003 * with real [Expression]. |
| 918 */ | 1004 */ |
| 919 class UninterpolateJSExpression extends BaseVisitor<Node> { | 1005 class UninterpolateJSExpression extends BaseVisitor<Node> { |
| 920 final List<Expression> arguments; | 1006 final List<Expression> arguments; |
| 921 int argumentIndex = 0; | 1007 int argumentIndex = 0; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 960 | 1046 |
| 961 Node visitCall(Call node) { | 1047 Node visitCall(Call node) { |
| 962 return copyPosition(node, | 1048 return copyPosition(node, |
| 963 new Call(visit(node.target), visitList(node.arguments))); | 1049 new Call(visit(node.target), visitList(node.arguments))); |
| 964 } | 1050 } |
| 965 | 1051 |
| 966 Node visitInterpolatedExpression(InterpolatedExpression expression) { | 1052 Node visitInterpolatedExpression(InterpolatedExpression expression) { |
| 967 return arguments[argumentIndex++]; | 1053 return arguments[argumentIndex++]; |
| 968 } | 1054 } |
| 969 | 1055 |
| 1056 Node visitInterpolatedStatement(InterpolatedStatement statement) { |
| 1057 return arguments[argumentIndex++]; |
| 1058 } |
| 1059 |
| 970 Node visitJSExpression(JSExpression expression) { | 1060 Node visitJSExpression(JSExpression expression) { |
| 971 assert(argumentIndex == 0); | 1061 assert(argumentIndex == 0); |
| 972 Node result = visit(expression.value); | 1062 Node result = visit(expression.value); |
| 973 if (argumentIndex != arguments.length) { | 1063 if (argumentIndex != arguments.length) { |
| 974 error("Invalid number of arguments"); | 1064 error("Invalid number of arguments"); |
| 975 } | 1065 } |
| 976 assert(result is! JSExpression); | 1066 assert(result is! JSExpression); |
| 977 return result; | 1067 return result; |
| 978 } | 1068 } |
| 979 | 1069 |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1036 } | 1126 } |
| 1037 | 1127 |
| 1038 Node visitLiteralNull(LiteralNull node) { | 1128 Node visitLiteralNull(LiteralNull node) { |
| 1039 return node; | 1129 return node; |
| 1040 } | 1130 } |
| 1041 | 1131 |
| 1042 Node visitLiteralBool(LiteralBool node) { | 1132 Node visitLiteralBool(LiteralBool node) { |
| 1043 return node; | 1133 return node; |
| 1044 } | 1134 } |
| 1045 } | 1135 } |
| OLD | NEW |