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_ast; | 8 part of js_ast; |
9 | 9 |
10 | 10 |
(...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
288 */ | 288 */ |
289 Template expressionTemplateYielding(Node ast) { | 289 Template expressionTemplateYielding(Node ast) { |
290 return new Template.withExpressionResult(ast); | 290 return new Template.withExpressionResult(ast); |
291 } | 291 } |
292 | 292 |
293 Template statementTemplateYielding(Node ast) { | 293 Template statementTemplateYielding(Node ast) { |
294 return new Template.withStatementResult(ast); | 294 return new Template.withStatementResult(ast); |
295 } | 295 } |
296 | 296 |
297 /// Creates a literal js string from [value]. | 297 /// Creates a literal js string from [value]. |
298 LiteralString escapedString(String value) { | 298 LiteralString escapedString(String value, [String quote = '"']) { |
299 // Start by escaping the backslashes. | 299 // Start by escaping the backslashes. |
300 String escaped = value.replaceAll('\\', '\\\\'); | 300 String escaped = value.replaceAll('\\', '\\\\'); |
301 // Do not escape unicode characters and ' because they are allowed in the | 301 // Do not escape unicode characters and ' because they are allowed in the |
302 // string literal anyway. | 302 // string literal anyway. |
303 escaped = escaped.replaceAllMapped(new RegExp('\n|"|\b|\t|\v'), (match) { | 303 escaped = escaped.replaceAllMapped(new RegExp('\n|$quote|\b|\t|\v'), (m) { |
304 switch (match.group(0)) { | 304 switch (m.group(0)) { |
305 case "\n" : return r"\n"; | 305 case "\n" : return r"\n"; |
306 case "\"" : return r'\"'; | 306 // Quotes are only replaced if they conflict with the containing quote |
| 307 case '"': return r'\"'; |
| 308 case "'": return r"\'"; |
| 309 case "`": return r"\`"; |
307 case "\b" : return r"\b"; | 310 case "\b" : return r"\b"; |
308 case "\t" : return r"\t"; | 311 case "\t" : return r"\t"; |
309 case "\f" : return r"\f"; | 312 case "\f" : return r"\f"; |
310 case "\v" : return r"\v"; | 313 case "\v" : return r"\v"; |
311 } | 314 } |
312 }); | 315 }); |
313 LiteralString result = string(escaped); | 316 LiteralString result = new LiteralString('$quote$escaped$quote'); |
314 // We don't escape ' under the assumption that the string is wrapped | 317 // We don't escape quotes of a different style under the assumption that the |
315 // into ". Verify that assumption. | 318 // string is wrapped into quotes. Verify that assumption. |
316 assert(result.value.codeUnitAt(0) == '"'.codeUnitAt(0)); | 319 assert(result.value.codeUnitAt(0) == quote.codeUnitAt(0)); |
317 return result; | 320 return result; |
318 } | 321 } |
319 | 322 |
320 /// Creates a literal js string from [value]. | 323 /// Creates a literal js string from [value]. |
321 /// | 324 /// |
322 /// Note that this function only puts quotes around [value]. It does not do | 325 /// Note that this function only puts quotes around [value]. It does not do |
323 /// any escaping, so use only when you can guarantee that [value] does not | 326 /// any escaping, so use only when you can guarantee that [value] does not |
324 /// contain newlines or backslashes. For escaping the string use | 327 /// contain newlines or backslashes. For escaping the string use |
325 /// [escapedString]. | 328 /// [escapedString]. |
326 LiteralString string(String value) => new LiteralString('"$value"'); | 329 LiteralString string(String value, [String quote = '"']) => |
| 330 new LiteralString('$quote$value$quote'); |
327 | 331 |
328 LiteralNumber number(num value) => new LiteralNumber('$value'); | 332 LiteralNumber number(num value) => new LiteralNumber('$value'); |
329 | 333 |
330 LiteralBool boolean(bool value) => new LiteralBool(value); | 334 LiteralBool boolean(bool value) => new LiteralBool(value); |
331 | 335 |
332 ArrayInitializer numArray(Iterable<int> list) => | 336 ArrayInitializer numArray(Iterable<int> list) => |
333 new ArrayInitializer(list.map(number).toList()); | 337 new ArrayInitializer(list.map(number).toList()); |
334 | 338 |
335 ArrayInitializer stringArray(Iterable<String> list) => | 339 ArrayInitializer stringArray(Iterable<String> list) => |
336 new ArrayInitializer(list.map(string).toList()); | 340 new ArrayInitializer(list.map(string).toList()); |
337 | 341 |
338 Comment comment(String text) => new Comment(text); | 342 Comment comment(String text) => new Comment(text); |
| 343 CommentExpression commentExpression(String text, Expression expression) => |
| 344 new CommentExpression(text, expression); |
339 | 345 |
340 Call propertyCall(Expression receiver, | 346 Call propertyCall(Expression receiver, |
341 String fieldName, | 347 String fieldName, |
342 List<Expression> arguments) { | 348 List<Expression> arguments) { |
343 return new Call(new PropertyAccess.field(receiver, fieldName), arguments); | 349 return new Call(new PropertyAccess.field(receiver, fieldName), arguments); |
344 } | 350 } |
345 } | 351 } |
346 | 352 |
347 LiteralString string(String value) => js.string(value); | 353 LiteralString string(String value) => js.string(value); |
348 LiteralNumber number(num value) => js.number(value); | 354 LiteralNumber number(num value) => js.number(value); |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
436 static const LPAREN = 6; | 442 static const LPAREN = 6; |
437 static const RPAREN = 7; | 443 static const RPAREN = 7; |
438 static const LBRACE = 8; | 444 static const LBRACE = 8; |
439 static const RBRACE = 9; | 445 static const RBRACE = 9; |
440 static const LSQUARE = 10; | 446 static const LSQUARE = 10; |
441 static const RSQUARE = 11; | 447 static const RSQUARE = 11; |
442 static const COMMA = 12; | 448 static const COMMA = 12; |
443 static const QUERY = 13; | 449 static const QUERY = 13; |
444 static const COLON = 14; | 450 static const COLON = 14; |
445 static const SEMICOLON = 15; | 451 static const SEMICOLON = 15; |
446 static const HASH = 16; | 452 static const ARROW = 16; |
447 static const WHITESPACE = 17; | 453 static const HASH = 17; |
448 static const OTHER = 18; | 454 static const WHITESPACE = 18; |
| 455 static const OTHER = 19; |
449 | 456 |
450 // Make sure that ]] is two symbols. | 457 // Make sure that ]] is two symbols. |
451 bool singleCharCategory(int category) => category >= DOT; | 458 bool singleCharCategory(int category) => category >= DOT; |
452 | 459 |
453 static String categoryToString(int cat) { | 460 static String categoryToString(int cat) { |
454 switch (cat) { | 461 switch (cat) { |
455 case NONE: return "NONE"; | 462 case NONE: return "NONE"; |
456 case ALPHA: return "ALPHA"; | 463 case ALPHA: return "ALPHA"; |
457 case NUMERIC: return "NUMERIC"; | 464 case NUMERIC: return "NUMERIC"; |
458 case SYMBOL: return "SYMBOL"; | 465 case SYMBOL: return "SYMBOL"; |
459 case ASSIGNMENT: return "ASSIGNMENT"; | 466 case ASSIGNMENT: return "ASSIGNMENT"; |
460 case DOT: return "DOT"; | 467 case DOT: return "DOT"; |
461 case LPAREN: return "LPAREN"; | 468 case LPAREN: return "LPAREN"; |
462 case RPAREN: return "RPAREN"; | 469 case RPAREN: return "RPAREN"; |
463 case LBRACE: return "LBRACE"; | 470 case LBRACE: return "LBRACE"; |
464 case RBRACE: return "RBRACE"; | 471 case RBRACE: return "RBRACE"; |
465 case LSQUARE: return "LSQUARE"; | 472 case LSQUARE: return "LSQUARE"; |
466 case RSQUARE: return "RSQUARE"; | 473 case RSQUARE: return "RSQUARE"; |
467 case STRING: return "STRING"; | 474 case STRING: return "STRING"; |
468 case COMMA: return "COMMA"; | 475 case COMMA: return "COMMA"; |
469 case QUERY: return "QUERY"; | 476 case QUERY: return "QUERY"; |
470 case COLON: return "COLON"; | 477 case COLON: return "COLON"; |
471 case SEMICOLON: return "SEMICOLON"; | 478 case SEMICOLON: return "SEMICOLON"; |
| 479 case ARROW: return "ARROW"; |
472 case HASH: return "HASH"; | 480 case HASH: return "HASH"; |
473 case WHITESPACE: return "WHITESPACE"; | 481 case WHITESPACE: return "WHITESPACE"; |
474 case OTHER: return "OTHER"; | 482 case OTHER: return "OTHER"; |
475 } | 483 } |
476 return "Unknown: $cat"; | 484 return "Unknown: $cat"; |
477 } | 485 } |
478 | 486 |
479 static const CATEGORIES = const <int>[ | 487 static const CATEGORIES = const <int>[ |
480 OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 0-7 | 488 OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 0-7 |
481 OTHER, WHITESPACE, WHITESPACE, OTHER, OTHER, WHITESPACE, // 8-13 | 489 OTHER, WHITESPACE, WHITESPACE, OTHER, OTHER, WHITESPACE, // 8-13 |
(...skipping 30 matching lines...) Expand all Loading... |
512 '!=': 9, '==': 9, '!==': 9, '===': 9, | 520 '!=': 9, '==': 9, '!==': 9, '===': 9, |
513 '<': 8, '<=': 8, '>=': 8, '>': 8, 'in': 8, 'instanceof': 8, | 521 '<': 8, '<=': 8, '>=': 8, '>': 8, 'in': 8, 'instanceof': 8, |
514 '<<': 7, '>>': 7, '>>>': 7, | 522 '<<': 7, '>>': 7, '>>>': 7, |
515 '+': 6, '-': 6, | 523 '+': 6, '-': 6, |
516 '*': 5, '/': 5, '%': 5 | 524 '*': 5, '/': 5, '%': 5 |
517 }; | 525 }; |
518 static final UNARY_OPERATORS = | 526 static final UNARY_OPERATORS = |
519 ['++', '--', '+', '-', '~', '!', 'typeof', 'void', 'delete', 'await'] | 527 ['++', '--', '+', '-', '~', '!', 'typeof', 'void', 'delete', 'await'] |
520 .toSet(); | 528 .toSet(); |
521 | 529 |
| 530 static final ARROW_TOKEN = '=>'; |
| 531 |
522 static final OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS = | 532 static final OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS = |
523 ['typeof', 'void', 'delete', 'in', 'instanceof', 'await'].toSet(); | 533 ['typeof', 'void', 'delete', 'in', 'instanceof', 'await'].toSet(); |
524 | 534 |
525 static int category(int code) { | 535 static int category(int code) { |
526 if (code >= CATEGORIES.length) return OTHER; | 536 if (code >= CATEGORIES.length) return OTHER; |
527 return CATEGORIES[code]; | 537 return CATEGORIES[code]; |
528 } | 538 } |
529 | 539 |
530 String getDelimited(int startPosition) { | 540 String getDelimited(int startPosition) { |
531 position = startPosition; | 541 position = startPosition; |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
626 (cat == newCat || | 636 (cat == newCat || |
627 (cat == ALPHA && newCat == NUMERIC) || // eg. level42. | 637 (cat == ALPHA && newCat == NUMERIC) || // eg. level42. |
628 (cat == NUMERIC && newCat == DOT))); // eg. 3.1415 | 638 (cat == NUMERIC && newCat == DOT))); // eg. 3.1415 |
629 lastCategory = cat; | 639 lastCategory = cat; |
630 lastToken = src.substring(lastPosition, position); | 640 lastToken = src.substring(lastPosition, position); |
631 if (cat == NUMERIC) { | 641 if (cat == NUMERIC) { |
632 double.parse(lastToken, (_) { | 642 double.parse(lastToken, (_) { |
633 error("Unparseable number"); | 643 error("Unparseable number"); |
634 }); | 644 }); |
635 } else if (cat == SYMBOL) { | 645 } else if (cat == SYMBOL) { |
636 int binaryPrecendence = BINARY_PRECEDENCE[lastToken]; | 646 if (lastToken == ARROW_TOKEN) { |
637 if (binaryPrecendence == null && !UNARY_OPERATORS.contains(lastToken)) { | 647 lastCategory = ARROW; |
638 error("Unknown operator"); | 648 } else { |
| 649 int binaryPrecendence = BINARY_PRECEDENCE[lastToken]; |
| 650 if (binaryPrecendence == null && !UNARY_OPERATORS.contains(lastToken))
{ |
| 651 error("Unknown operator"); |
| 652 } |
| 653 if (isAssignment(lastToken)) lastCategory = ASSIGNMENT; |
639 } | 654 } |
640 if (isAssignment(lastToken)) lastCategory = ASSIGNMENT; | |
641 } else if (cat == ALPHA) { | 655 } else if (cat == ALPHA) { |
642 if (OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS.contains(lastToken)) { | 656 if (OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS.contains(lastToken)) { |
643 lastCategory = SYMBOL; | 657 lastCategory = SYMBOL; |
644 } | 658 } |
645 } | 659 } |
646 } | 660 } |
647 } | 661 } |
648 | 662 |
649 void expectCategory(int cat) { | 663 void expectCategory(int cat) { |
650 if (cat != lastCategory) error("Expected ${categoryToString(cat)}"); | 664 if (cat != lastCategory) error("Expected ${categoryToString(cat)}"); |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
711 if (last == "true") { | 725 if (last == "true") { |
712 return new LiteralBool(true); | 726 return new LiteralBool(true); |
713 } else if (last == "false") { | 727 } else if (last == "false") { |
714 return new LiteralBool(false); | 728 return new LiteralBool(false); |
715 } else if (last == "null") { | 729 } else if (last == "null") { |
716 return new LiteralNull(); | 730 return new LiteralNull(); |
717 } else if (last == "function") { | 731 } else if (last == "function") { |
718 return parseFunctionExpression(); | 732 return parseFunctionExpression(); |
719 } else if (last == "this") { | 733 } else if (last == "this") { |
720 return new This(); | 734 return new This(); |
| 735 } else if (last == "super") { |
| 736 return new Super(); |
| 737 } else if (last == "class") { |
| 738 return parseClass(); |
721 } else { | 739 } else { |
722 return new VariableUse(last); | 740 return new VariableUse(last); |
723 } | 741 } |
724 } else if (acceptCategory(LPAREN)) { | 742 } else if (acceptCategory(LPAREN)) { |
725 Expression expression = parseExpression(); | 743 return parseExpressionOrArrowFunction(); |
726 expectCategory(RPAREN); | |
727 return expression; | |
728 } else if (acceptCategory(STRING)) { | 744 } else if (acceptCategory(STRING)) { |
729 return new LiteralString(last); | 745 return new LiteralString(last); |
730 } else if (acceptCategory(NUMERIC)) { | 746 } else if (acceptCategory(NUMERIC)) { |
731 return new LiteralNumber(last); | 747 return new LiteralNumber(last); |
732 } else if (acceptCategory(LBRACE)) { | 748 } else if (acceptCategory(LBRACE)) { |
733 return parseObjectInitializer(); | 749 return parseObjectInitializer(); |
734 } else if (acceptCategory(LSQUARE)) { | 750 } else if (acceptCategory(LSQUARE)) { |
735 var values = <Expression>[]; | 751 var values = <Expression>[]; |
736 | 752 |
737 while (true) { | 753 while (true) { |
(...skipping 19 matching lines...) Expand all Loading... |
757 InterpolatedExpression expression = | 773 InterpolatedExpression expression = |
758 new InterpolatedExpression(nameOrPosition); | 774 new InterpolatedExpression(nameOrPosition); |
759 interpolatedValues.add(expression); | 775 interpolatedValues.add(expression); |
760 return expression; | 776 return expression; |
761 } else { | 777 } else { |
762 error("Expected primary expression"); | 778 error("Expected primary expression"); |
763 return null; | 779 return null; |
764 } | 780 } |
765 } | 781 } |
766 | 782 |
| 783 /** |
| 784 * CoverParenthesizedExpressionAndArrowParameterList[Yield] : |
| 785 * ( Expression ) |
| 786 * ( ) |
| 787 * ( ... BindingIdentifier ) |
| 788 * ( Expression , ... BindingIdentifier ) |
| 789 */ |
| 790 Expression parseExpressionOrArrowFunction() { |
| 791 if (acceptCategory(RPAREN)) { |
| 792 expectCategory(ARROW); |
| 793 return parseArrowFunctionBody(<Parameter>[]); |
| 794 } |
| 795 Expression expression = parseExpression(); |
| 796 expectCategory(RPAREN); |
| 797 if (acceptCategory(ARROW)) { |
| 798 var params = <Parameter>[]; |
| 799 _expressionToParameterList(expression, params); |
| 800 return parseArrowFunctionBody(params); |
| 801 } |
| 802 return expression; |
| 803 |
| 804 } |
| 805 |
| 806 /** |
| 807 * Converts a parenthesized expression into a list of parameters, issuing an |
| 808 * error if the conversion fails. |
| 809 */ |
| 810 void _expressionToParameterList(Expression node, List<Parameter> params) { |
| 811 if (node is VariableUse) { |
| 812 // TODO(jmesserly): support default/rest parameters |
| 813 params.add(new Parameter(node.name)); |
| 814 } else if (node is Binary && node.op == ',') { |
| 815 // TODO(jmesserly): this will allow illegal parens, such as |
| 816 // `((a, b), (c, d))`. Fixing it on the left side needs an explicit |
| 817 // ParenthesizedExpression node, so we can distinguish |
| 818 // `((a, b), c)` from `(a, b, c)`. |
| 819 _expressionToParameterList(node.left, params); |
| 820 _expressionToParameterList(node.right, params); |
| 821 } else if (node is InterpolatedExpression) { |
| 822 params.add(new InterpolatedParameter(node.nameOrPosition)); |
| 823 } else { |
| 824 error("Expected arrow function parameter list"); |
| 825 } |
| 826 } |
| 827 |
| 828 Expression parseArrowFunctionBody(List<Parameter> params) { |
| 829 Node body; |
| 830 if (acceptCategory(LBRACE)) { |
| 831 body = parseBlock(); |
| 832 } else { |
| 833 body = parseAssignment(); |
| 834 } |
| 835 return new ArrowFun(params, body); |
| 836 } |
| 837 |
767 Expression parseFunctionExpression() { | 838 Expression parseFunctionExpression() { |
768 String last = lastToken; | 839 String last = lastToken; |
769 if (acceptCategory(ALPHA)) { | 840 if (acceptCategory(ALPHA)) { |
770 String functionName = last; | 841 String functionName = last; |
771 return new NamedFunction(new VariableDeclaration(functionName), | 842 return new NamedFunction(new VariableDeclaration(functionName), |
772 parseFun()); | 843 parseFun()); |
773 } | 844 } |
774 return parseFun(); | 845 return parseFun(); |
775 } | 846 } |
776 | 847 |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
811 } | 882 } |
812 expectCategory(LBRACE); | 883 expectCategory(LBRACE); |
813 Block block = parseBlock(); | 884 Block block = parseBlock(); |
814 return new Fun(params, block, asyncModifier: asyncModifier); | 885 return new Fun(params, block, asyncModifier: asyncModifier); |
815 } | 886 } |
816 | 887 |
817 Expression parseObjectInitializer() { | 888 Expression parseObjectInitializer() { |
818 List<Property> properties = <Property>[]; | 889 List<Property> properties = <Property>[]; |
819 for (;;) { | 890 for (;;) { |
820 if (acceptCategory(RBRACE)) break; | 891 if (acceptCategory(RBRACE)) break; |
821 // Limited subset: keys are identifiers, no 'get' or 'set' properties. | 892 // Limited subset of ES6 object initializers. |
822 Literal propertyName; | 893 // |
823 String identifier = lastToken; | 894 // PropertyDefinition : |
824 if (acceptCategory(ALPHA)) { | 895 // PropertyName : AssignmentExpression |
825 propertyName = new LiteralString('"$identifier"'); | 896 // MethodDefinition |
826 } else if (acceptCategory(STRING)) { | 897 |
827 propertyName = new LiteralString(identifier); | 898 if (acceptCategory(HASH)) { |
828 } else if (acceptCategory(SYMBOL)) { // e.g. void | 899 properties.add(parseInterpolatedMember()); |
829 propertyName = new LiteralString('"$identifier"'); | |
830 } else if (acceptCategory(HASH)) { | |
831 var nameOrPosition = parseHash(); | |
832 InterpolatedLiteral interpolatedLiteral = | |
833 new InterpolatedLiteral(nameOrPosition); | |
834 interpolatedValues.add(interpolatedLiteral); | |
835 propertyName = interpolatedLiteral; | |
836 } else { | 900 } else { |
837 error('Expected property name'); | 901 bool isGetter = acceptString('get'); |
| 902 bool isSetter = isGetter ? false : acceptString('set'); |
| 903 Expression name = parsePropertyName(); |
| 904 |
| 905 if (lastCategory == LPAREN) { |
| 906 Fun fun = parseFun(); |
| 907 properties.add( |
| 908 new Method(name, fun, isGetter: isGetter, isSetter: isSetter)); |
| 909 } else { |
| 910 expectCategory(COLON); |
| 911 Expression value = parseAssignment(); |
| 912 properties.add(new Property(name, value)); |
| 913 } |
| 914 if (acceptCategory(RBRACE)) break; |
| 915 expectCategory(COMMA); |
838 } | 916 } |
839 expectCategory(COLON); | |
840 Expression value = parseAssignment(); | |
841 properties.add(new Property(propertyName, value)); | |
842 if (acceptCategory(RBRACE)) break; | |
843 expectCategory(COMMA); | |
844 } | 917 } |
845 return new ObjectInitializer(properties); | 918 return new ObjectInitializer(properties); |
846 } | 919 } |
847 | 920 |
848 Expression parseMember() { | 921 Expression parseMember() { |
849 Expression receiver = parsePrimary(); | 922 Expression receiver = parsePrimary(); |
850 while (true) { | 923 while (true) { |
851 if (acceptCategory(DOT)) { | 924 if (acceptCategory(DOT)) { |
852 receiver = getDotRhs(receiver); | 925 receiver = getDotRhs(receiver); |
853 } else if (acceptCategory(LSQUARE)) { | 926 } else if (acceptCategory(LSQUARE)) { |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
980 | 1053 |
981 Expression parseConditional() { | 1054 Expression parseConditional() { |
982 Expression lhs = parseBinary(HIGHEST_PARSE_BINARY_PRECEDENCE); | 1055 Expression lhs = parseBinary(HIGHEST_PARSE_BINARY_PRECEDENCE); |
983 if (!acceptCategory(QUERY)) return lhs; | 1056 if (!acceptCategory(QUERY)) return lhs; |
984 Expression ifTrue = parseAssignment(); | 1057 Expression ifTrue = parseAssignment(); |
985 expectCategory(COLON); | 1058 expectCategory(COLON); |
986 Expression ifFalse = parseAssignment(); | 1059 Expression ifFalse = parseAssignment(); |
987 return new Conditional(lhs, ifTrue, ifFalse); | 1060 return new Conditional(lhs, ifTrue, ifFalse); |
988 } | 1061 } |
989 | 1062 |
| 1063 Expression parseLeftHandSide() => parseConditional(); |
990 | 1064 |
991 Expression parseAssignment() { | 1065 Expression parseAssignment() { |
992 Expression lhs = parseConditional(); | 1066 Expression lhs = parseLeftHandSide(); |
993 String assignmentOperator = lastToken; | 1067 String assignmentOperator = lastToken; |
994 if (acceptCategory(ASSIGNMENT)) { | 1068 if (acceptCategory(ASSIGNMENT)) { |
995 Expression rhs = parseAssignment(); | 1069 Expression rhs = parseAssignment(); |
996 if (assignmentOperator == "=") { | 1070 if (assignmentOperator == "=") { |
997 return new Assignment(lhs, rhs); | 1071 return new Assignment(lhs, rhs); |
998 } else { | 1072 } else { |
999 // Handle +=, -=, etc. | 1073 // Handle +=, -=, etc. |
1000 String operator = | 1074 String operator = |
1001 assignmentOperator.substring(0, assignmentOperator.length - 1); | 1075 assignmentOperator.substring(0, assignmentOperator.length - 1); |
1002 return new Assignment.compound(lhs, operator, rhs); | 1076 return new Assignment.compound(lhs, operator, rhs); |
1003 } | 1077 } |
1004 } | 1078 } |
1005 return lhs; | 1079 return lhs; |
1006 } | 1080 } |
1007 | 1081 |
1008 Expression parseExpression() { | 1082 Expression parseExpression() { |
1009 Expression expression = parseAssignment(); | 1083 Expression expression = parseAssignment(); |
1010 while (acceptCategory(COMMA)) { | 1084 while (acceptCategory(COMMA)) { |
1011 Expression right = parseAssignment(); | 1085 Expression right = parseAssignment(); |
1012 expression = new Binary(',', expression, right); | 1086 expression = new Binary(',', expression, right); |
1013 } | 1087 } |
1014 return expression; | 1088 return expression; |
1015 } | 1089 } |
1016 | 1090 |
1017 VariableDeclarationList parseVariableDeclarationList() { | 1091 /** Parse a variable declaration list, with `var` or `let` [keyword] */ |
| 1092 VariableDeclarationList parseVariableDeclarationList(String keyword) { |
| 1093 // Supports one form for interpolated variable declaration: |
| 1094 // let # = ... |
| 1095 if (acceptCategory(HASH)) { |
| 1096 var name = new InterpolatedVariableDeclaration(parseHash()); |
| 1097 interpolatedValues.add(name); |
| 1098 |
| 1099 Expression initializer = acceptString("=") ? parseAssignment() : null; |
| 1100 return new VariableDeclarationList(keyword, |
| 1101 [new VariableInitialization(name, initializer)]); |
| 1102 } |
| 1103 |
1018 String firstVariable = lastToken; | 1104 String firstVariable = lastToken; |
1019 expectCategory(ALPHA); | 1105 expectCategory(ALPHA); |
1020 return finishVariableDeclarationList(firstVariable); | 1106 return finishVariableDeclarationList(keyword, firstVariable); |
1021 } | 1107 } |
1022 | 1108 |
1023 VariableDeclarationList finishVariableDeclarationList(String firstVariable) { | 1109 VariableDeclarationList finishVariableDeclarationList( |
| 1110 String keyword, String firstVariable) { |
1024 var initialization = []; | 1111 var initialization = []; |
1025 | 1112 |
1026 void declare(String variable) { | 1113 void declare(String variable) { |
1027 Expression initializer = null; | 1114 Expression initializer = null; |
1028 if (acceptString("=")) { | 1115 if (acceptString("=")) { |
1029 initializer = parseAssignment(); | 1116 initializer = parseAssignment(); |
1030 } | 1117 } |
1031 var declaration = new VariableDeclaration(variable); | 1118 var declaration = new VariableDeclaration(variable); |
1032 initialization.add(new VariableInitialization(declaration, initializer)); | 1119 initialization.add(new VariableInitialization(declaration, initializer)); |
1033 } | 1120 } |
1034 | 1121 |
1035 declare(firstVariable); | 1122 declare(firstVariable); |
1036 while (acceptCategory(COMMA)) { | 1123 while (acceptCategory(COMMA)) { |
1037 String variable = lastToken; | 1124 String variable = lastToken; |
1038 expectCategory(ALPHA); | 1125 expectCategory(ALPHA); |
1039 declare(variable); | 1126 declare(variable); |
1040 } | 1127 } |
1041 return new VariableDeclarationList(initialization); | 1128 return new VariableDeclarationList(keyword, initialization); |
1042 } | 1129 } |
1043 | 1130 |
1044 Expression parseVarDeclarationOrExpression() { | 1131 Expression parseVarDeclarationOrExpression() { |
1045 if (acceptString("var")) { | 1132 var keyword = acceptVarOrLet(); |
1046 return parseVariableDeclarationList(); | 1133 if (keyword != null) { |
| 1134 return parseVariableDeclarationList(keyword); |
1047 } else { | 1135 } else { |
1048 return parseExpression(); | 1136 return parseExpression(); |
1049 } | 1137 } |
1050 } | 1138 } |
1051 | 1139 |
| 1140 /** Accepts a `var` or `let` keyword. If neither is found, returns null. */ |
| 1141 String acceptVarOrLet() { |
| 1142 if (acceptString('var')) return 'var'; |
| 1143 if (acceptString('let')) return 'let'; |
| 1144 return null; |
| 1145 } |
| 1146 |
1052 Expression expression() { | 1147 Expression expression() { |
1053 Expression expression = parseVarDeclarationOrExpression(); | 1148 Expression expression = parseVarDeclarationOrExpression(); |
1054 if (lastCategory != NONE || position != src.length) { | 1149 if (lastCategory != NONE || position != src.length) { |
1055 error("Unparsed junk: ${categoryToString(lastCategory)}"); | 1150 error("Unparsed junk: ${categoryToString(lastCategory)}"); |
1056 } | 1151 } |
1057 return expression; | 1152 return expression; |
1058 } | 1153 } |
1059 | 1154 |
1060 Statement statement() { | 1155 Statement statement() { |
1061 Statement statement = parseStatement(); | 1156 Statement statement = parseStatement(); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1093 if (acceptString('continue')) { | 1188 if (acceptString('continue')) { |
1094 return parseBreakOrContinue((label) => new Continue(label)); | 1189 return parseBreakOrContinue((label) => new Continue(label)); |
1095 } | 1190 } |
1096 | 1191 |
1097 if (acceptString('if')) return parseIfThenElse(); | 1192 if (acceptString('if')) return parseIfThenElse(); |
1098 | 1193 |
1099 if (acceptString('for')) return parseFor(); | 1194 if (acceptString('for')) return parseFor(); |
1100 | 1195 |
1101 if (acceptString('function')) return parseFunctionDeclaration(); | 1196 if (acceptString('function')) return parseFunctionDeclaration(); |
1102 | 1197 |
| 1198 if (acceptString('class')) return new ClassDeclaration(parseClass()); |
| 1199 |
1103 if (acceptString('try')) return parseTry(); | 1200 if (acceptString('try')) return parseTry(); |
1104 | 1201 |
1105 if (acceptString('var')) { | 1202 var keyword = acceptVarOrLet(); |
1106 Expression declarations = parseVariableDeclarationList(); | 1203 if (keyword != null) { |
| 1204 Expression declarations = parseVariableDeclarationList(keyword); |
1107 expectSemicolon(); | 1205 expectSemicolon(); |
1108 return new ExpressionStatement(declarations); | 1206 return new ExpressionStatement(declarations); |
1109 } | 1207 } |
1110 | 1208 |
1111 if (acceptString('while')) return parseWhile(); | 1209 if (acceptString('while')) return parseWhile(); |
1112 | 1210 |
1113 if (acceptString('do')) return parseDo(); | 1211 if (acceptString('do')) return parseDo(); |
1114 | 1212 |
1115 if (acceptString('switch')) return parseSwitch(); | 1213 if (acceptString('switch')) return parseSwitch(); |
1116 | 1214 |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1196 } | 1294 } |
1197 } | 1295 } |
1198 | 1296 |
1199 Statement parseFor() { | 1297 Statement parseFor() { |
1200 // For-init-condition-increment style loops are fully supported. | 1298 // For-init-condition-increment style loops are fully supported. |
1201 // | 1299 // |
1202 // Only one for-in variant is currently implemented: | 1300 // Only one for-in variant is currently implemented: |
1203 // | 1301 // |
1204 // for (var variable in Expression) Statement | 1302 // for (var variable in Expression) Statement |
1205 // | 1303 // |
| 1304 // One variant of ES6 for-of is also implemented: |
| 1305 // |
| 1306 // for (let variable of Expression) Statement |
| 1307 // |
1206 Statement finishFor(Expression init) { | 1308 Statement finishFor(Expression init) { |
1207 Expression condition = null; | 1309 Expression condition = null; |
1208 if (!acceptCategory(SEMICOLON)) { | 1310 if (!acceptCategory(SEMICOLON)) { |
1209 condition = parseExpression(); | 1311 condition = parseExpression(); |
1210 expectCategory(SEMICOLON); | 1312 expectCategory(SEMICOLON); |
1211 } | 1313 } |
1212 Expression update = null; | 1314 Expression update = null; |
1213 if (!acceptCategory(RPAREN)) { | 1315 if (!acceptCategory(RPAREN)) { |
1214 update = parseExpression(); | 1316 update = parseExpression(); |
1215 expectCategory(RPAREN); | 1317 expectCategory(RPAREN); |
1216 } | 1318 } |
1217 Statement body = parseStatement(); | 1319 Statement body = parseStatement(); |
1218 return new For(init, condition, update, body); | 1320 return new For(init, condition, update, body); |
1219 } | 1321 } |
1220 | 1322 |
1221 expectCategory(LPAREN); | 1323 expectCategory(LPAREN); |
1222 if (acceptCategory(SEMICOLON)) { | 1324 if (acceptCategory(SEMICOLON)) { |
1223 return finishFor(null); | 1325 return finishFor(null); |
1224 } | 1326 } |
1225 | 1327 |
1226 if (acceptString('var')) { | 1328 var keyword = acceptVarOrLet(); |
| 1329 if (keyword != null) { |
1227 String identifier = lastToken; | 1330 String identifier = lastToken; |
1228 expectCategory(ALPHA); | 1331 expectCategory(ALPHA); |
| 1332 |
1229 if (acceptString('in')) { | 1333 if (acceptString('in')) { |
1230 Expression objectExpression = parseExpression(); | 1334 Expression objectExpression = parseExpression(); |
1231 expectCategory(RPAREN); | 1335 expectCategory(RPAREN); |
1232 Statement body = parseStatement(); | 1336 Statement body = parseStatement(); |
1233 return new ForIn( | 1337 return new ForIn( |
1234 new VariableDeclarationList([ | 1338 _createVariableDeclarationList(keyword, identifier), |
1235 new VariableInitialization( | |
1236 new VariableDeclaration(identifier), null)]), | |
1237 objectExpression, | 1339 objectExpression, |
1238 body); | 1340 body); |
| 1341 } else if (acceptString('of')) { |
| 1342 Expression iterableExpression = parseAssignment(); |
| 1343 expectCategory(RPAREN); |
| 1344 Statement body = parseStatement(); |
| 1345 return new ForOf( |
| 1346 _createVariableDeclarationList(keyword, identifier), |
| 1347 iterableExpression, |
| 1348 body); |
1239 } | 1349 } |
1240 Expression declarations = finishVariableDeclarationList(identifier); | 1350 var declarations = finishVariableDeclarationList(keyword, identifier); |
1241 expectCategory(SEMICOLON); | 1351 expectCategory(SEMICOLON); |
1242 return finishFor(declarations); | 1352 return finishFor(declarations); |
1243 } | 1353 } |
1244 | 1354 |
1245 Expression init = parseExpression(); | 1355 Expression init = parseExpression(); |
1246 expectCategory(SEMICOLON); | 1356 expectCategory(SEMICOLON); |
1247 return finishFor(init); | 1357 return finishFor(init); |
1248 } | 1358 } |
1249 | 1359 |
| 1360 static VariableDeclarationList _createVariableDeclarationList( |
| 1361 String keyword, String identifier) { |
| 1362 return new VariableDeclarationList(keyword, [ |
| 1363 new VariableInitialization( |
| 1364 new VariableDeclaration(identifier), null)]); |
| 1365 } |
| 1366 |
1250 Statement parseFunctionDeclaration() { | 1367 Statement parseFunctionDeclaration() { |
1251 String name = lastToken; | 1368 String name = lastToken; |
1252 expectCategory(ALPHA); | 1369 expectCategory(ALPHA); |
1253 Expression fun = parseFun(); | 1370 Expression fun = parseFun(); |
1254 return new FunctionDeclaration(new VariableDeclaration(name), fun); | 1371 return new FunctionDeclaration(new VariableDeclaration(name), fun); |
1255 } | 1372 } |
1256 | 1373 |
1257 Statement parseTry() { | 1374 Statement parseTry() { |
1258 expectCategory(LBRACE); | 1375 expectCategory(LBRACE); |
1259 Block body = parseBlock(); | 1376 Block body = parseBlock(); |
1260 String token = lastToken; | |
1261 Catch catchPart = null; | 1377 Catch catchPart = null; |
1262 if (acceptString('catch')) catchPart = parseCatch(); | 1378 if (acceptString('catch')) catchPart = parseCatch(); |
1263 Block finallyPart = null; | 1379 Block finallyPart = null; |
1264 if (acceptString('finally')) { | 1380 if (acceptString('finally')) { |
1265 expectCategory(LBRACE); | 1381 expectCategory(LBRACE); |
1266 finallyPart = parseBlock(); | 1382 finallyPart = parseBlock(); |
1267 } else { | 1383 } else { |
1268 if (catchPart == null) error("expected 'finally'"); | 1384 if (catchPart == null) error("expected 'finally'"); |
1269 } | 1385 } |
1270 return new Try(body, catchPart, finallyPart); | 1386 return new Try(body, catchPart, finallyPart); |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1326 | 1442 |
1327 Catch parseCatch() { | 1443 Catch parseCatch() { |
1328 expectCategory(LPAREN); | 1444 expectCategory(LPAREN); |
1329 String identifier = lastToken; | 1445 String identifier = lastToken; |
1330 expectCategory(ALPHA); | 1446 expectCategory(ALPHA); |
1331 expectCategory(RPAREN); | 1447 expectCategory(RPAREN); |
1332 expectCategory(LBRACE); | 1448 expectCategory(LBRACE); |
1333 Block body = parseBlock(); | 1449 Block body = parseBlock(); |
1334 return new Catch(new VariableDeclaration(identifier), body); | 1450 return new Catch(new VariableDeclaration(identifier), body); |
1335 } | 1451 } |
| 1452 |
| 1453 ClassExpression parseClass() { |
| 1454 VariableDeclaration name; |
| 1455 if (acceptCategory(HASH)) { |
| 1456 var interpolatedName = new InterpolatedVariableDeclaration(parseHash()); |
| 1457 interpolatedValues.add(interpolatedName); |
| 1458 name = interpolatedName; |
| 1459 } else { |
| 1460 name = new VariableDeclaration(lastToken); |
| 1461 expectCategory(ALPHA); |
| 1462 } |
| 1463 Expression heritage = null; |
| 1464 if (acceptString('extends')) { |
| 1465 heritage = parseLeftHandSide(); |
| 1466 } |
| 1467 expectCategory(LBRACE); |
| 1468 var methods = new List<Method>(); |
| 1469 while (lastCategory != RBRACE) { |
| 1470 methods.add(parseMethod()); |
| 1471 } |
| 1472 expectCategory(RBRACE); |
| 1473 return new ClassExpression(name, heritage, methods); |
| 1474 } |
| 1475 |
| 1476 Method parseMethod() { |
| 1477 if (acceptCategory(HASH)) return parseInterpolatedMember(); |
| 1478 |
| 1479 bool isStatic = acceptString('static'); |
| 1480 bool isGetter = acceptString('get'); |
| 1481 bool isSetter = isGetter ? false : acceptString('set'); |
| 1482 var name = parsePropertyName(); |
| 1483 var fun = parseFun(); |
| 1484 return new Method(name, fun, |
| 1485 isGetter: isGetter, isSetter: isSetter, isStatic: isStatic); |
| 1486 } |
| 1487 |
| 1488 InterpolatedMethod parseInterpolatedMember() { |
| 1489 var member = new InterpolatedMethod(parseHash()); |
| 1490 interpolatedValues.add(member); |
| 1491 return member; |
| 1492 } |
| 1493 |
| 1494 Expression parsePropertyName() { |
| 1495 String identifier = lastToken; |
| 1496 if (acceptCategory(ALPHA)) { |
| 1497 return new PropertyName(identifier); |
| 1498 } else if (acceptCategory(STRING)) { |
| 1499 return new LiteralString(identifier); |
| 1500 } else if (acceptCategory(SYMBOL)) { |
| 1501 // e.g. void |
| 1502 return new LiteralString('"$identifier"'); |
| 1503 } else if (acceptCategory(LSQUARE)) { |
| 1504 var expr = parseAssignment(); |
| 1505 expectCategory(RSQUARE); |
| 1506 return expr; |
| 1507 } else if (acceptCategory(HASH)) { |
| 1508 var nameOrPosition = parseHash(); |
| 1509 var interpolatedLiteral = new InterpolatedLiteral(nameOrPosition); |
| 1510 interpolatedValues.add(interpolatedLiteral); |
| 1511 return interpolatedLiteral; |
| 1512 } else { |
| 1513 error('Expected property name'); |
| 1514 return null; |
| 1515 } |
| 1516 } |
1336 } | 1517 } |
OLD | NEW |