| 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 |
| 410 /// Whether the template uses named or positional holes. |
| 411 /// |
| 412 /// Initially set to `null`, and updated when the first hole is encountered. |
| 413 bool hasNamedHoles; |
| 405 final List<InterpolatedNode> interpolatedValues = <InterpolatedNode>[]; | 414 final List<InterpolatedNode> interpolatedValues = <InterpolatedNode>[]; |
| 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; |
| (...skipping 242 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 (hasNamedHoles == false) { |
| 682 error('Holes must all be positional or named. $holeName'); |
| 683 } |
| 684 hasNamedHoles = true; |
| 685 return holeName; |
| 686 } else if (acceptCategory(LBRACE)) { |
| 687 // Named guarded hole. Example: 'function #{funName}() { ... }' |
| 688 String holeName = lastToken; |
| 689 if (!acceptCategory(ALPHA)) { |
| 690 error('Named hole does not contain alpha-characters.'); |
| 691 } |
| 692 if (!acceptCategory(RBRACE)) { |
| 693 error('Named hole is not correctly terminated with `}`'); |
| 694 } |
| 695 if (hasNamedHoles == false) { |
| 696 error('Holes must all be positional or named. $holeName'); |
| 697 } |
| 698 hasNamedHoles = true; |
| 699 return holeName; |
| 700 } else { |
| 701 if (hasNamedHoles == true) { |
| 702 error('Holes must all be positional or named. $holeName'); |
| 703 } |
| 704 hasNamedHoles = false; |
| 705 int position = interpolatedValues.length; |
| 706 return position; |
| 707 } |
| 708 } |
| 709 |
| 667 Expression parsePrimary() { | 710 Expression parsePrimary() { |
| 668 String last = lastToken; | 711 String last = lastToken; |
| 669 if (acceptCategory(ALPHA)) { | 712 if (acceptCategory(ALPHA)) { |
| 670 if (last == "true") { | 713 if (last == "true") { |
| 671 return new LiteralBool(true); | 714 return new LiteralBool(true); |
| 672 } else if (last == "false") { | 715 } else if (last == "false") { |
| 673 return new LiteralBool(false); | 716 return new LiteralBool(false); |
| 674 } else if (last == "null") { | 717 } else if (last == "null") { |
| 675 return new LiteralNull(); | 718 return new LiteralNull(); |
| 676 } else if (last == "function") { | 719 } else if (last == "function") { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 698 } | 741 } |
| 699 return new ArrayInitializer(values.length, values); | 742 return new ArrayInitializer(values.length, values); |
| 700 } else if (last != null && last.startsWith("/")) { | 743 } else if (last != null && last.startsWith("/")) { |
| 701 String regexp = getDelimited(lastPosition); | 744 String regexp = getDelimited(lastPosition); |
| 702 getToken(); | 745 getToken(); |
| 703 String flags = lastToken; | 746 String flags = lastToken; |
| 704 if (!acceptCategory(ALPHA)) flags = ""; | 747 if (!acceptCategory(ALPHA)) flags = ""; |
| 705 Expression expression = new RegExpLiteral(regexp + flags); | 748 Expression expression = new RegExpLiteral(regexp + flags); |
| 706 return expression; | 749 return expression; |
| 707 } else if (acceptCategory(HASH)) { | 750 } else if (acceptCategory(HASH)) { |
| 708 InterpolatedExpression expression = | 751 var name = parseHash(); |
| 709 new InterpolatedExpression(interpolatedValues.length); | 752 InterpolatedExpression expression = new InterpolatedExpression(name); |
| 710 interpolatedValues.add(expression); | 753 interpolatedValues.add(expression); |
| 711 return expression; | 754 return expression; |
| 712 } else { | 755 } else { |
| 713 error("Expected primary expression"); | 756 error("Expected primary expression"); |
| 714 return null; | 757 return null; |
| 715 } | 758 } |
| 716 } | 759 } |
| 717 | 760 |
| 718 Expression parseFunctionExpression() { | 761 Expression parseFunctionExpression() { |
| 719 String last = lastToken; | 762 String last = lastToken; |
| 720 if (acceptCategory(ALPHA)) { | 763 if (acceptCategory(ALPHA)) { |
| 721 String functionName = last; | 764 String functionName = last; |
| 722 return new NamedFunction(new VariableDeclaration(functionName), | 765 return new NamedFunction(new VariableDeclaration(functionName), |
| 723 parseFun()); | 766 parseFun()); |
| 724 } | 767 } |
| 725 return parseFun(); | 768 return parseFun(); |
| 726 } | 769 } |
| 727 | 770 |
| 728 Expression parseFun() { | 771 Expression parseFun() { |
| 729 List<Parameter> params = <Parameter>[]; | 772 List<Parameter> params = <Parameter>[]; |
| 730 | 773 |
| 731 expectCategory(LPAREN); | 774 expectCategory(LPAREN); |
| 732 if (!acceptCategory(RPAREN)) { | 775 if (!acceptCategory(RPAREN)) { |
| 733 for (;;) { | 776 for (;;) { |
| 734 if (acceptCategory(HASH)) { | 777 if (acceptCategory(HASH)) { |
| 735 InterpolatedParameter parameter = | 778 var name = parseHash(); |
| 736 new InterpolatedParameter(interpolatedValues.length); | 779 InterpolatedParameter parameter = new InterpolatedParameter(name); |
| 737 interpolatedValues.add(parameter); | 780 interpolatedValues.add(parameter); |
| 738 params.add(parameter); | 781 params.add(parameter); |
| 739 } else { | 782 } else { |
| 740 String argumentName = lastToken; | 783 String argumentName = lastToken; |
| 741 expectCategory(ALPHA); | 784 expectCategory(ALPHA); |
| 742 params.add(new Parameter(argumentName)); | 785 params.add(new Parameter(argumentName)); |
| 743 } | 786 } |
| 744 if (acceptCategory(COMMA)) continue; | 787 if (acceptCategory(COMMA)) continue; |
| 745 expectCategory(RPAREN); | 788 expectCategory(RPAREN); |
| 746 break; | 789 break; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 759 // Limited subset: keys are identifiers, no 'get' or 'set' properties. | 802 // Limited subset: keys are identifiers, no 'get' or 'set' properties. |
| 760 Literal propertyName; | 803 Literal propertyName; |
| 761 String identifier = lastToken; | 804 String identifier = lastToken; |
| 762 if (acceptCategory(ALPHA)) { | 805 if (acceptCategory(ALPHA)) { |
| 763 propertyName = new LiteralString('"$identifier"'); | 806 propertyName = new LiteralString('"$identifier"'); |
| 764 } else if (acceptCategory(STRING)) { | 807 } else if (acceptCategory(STRING)) { |
| 765 propertyName = new LiteralString(identifier); | 808 propertyName = new LiteralString(identifier); |
| 766 } else if (acceptCategory(SYMBOL)) { // e.g. void | 809 } else if (acceptCategory(SYMBOL)) { // e.g. void |
| 767 propertyName = new LiteralString('"$identifier"'); | 810 propertyName = new LiteralString('"$identifier"'); |
| 768 } else if (acceptCategory(HASH)) { | 811 } else if (acceptCategory(HASH)) { |
| 812 var name = parseHash(); |
| 769 InterpolatedLiteral interpolatedLiteral = | 813 InterpolatedLiteral interpolatedLiteral = |
| 770 new InterpolatedLiteral(interpolatedValues.length); | 814 new InterpolatedLiteral(name); |
| 771 interpolatedValues.add(interpolatedLiteral); | 815 interpolatedValues.add(interpolatedLiteral); |
| 772 propertyName = interpolatedLiteral; | 816 propertyName = interpolatedLiteral; |
| 773 } else { | 817 } else { |
| 774 error('Expected property name'); | 818 error('Expected property name'); |
| 775 } | 819 } |
| 776 expectCategory(COLON); | 820 expectCategory(COLON); |
| 777 Expression value = parseAssignment(); | 821 Expression value = parseAssignment(); |
| 778 properties.add(new Property(propertyName, value)); | 822 properties.add(new Property(propertyName, value)); |
| 779 if (acceptCategory(RBRACE)) break; | 823 if (acceptCategory(RBRACE)) break; |
| 780 expectCategory(COMMA); | 824 expectCategory(COMMA); |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 826 // JS allows new without (), but we don't. | 870 // JS allows new without (), but we don't. |
| 827 if (constructor) error("Parentheses are required for new"); | 871 if (constructor) error("Parentheses are required for new"); |
| 828 break; | 872 break; |
| 829 } | 873 } |
| 830 } | 874 } |
| 831 return receiver; | 875 return receiver; |
| 832 } | 876 } |
| 833 | 877 |
| 834 Expression getDotRhs(Expression receiver) { | 878 Expression getDotRhs(Expression receiver) { |
| 835 if (acceptCategory(HASH)) { | 879 if (acceptCategory(HASH)) { |
| 836 InterpolatedSelector property = | 880 var name = parseHash(); |
| 837 new InterpolatedSelector(interpolatedValues.length); | 881 InterpolatedSelector property = new InterpolatedSelector(name); |
| 838 interpolatedValues.add(property); | 882 interpolatedValues.add(property); |
| 839 return new PropertyAccess(receiver, property); | 883 return new PropertyAccess(receiver, property); |
| 840 } | 884 } |
| 841 String identifier = lastToken; | 885 String identifier = lastToken; |
| 842 // In ES5 keywords like delete and continue are allowed as property | 886 // 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. | 887 // names, and the IndexedDB API uses that, so we need to allow it here. |
| 844 if (acceptCategory(SYMBOL)) { | 888 if (acceptCategory(SYMBOL)) { |
| 845 if (!OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS.contains(identifier)) { | 889 if (!OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS.contains(identifier)) { |
| 846 error("Expected alphanumeric identifier"); | 890 error("Expected alphanumeric identifier"); |
| 847 } | 891 } |
| (...skipping 342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1190 Catch parseCatch() { | 1234 Catch parseCatch() { |
| 1191 expectCategory(LPAREN); | 1235 expectCategory(LPAREN); |
| 1192 String identifier = lastToken; | 1236 String identifier = lastToken; |
| 1193 expectCategory(ALPHA); | 1237 expectCategory(ALPHA); |
| 1194 expectCategory(RPAREN); | 1238 expectCategory(RPAREN); |
| 1195 expectCategory(LBRACE); | 1239 expectCategory(LBRACE); |
| 1196 Block body = parseBlock(); | 1240 Block body = parseBlock(); |
| 1197 return new Catch(new VariableDeclaration(identifier), body); | 1241 return new Catch(new VariableDeclaration(identifier), body); |
| 1198 } | 1242 } |
| 1199 } | 1243 } |
| OLD | NEW |