| 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 library polymer_expressions.parser; | 5 library polymer_expressions.parser; |
| 6 | 6 |
| 7 import 'tokenizer.dart'; | 7 import 'tokenizer.dart'; |
| 8 export 'tokenizer.dart' show ParseException; |
| 8 import 'expression.dart'; | 9 import 'expression.dart'; |
| 9 | 10 |
| 10 const _UNARY_OPERATORS = const ['+', '-', '!']; | 11 const _UNARY_OPERATORS = const <String>['+', '-', '!']; |
| 12 const _BINARY_OPERATORS = const <String>['+', '-', '*', '/', '%', '^', '==', |
| 13 '!=', '>', '<', '>=', '<=', '||', '&&', '&', '===', '!==', '|']; |
| 11 | 14 |
| 12 Expression parse(String expr) => new Parser(expr).parse(); | 15 Expression parse(String expr) => new Parser(expr).parse(); |
| 13 | 16 |
| 14 class Parser { | 17 class Parser { |
| 15 final AstFactory _astFactory; | 18 final AstFactory _astFactory; |
| 16 final Tokenizer _tokenizer; | 19 final Tokenizer _tokenizer; |
| 17 List<Token> _tokens; | 20 List<Token> _tokens; |
| 18 Iterator _iterator; | 21 Iterator _iterator; |
| 19 Token get _token => _iterator.current; | 22 Token get _token => _iterator.current; |
| 20 | 23 |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 89 } else if (right is Invoke && right.receiver is Identifier) { | 92 } else if (right is Invoke && right.receiver is Identifier) { |
| 90 Identifier method = right.receiver; | 93 Identifier method = right.receiver; |
| 91 return _astFactory.invoke(left, method.value, right.arguments); | 94 return _astFactory.invoke(left, method.value, right.arguments); |
| 92 } else { | 95 } else { |
| 93 throw new ParseException("expected identifier: $right"); | 96 throw new ParseException("expected identifier: $right"); |
| 94 } | 97 } |
| 95 } | 98 } |
| 96 | 99 |
| 97 Expression _parseBinary(left) { | 100 Expression _parseBinary(left) { |
| 98 var op = _token; | 101 var op = _token; |
| 102 if (!_BINARY_OPERATORS.contains(op.value)) { |
| 103 throw new ParseException("unknown operator: ${op.value}"); |
| 104 } |
| 99 _advance(); | 105 _advance(); |
| 100 var right = _parseUnary(); | 106 var right = _parseUnary(); |
| 101 while (_token != null | 107 while (_token != null |
| 102 && (_token.kind == OPERATOR_TOKEN | 108 && (_token.kind == OPERATOR_TOKEN |
| 103 || _token.kind == DOT_TOKEN | 109 || _token.kind == DOT_TOKEN |
| 104 || _token.kind == GROUPER_TOKEN) | 110 || _token.kind == GROUPER_TOKEN) |
| 105 && _token.precedence > op.precedence) { | 111 && _token.precedence > op.precedence) { |
| 106 right = _parsePrecedence(right, _token.precedence); | 112 right = _parsePrecedence(right, _token.precedence); |
| 107 } | 113 } |
| 108 return _astFactory.binary(left, op.value, right); | 114 return _astFactory.binary(left, op.value, right); |
| 109 } | 115 } |
| 110 | 116 |
| 111 Expression _parseUnary() { | 117 Expression _parseUnary() { |
| 112 if (_token.kind == OPERATOR_TOKEN) { | 118 if (_token.kind == OPERATOR_TOKEN) { |
| 113 var value = _token.value; | 119 var value = _token.value; |
| 114 if (value == '+' || value == '-') { | 120 if (value == '+' || value == '-') { |
| 115 _advance(); | 121 _advance(); |
| 116 if (_token.kind == INTEGER_TOKEN) { | 122 if (_token.kind == INTEGER_TOKEN) { |
| 117 return _parseInteger(value); | 123 return _parseInteger(value); |
| 118 } else if (_token.kind == DECIMAL_TOKEN) { | 124 } else if (_token.kind == DECIMAL_TOKEN) { |
| 119 return _parseDecimal(value); | 125 return _parseDecimal(value); |
| 120 } else { | 126 } else { |
| 121 var expr = _parsePrecedence(_parsePrimary(), POSTFIX_PRECEDENCE); | 127 var expr = _parsePrecedence(_parsePrimary(), POSTFIX_PRECEDENCE); |
| 122 return _astFactory.unary(value, expr); | 128 return _astFactory.unary(value, expr); |
| 123 } | 129 } |
| 124 } else if (value == '!') { | 130 } else if (value == '!') { |
| 125 _advance(); | 131 _advance(); |
| 126 var expr = _parsePrecedence(_parsePrimary(), POSTFIX_PRECEDENCE); | 132 var expr = _parsePrecedence(_parsePrimary(), POSTFIX_PRECEDENCE); |
| 127 return _astFactory.unary(value, expr); | 133 return _astFactory.unary(value, expr); |
| 134 } else { |
| 135 throw new ParseException("unexpected token: $value"); |
| 128 } | 136 } |
| 129 } | 137 } |
| 130 return _parsePrimary(); | 138 return _parsePrimary(); |
| 131 } | 139 } |
| 132 | 140 |
| 133 Expression _parseTernary(condition) { | 141 Expression _parseTernary(condition) { |
| 134 _advance(OPERATOR_TOKEN, '?'); | 142 _advance(OPERATOR_TOKEN, '?'); |
| 135 var trueExpr = _parseExpression(); | 143 var trueExpr = _parseExpression(); |
| 136 _advance(COLON_TOKEN); | 144 _advance(COLON_TOKEN); |
| 137 var falseExpr = _parseExpression(); | 145 var falseExpr = _parseExpression(); |
| 138 return _astFactory.ternary(condition, trueExpr, falseExpr); | 146 return _astFactory.ternary(condition, trueExpr, falseExpr); |
| 139 } | 147 } |
| 140 | 148 |
| 141 Expression _parsePrimary() { | 149 Expression _parsePrimary() { |
| 142 var kind = _token.kind; | 150 var kind = _token.kind; |
| 143 switch (kind) { | 151 switch (kind) { |
| 144 case KEYWORD_TOKEN: | 152 case KEYWORD_TOKEN: |
| 145 var keyword = _token.value; | 153 var keyword = _token.value; |
| 146 if (keyword == 'this') { | 154 if (keyword == 'this') { |
| 147 _advance(); | 155 _advance(); |
| 148 // TODO(justin): return keyword node | 156 // TODO(justin): return keyword node |
| 149 return _astFactory.identifier('this'); | 157 return _astFactory.identifier('this'); |
| 150 } else if (KEYWORDS.contains(keyword)) { | 158 } else if (KEYWORDS.contains(keyword)) { |
| 151 throw new ParseException('invalid keyword: $keyword'); | 159 throw new ParseException('unexpected keyword: $keyword'); |
| 152 } | 160 } |
| 153 throw new ArgumentError('unrecognized keyword: $keyword'); | 161 throw new ParseException('unrecognized keyword: $keyword'); |
| 154 case IDENTIFIER_TOKEN: | 162 case IDENTIFIER_TOKEN: |
| 155 return _parseInvokeOrIdentifier(); | 163 return _parseInvokeOrIdentifier(); |
| 156 case STRING_TOKEN: | 164 case STRING_TOKEN: |
| 157 return _parseString(); | 165 return _parseString(); |
| 158 case INTEGER_TOKEN: | 166 case INTEGER_TOKEN: |
| 159 return _parseInteger(); | 167 return _parseInteger(); |
| 160 case DECIMAL_TOKEN: | 168 case DECIMAL_TOKEN: |
| 161 return _parseDecimal(); | 169 return _parseDecimal(); |
| 162 case GROUPER_TOKEN: | 170 case GROUPER_TOKEN: |
| 163 if (_token.value == '(') { | 171 if (_token.value == '(') { |
| 164 return _parseParenthesized(); | 172 return _parseParenthesized(); |
| 165 } else if (_token.value == '{') { | 173 } else if (_token.value == '{') { |
| 166 return _parseMapLiteral(); | 174 return _parseMapLiteral(); |
| 167 } else if (_token.value == '[') { | 175 } else if (_token.value == '[') { |
| 168 return _parseListLiteral(); | 176 return _parseListLiteral(); |
| 169 } | 177 } |
| 170 return null; | 178 return null; |
| 171 case COLON_TOKEN: | 179 case COLON_TOKEN: |
| 172 // TODO(justinfagnani): We need better errors throughout the parser, and | 180 throw new ParseException('unexpected token ":"'); |
| 173 // we should be throwing ParseErrors to be caught by the caller | |
| 174 throw new ArgumentError('unexpected token ":"'); | |
| 175 default: | 181 default: |
| 176 return null; | 182 return null; |
| 177 } | 183 } |
| 178 } | 184 } |
| 179 | 185 |
| 180 ListLiteral _parseListLiteral() { | 186 ListLiteral _parseListLiteral() { |
| 181 var items = []; | 187 var items = []; |
| 182 do { | 188 do { |
| 183 _advance(); | 189 _advance(); |
| 184 if (_token.kind == GROUPER_TOKEN && _token.value == ']') { | 190 if (_token.kind == GROUPER_TOKEN && _token.value == ']') { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 220 var right = _parseExpression(); | 226 var right = _parseExpression(); |
| 221 return _astFactory.inExpr(left, right); | 227 return _astFactory.inExpr(left, right); |
| 222 } | 228 } |
| 223 | 229 |
| 224 AsExpression _parseAsExpression(Expression left) { | 230 AsExpression _parseAsExpression(Expression left) { |
| 225 assert(_token.value == 'as'); | 231 assert(_token.value == 'as'); |
| 226 _advance(); | 232 _advance(); |
| 227 var right = _parseExpression(); | 233 var right = _parseExpression(); |
| 228 if (right is! Identifier) { | 234 if (right is! Identifier) { |
| 229 throw new ParseException( | 235 throw new ParseException( |
| 230 "as... statements must end with an identifier"); | 236 "'as' statements must end with an identifier"); |
| 231 } | 237 } |
| 232 return _astFactory.asExpr(left, right); | 238 return _astFactory.asExpr(left, right); |
| 233 } | 239 } |
| 234 | 240 |
| 235 Expression _parseInvokeOrIdentifier() { | 241 Expression _parseInvokeOrIdentifier() { |
| 236 if (_token.value == 'true') { | 242 if (_token.value == 'true') { |
| 237 _advance(); | 243 _advance(); |
| 238 return _astFactory.literal(true); | 244 return _astFactory.literal(true); |
| 239 } | 245 } |
| 240 if (_token.value == 'false') { | 246 if (_token.value == 'false') { |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 309 return value; | 315 return value; |
| 310 } | 316 } |
| 311 | 317 |
| 312 Literal<double> _parseDecimal([String prefix = '']) { | 318 Literal<double> _parseDecimal([String prefix = '']) { |
| 313 var value = _astFactory.literal(double.parse('$prefix${_token.value}')); | 319 var value = _astFactory.literal(double.parse('$prefix${_token.value}')); |
| 314 _advance(); | 320 _advance(); |
| 315 return value; | 321 return value; |
| 316 } | 322 } |
| 317 | 323 |
| 318 } | 324 } |
| OLD | NEW |