| 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 | 10 |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 201 * | 201 * |
| 202 * See the MiniJsParser class. | 202 * See the MiniJsParser class. |
| 203 * | 203 * |
| 204 * [arguments] can be a single [Node] (e.g. an [Expression] or [Statement]) or | 204 * [arguments] can be a single [Node] (e.g. an [Expression] or [Statement]) or |
| 205 * a list of [Node]s, which will be interpolated into the source at the '#' | 205 * a list of [Node]s, which will be interpolated into the source at the '#' |
| 206 * signs. | 206 * signs. |
| 207 */ | 207 */ |
| 208 Expression call(String source, [var arguments]) { | 208 Expression call(String source, [var arguments]) { |
| 209 Template template = _findExpressionTemplate(source); | 209 Template template = _findExpressionTemplate(source); |
| 210 if (arguments == null) return template.instantiate([]); | 210 if (arguments == null) return template.instantiate([]); |
| 211 return template.instantiate(arguments is List ? arguments : [arguments]); | 211 // We allow a single argument to be given directly. |
| 212 if (arguments is! List && arguments is! Map) arguments = [arguments]; |
| 213 return template.instantiate(arguments); |
| 212 } | 214 } |
| 213 | 215 |
| 214 /** | 216 /** |
| 215 * Parses a JavaScript Statement, otherwise just like [call]. | 217 * Parses a JavaScript Statement, otherwise just like [call]. |
| 216 */ | 218 */ |
| 217 Statement statement(String source, [var arguments]) { | 219 Statement statement(String source, [var arguments]) { |
| 218 Template template = _findStatementTemplate(source); | 220 Template template = _findStatementTemplate(source); |
| 219 if (arguments == null) return template.instantiate([]); | 221 if (arguments == null) return template.instantiate([]); |
| 220 return template.instantiate(arguments is List ? arguments : [arguments]); | 222 // We allow a single argument to be given directly. |
| 223 if (arguments is! List && arguments is! Map) arguments = [arguments]; |
| 224 return template.instantiate(arguments); |
| 221 } | 225 } |
| 222 | 226 |
| 223 /** | 227 /** |
| 224 * Parses JavaScript written in the `JS` foreign instruction. | 228 * Parses JavaScript written in the `JS` foreign instruction. |
| 225 * | 229 * |
| 226 * The [source] must be a JavaScript expression or a JavaScript throw | 230 * The [source] must be a JavaScript expression or a JavaScript throw |
| 227 * statement. | 231 * statement. |
| 228 */ | 232 */ |
| 229 Template parseForeignJS(String source) { | 233 Template parseForeignJS(String source) { |
| 230 // TODO(sra): Parse with extra validation to forbid `#` interpolation in | 234 // TODO(sra): Parse with extra validation to forbid `#` interpolation in |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 395 position = 0 { | 399 position = 0 { |
| 396 getToken(); | 400 getToken(); |
| 397 } | 401 } |
| 398 | 402 |
| 399 int lastCategory = NONE; | 403 int lastCategory = NONE; |
| 400 String lastToken = null; | 404 String lastToken = null; |
| 401 int lastPosition = 0; | 405 int lastPosition = 0; |
| 402 int position = 0; | 406 int position = 0; |
| 403 bool skippedNewline = false; // skipped newline in last getToken? | 407 bool skippedNewline = false; // skipped newline in last getToken? |
| 404 final String src; | 408 final String src; |
| 409 |
| 405 final List<InterpolatedNode> interpolatedValues = <InterpolatedNode>[]; | 410 final List<InterpolatedNode> interpolatedValues = <InterpolatedNode>[]; |
| 411 bool get hasNamedHoles => |
| 412 interpolatedValues.isNotEmpty && interpolatedValues.first.isNamed; |
| 413 bool get hasPositionalHoles => |
| 414 interpolatedValues.isNotEmpty && interpolatedValues.first.isPositional; |
| 406 | 415 |
| 407 static const NONE = -1; | 416 static const NONE = -1; |
| 408 static const ALPHA = 0; | 417 static const ALPHA = 0; |
| 409 static const NUMERIC = 1; | 418 static const NUMERIC = 1; |
| 410 static const STRING = 2; | 419 static const STRING = 2; |
| 411 static const SYMBOL = 3; | 420 static const SYMBOL = 3; |
| 412 static const ASSIGNMENT = 4; | 421 static const ASSIGNMENT = 4; |
| 413 static const DOT = 5; | 422 static const DOT = 5; |
| 414 static const LPAREN = 6; | 423 static const LPAREN = 6; |
| 415 static const RPAREN = 7; | 424 static const RPAREN = 7; |
| (...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 657 getToken(); | 666 getToken(); |
| 658 return true; | 667 return true; |
| 659 } | 668 } |
| 660 return false; | 669 return false; |
| 661 } | 670 } |
| 662 | 671 |
| 663 void error(message) { | 672 void error(message) { |
| 664 throw new MiniJsParserError(this, message); | 673 throw new MiniJsParserError(this, message); |
| 665 } | 674 } |
| 666 | 675 |
| 676 /// Returns either the name for the hole, or its integer position. |
| 677 parseHash() { |
| 678 String holeName = lastToken; |
| 679 if (acceptCategory(ALPHA)) { |
| 680 // Named hole. Example: 'function #funName() { ... }' |
| 681 if (hasPositionalHoles) { |
| 682 error('Holes must all be positional or named. $holeName'); |
| 683 } |
| 684 return holeName; |
| 685 } else { |
| 686 if (hasNamedHoles) { |
| 687 error('Holes must all be positional or named. $holeName'); |
| 688 } |
| 689 int position = interpolatedValues.length; |
| 690 return position; |
| 691 } |
| 692 } |
| 693 |
| 667 Expression parsePrimary() { | 694 Expression parsePrimary() { |
| 668 String last = lastToken; | 695 String last = lastToken; |
| 669 if (acceptCategory(ALPHA)) { | 696 if (acceptCategory(ALPHA)) { |
| 670 if (last == "true") { | 697 if (last == "true") { |
| 671 return new LiteralBool(true); | 698 return new LiteralBool(true); |
| 672 } else if (last == "false") { | 699 } else if (last == "false") { |
| 673 return new LiteralBool(false); | 700 return new LiteralBool(false); |
| 674 } else if (last == "null") { | 701 } else if (last == "null") { |
| 675 return new LiteralNull(); | 702 return new LiteralNull(); |
| 676 } else if (last == "function") { | 703 } else if (last == "function") { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 698 } | 725 } |
| 699 return new ArrayInitializer(values.length, values); | 726 return new ArrayInitializer(values.length, values); |
| 700 } else if (last != null && last.startsWith("/")) { | 727 } else if (last != null && last.startsWith("/")) { |
| 701 String regexp = getDelimited(lastPosition); | 728 String regexp = getDelimited(lastPosition); |
| 702 getToken(); | 729 getToken(); |
| 703 String flags = lastToken; | 730 String flags = lastToken; |
| 704 if (!acceptCategory(ALPHA)) flags = ""; | 731 if (!acceptCategory(ALPHA)) flags = ""; |
| 705 Expression expression = new RegExpLiteral(regexp + flags); | 732 Expression expression = new RegExpLiteral(regexp + flags); |
| 706 return expression; | 733 return expression; |
| 707 } else if (acceptCategory(HASH)) { | 734 } else if (acceptCategory(HASH)) { |
| 735 var nameOrPosition = parseHash(); |
| 708 InterpolatedExpression expression = | 736 InterpolatedExpression expression = |
| 709 new InterpolatedExpression(interpolatedValues.length); | 737 new InterpolatedExpression(nameOrPosition); |
| 710 interpolatedValues.add(expression); | 738 interpolatedValues.add(expression); |
| 711 return expression; | 739 return expression; |
| 712 } else { | 740 } else { |
| 713 error("Expected primary expression"); | 741 error("Expected primary expression"); |
| 714 return null; | 742 return null; |
| 715 } | 743 } |
| 716 } | 744 } |
| 717 | 745 |
| 718 Expression parseFunctionExpression() { | 746 Expression parseFunctionExpression() { |
| 719 String last = lastToken; | 747 String last = lastToken; |
| 720 if (acceptCategory(ALPHA)) { | 748 if (acceptCategory(ALPHA)) { |
| 721 String functionName = last; | 749 String functionName = last; |
| 722 return new NamedFunction(new VariableDeclaration(functionName), | 750 return new NamedFunction(new VariableDeclaration(functionName), |
| 723 parseFun()); | 751 parseFun()); |
| 724 } | 752 } |
| 725 return parseFun(); | 753 return parseFun(); |
| 726 } | 754 } |
| 727 | 755 |
| 728 Expression parseFun() { | 756 Expression parseFun() { |
| 729 List<Parameter> params = <Parameter>[]; | 757 List<Parameter> params = <Parameter>[]; |
| 730 | 758 |
| 731 expectCategory(LPAREN); | 759 expectCategory(LPAREN); |
| 732 if (!acceptCategory(RPAREN)) { | 760 if (!acceptCategory(RPAREN)) { |
| 733 for (;;) { | 761 for (;;) { |
| 734 if (acceptCategory(HASH)) { | 762 if (acceptCategory(HASH)) { |
| 763 var nameOrPosition = parseHash(); |
| 735 InterpolatedParameter parameter = | 764 InterpolatedParameter parameter = |
| 736 new InterpolatedParameter(interpolatedValues.length); | 765 new InterpolatedParameter(nameOrPosition); |
| 737 interpolatedValues.add(parameter); | 766 interpolatedValues.add(parameter); |
| 738 params.add(parameter); | 767 params.add(parameter); |
| 739 } else { | 768 } else { |
| 740 String argumentName = lastToken; | 769 String argumentName = lastToken; |
| 741 expectCategory(ALPHA); | 770 expectCategory(ALPHA); |
| 742 params.add(new Parameter(argumentName)); | 771 params.add(new Parameter(argumentName)); |
| 743 } | 772 } |
| 744 if (acceptCategory(COMMA)) continue; | 773 if (acceptCategory(COMMA)) continue; |
| 745 expectCategory(RPAREN); | 774 expectCategory(RPAREN); |
| 746 break; | 775 break; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 759 // Limited subset: keys are identifiers, no 'get' or 'set' properties. | 788 // Limited subset: keys are identifiers, no 'get' or 'set' properties. |
| 760 Literal propertyName; | 789 Literal propertyName; |
| 761 String identifier = lastToken; | 790 String identifier = lastToken; |
| 762 if (acceptCategory(ALPHA)) { | 791 if (acceptCategory(ALPHA)) { |
| 763 propertyName = new LiteralString('"$identifier"'); | 792 propertyName = new LiteralString('"$identifier"'); |
| 764 } else if (acceptCategory(STRING)) { | 793 } else if (acceptCategory(STRING)) { |
| 765 propertyName = new LiteralString(identifier); | 794 propertyName = new LiteralString(identifier); |
| 766 } else if (acceptCategory(SYMBOL)) { // e.g. void | 795 } else if (acceptCategory(SYMBOL)) { // e.g. void |
| 767 propertyName = new LiteralString('"$identifier"'); | 796 propertyName = new LiteralString('"$identifier"'); |
| 768 } else if (acceptCategory(HASH)) { | 797 } else if (acceptCategory(HASH)) { |
| 798 var nameOrPosition = parseHash(); |
| 769 InterpolatedLiteral interpolatedLiteral = | 799 InterpolatedLiteral interpolatedLiteral = |
| 770 new InterpolatedLiteral(interpolatedValues.length); | 800 new InterpolatedLiteral(nameOrPosition); |
| 771 interpolatedValues.add(interpolatedLiteral); | 801 interpolatedValues.add(interpolatedLiteral); |
| 772 propertyName = interpolatedLiteral; | 802 propertyName = interpolatedLiteral; |
| 773 } else { | 803 } else { |
| 774 error('Expected property name'); | 804 error('Expected property name'); |
| 775 } | 805 } |
| 776 expectCategory(COLON); | 806 expectCategory(COLON); |
| 777 Expression value = parseAssignment(); | 807 Expression value = parseAssignment(); |
| 778 properties.add(new Property(propertyName, value)); | 808 properties.add(new Property(propertyName, value)); |
| 779 if (acceptCategory(RBRACE)) break; | 809 if (acceptCategory(RBRACE)) break; |
| 780 expectCategory(COMMA); | 810 expectCategory(COMMA); |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 826 // JS allows new without (), but we don't. | 856 // JS allows new without (), but we don't. |
| 827 if (constructor) error("Parentheses are required for new"); | 857 if (constructor) error("Parentheses are required for new"); |
| 828 break; | 858 break; |
| 829 } | 859 } |
| 830 } | 860 } |
| 831 return receiver; | 861 return receiver; |
| 832 } | 862 } |
| 833 | 863 |
| 834 Expression getDotRhs(Expression receiver) { | 864 Expression getDotRhs(Expression receiver) { |
| 835 if (acceptCategory(HASH)) { | 865 if (acceptCategory(HASH)) { |
| 836 InterpolatedSelector property = | 866 var nameOrPosition = parseHash(); |
| 837 new InterpolatedSelector(interpolatedValues.length); | 867 InterpolatedSelector property = new InterpolatedSelector(nameOrPosition); |
| 838 interpolatedValues.add(property); | 868 interpolatedValues.add(property); |
| 839 return new PropertyAccess(receiver, property); | 869 return new PropertyAccess(receiver, property); |
| 840 } | 870 } |
| 841 String identifier = lastToken; | 871 String identifier = lastToken; |
| 842 // In ES5 keywords like delete and continue are allowed as property | 872 // In ES5 keywords like delete and continue are allowed as property |
| 843 // names, and the IndexedDB API uses that, so we need to allow it here. | 873 // names, and the IndexedDB API uses that, so we need to allow it here. |
| 844 if (acceptCategory(SYMBOL)) { | 874 if (acceptCategory(SYMBOL)) { |
| 845 if (!OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS.contains(identifier)) { | 875 if (!OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS.contains(identifier)) { |
| 846 error("Expected alphanumeric identifier"); | 876 error("Expected alphanumeric identifier"); |
| 847 } | 877 } |
| (...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1059 | 1089 |
| 1060 Expression expression = parseExpression(); | 1090 Expression expression = parseExpression(); |
| 1061 expectSemicolon(); | 1091 expectSemicolon(); |
| 1062 | 1092 |
| 1063 if (checkForInterpolatedStatement) { | 1093 if (checkForInterpolatedStatement) { |
| 1064 // 'Promote' the interpolated expression `#;` to an interpolated | 1094 // 'Promote' the interpolated expression `#;` to an interpolated |
| 1065 // statement. | 1095 // statement. |
| 1066 if (expression is InterpolatedExpression) { | 1096 if (expression is InterpolatedExpression) { |
| 1067 assert(identical(interpolatedValues.last, expression)); | 1097 assert(identical(interpolatedValues.last, expression)); |
| 1068 InterpolatedStatement statement = | 1098 InterpolatedStatement statement = |
| 1069 new InterpolatedStatement(expression.name); | 1099 new InterpolatedStatement(expression.nameOrPosition); |
| 1070 interpolatedValues[interpolatedValues.length - 1] = statement; | 1100 interpolatedValues[interpolatedValues.length - 1] = statement; |
| 1071 return statement; | 1101 return statement; |
| 1072 } | 1102 } |
| 1073 } | 1103 } |
| 1074 | 1104 |
| 1075 return new ExpressionStatement(expression); | 1105 return new ExpressionStatement(expression); |
| 1076 } | 1106 } |
| 1077 | 1107 |
| 1078 Statement parseReturn() { | 1108 Statement parseReturn() { |
| 1079 if (acceptSemicolon()) return new Return(); | 1109 if (acceptSemicolon()) return new Return(); |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1190 Catch parseCatch() { | 1220 Catch parseCatch() { |
| 1191 expectCategory(LPAREN); | 1221 expectCategory(LPAREN); |
| 1192 String identifier = lastToken; | 1222 String identifier = lastToken; |
| 1193 expectCategory(ALPHA); | 1223 expectCategory(ALPHA); |
| 1194 expectCategory(RPAREN); | 1224 expectCategory(RPAREN); |
| 1195 expectCategory(LBRACE); | 1225 expectCategory(LBRACE); |
| 1196 Block body = parseBlock(); | 1226 Block body = parseBlock(); |
| 1197 return new Catch(new VariableDeclaration(identifier), body); | 1227 return new Catch(new VariableDeclaration(identifier), body); |
| 1198 } | 1228 } |
| 1199 } | 1229 } |
| OLD | NEW |