| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library polymer_expressions.parser; | |
| 6 | |
| 7 import 'tokenizer.dart'; | |
| 8 export 'tokenizer.dart' show ParseException; | |
| 9 import 'expression.dart'; | |
| 10 | |
| 11 const _UNARY_OPERATORS = const <String>['+', '-', '!']; | |
| 12 const _BINARY_OPERATORS = const <String>['+', '-', '*', '/', '%', '^', '==', | |
| 13 '!=', '>', '<', '>=', '<=', '||', '&&', '&', '===', '!==', '|']; | |
| 14 | |
| 15 Expression parse(String expr) => new Parser(expr).parse(); | |
| 16 | |
| 17 class Parser { | |
| 18 final AstFactory _astFactory; | |
| 19 final Tokenizer _tokenizer; | |
| 20 List<Token> _tokens; | |
| 21 Iterator _iterator; | |
| 22 Token get _token => _iterator.current; | |
| 23 | |
| 24 Parser(String input, {AstFactory astFactory}) | |
| 25 : _tokenizer = new Tokenizer(input), | |
| 26 _astFactory = (astFactory == null) ? new AstFactory() : astFactory; | |
| 27 | |
| 28 Expression parse() { | |
| 29 _tokens = _tokenizer.tokenize(); | |
| 30 _iterator = _tokens.iterator; | |
| 31 _advance(); | |
| 32 return _parseExpression(); | |
| 33 } | |
| 34 | |
| 35 _advance([int kind, String value]) { | |
| 36 if ((kind != null && (_token == null || _token.kind != kind)) | |
| 37 || (value != null && (_token == null || _token.value != value))) { | |
| 38 throw new ParseException("Expected kind $kind ($value): $_token"); | |
| 39 } | |
| 40 _iterator.moveNext(); | |
| 41 } | |
| 42 | |
| 43 Expression _parseExpression() { | |
| 44 if (_token == null) return _astFactory.empty(); | |
| 45 var expr = _parseUnary(); | |
| 46 return (expr == null) ? null : _parsePrecedence(expr, 0); | |
| 47 } | |
| 48 | |
| 49 // _parsePrecedence and _parseBinary implement the precedence climbing | |
| 50 // algorithm as described in: | |
| 51 // http://en.wikipedia.org/wiki/Operator-precedence_parser#Precedence_climbing
_method | |
| 52 Expression _parsePrecedence(Expression left, int precedence) { | |
| 53 assert(left != null); | |
| 54 while (_token != null) { | |
| 55 if (_token.kind == GROUPER_TOKEN) { | |
| 56 if (_token.value == '(') { | |
| 57 var args = _parseArguments(); | |
| 58 assert(args != null); | |
| 59 left = _astFactory.invoke(left, null, args); | |
| 60 } else if (_token.value == '[') { | |
| 61 var indexExpr = _parseIndex(); | |
| 62 left = _astFactory.index(left, indexExpr); | |
| 63 } else { | |
| 64 break; | |
| 65 } | |
| 66 } else if (_token.kind == DOT_TOKEN) { | |
| 67 _advance(); | |
| 68 var right = _parseUnary(); | |
| 69 left = _makeInvokeOrGetter(left, right); | |
| 70 } else if (_token.kind == KEYWORD_TOKEN) { | |
| 71 if (_token.value == 'in') { | |
| 72 left = _parseInExpression(left); | |
| 73 } else if (_token.value == 'as') { | |
| 74 left = _parseAsExpression(left); | |
| 75 } else { | |
| 76 break; | |
| 77 } | |
| 78 } else if (_token.kind == OPERATOR_TOKEN | |
| 79 && _token.precedence >= precedence) { | |
| 80 left = _token.value == '?' ? _parseTernary(left) : _parseBinary(left); | |
| 81 } else { | |
| 82 break; | |
| 83 } | |
| 84 } | |
| 85 return left; | |
| 86 } | |
| 87 | |
| 88 // invoke or getter | |
| 89 Expression _makeInvokeOrGetter(left, right) { | |
| 90 if (right is Identifier) { | |
| 91 return _astFactory.getter(left, right.value); | |
| 92 } else if (right is Invoke && right.receiver is Identifier) { | |
| 93 Identifier method = right.receiver; | |
| 94 return _astFactory.invoke(left, method.value, right.arguments); | |
| 95 } else { | |
| 96 throw new ParseException("expected identifier: $right"); | |
| 97 } | |
| 98 } | |
| 99 | |
| 100 Expression _parseBinary(left) { | |
| 101 var op = _token; | |
| 102 if (!_BINARY_OPERATORS.contains(op.value)) { | |
| 103 throw new ParseException("unknown operator: ${op.value}"); | |
| 104 } | |
| 105 _advance(); | |
| 106 var right = _parseUnary(); | |
| 107 while (_token != null | |
| 108 && (_token.kind == OPERATOR_TOKEN | |
| 109 || _token.kind == DOT_TOKEN | |
| 110 || _token.kind == GROUPER_TOKEN) | |
| 111 && _token.precedence > op.precedence) { | |
| 112 right = _parsePrecedence(right, _token.precedence); | |
| 113 } | |
| 114 return _astFactory.binary(left, op.value, right); | |
| 115 } | |
| 116 | |
| 117 Expression _parseUnary() { | |
| 118 if (_token.kind == OPERATOR_TOKEN) { | |
| 119 var value = _token.value; | |
| 120 if (value == '+' || value == '-') { | |
| 121 _advance(); | |
| 122 if (_token.kind == INTEGER_TOKEN) { | |
| 123 return _parseInteger(value); | |
| 124 } else if (_token.kind == DECIMAL_TOKEN) { | |
| 125 return _parseDecimal(value); | |
| 126 } else { | |
| 127 var expr = _parsePrecedence(_parsePrimary(), POSTFIX_PRECEDENCE); | |
| 128 return _astFactory.unary(value, expr); | |
| 129 } | |
| 130 } else if (value == '!') { | |
| 131 _advance(); | |
| 132 var expr = _parsePrecedence(_parsePrimary(), POSTFIX_PRECEDENCE); | |
| 133 return _astFactory.unary(value, expr); | |
| 134 } else { | |
| 135 throw new ParseException("unexpected token: $value"); | |
| 136 } | |
| 137 } | |
| 138 return _parsePrimary(); | |
| 139 } | |
| 140 | |
| 141 Expression _parseTernary(condition) { | |
| 142 _advance(OPERATOR_TOKEN, '?'); | |
| 143 var trueExpr = _parseExpression(); | |
| 144 _advance(COLON_TOKEN); | |
| 145 var falseExpr = _parseExpression(); | |
| 146 return _astFactory.ternary(condition, trueExpr, falseExpr); | |
| 147 } | |
| 148 | |
| 149 Expression _parsePrimary() { | |
| 150 var kind = _token.kind; | |
| 151 switch (kind) { | |
| 152 case KEYWORD_TOKEN: | |
| 153 var keyword = _token.value; | |
| 154 if (keyword == 'this') { | |
| 155 _advance(); | |
| 156 // TODO(justin): return keyword node | |
| 157 return _astFactory.identifier('this'); | |
| 158 } else if (KEYWORDS.contains(keyword)) { | |
| 159 throw new ParseException('unexpected keyword: $keyword'); | |
| 160 } | |
| 161 throw new ParseException('unrecognized keyword: $keyword'); | |
| 162 case IDENTIFIER_TOKEN: | |
| 163 return _parseInvokeOrIdentifier(); | |
| 164 case STRING_TOKEN: | |
| 165 return _parseString(); | |
| 166 case INTEGER_TOKEN: | |
| 167 return _parseInteger(); | |
| 168 case DECIMAL_TOKEN: | |
| 169 return _parseDecimal(); | |
| 170 case GROUPER_TOKEN: | |
| 171 if (_token.value == '(') { | |
| 172 return _parseParenthesized(); | |
| 173 } else if (_token.value == '{') { | |
| 174 return _parseMapLiteral(); | |
| 175 } else if (_token.value == '[') { | |
| 176 return _parseListLiteral(); | |
| 177 } | |
| 178 return null; | |
| 179 case COLON_TOKEN: | |
| 180 throw new ParseException('unexpected token ":"'); | |
| 181 default: | |
| 182 return null; | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 ListLiteral _parseListLiteral() { | |
| 187 var items = []; | |
| 188 do { | |
| 189 _advance(); | |
| 190 if (_token.kind == GROUPER_TOKEN && _token.value == ']') { | |
| 191 break; | |
| 192 } | |
| 193 items.add(_parseExpression()); | |
| 194 } while(_token != null && _token.value == ','); | |
| 195 _advance(GROUPER_TOKEN, ']'); | |
| 196 return new ListLiteral(items); | |
| 197 } | |
| 198 | |
| 199 MapLiteral _parseMapLiteral() { | |
| 200 var entries = []; | |
| 201 do { | |
| 202 _advance(); | |
| 203 if (_token.kind == GROUPER_TOKEN && _token.value == '}') { | |
| 204 break; | |
| 205 } | |
| 206 entries.add(_parseMapLiteralEntry()); | |
| 207 } while(_token != null && _token.value == ','); | |
| 208 _advance(GROUPER_TOKEN, '}'); | |
| 209 return new MapLiteral(entries); | |
| 210 } | |
| 211 | |
| 212 MapLiteralEntry _parseMapLiteralEntry() { | |
| 213 var key = _parseString(); | |
| 214 _advance(COLON_TOKEN, ':'); | |
| 215 var value = _parseExpression(); | |
| 216 return _astFactory.mapLiteralEntry(key, value); | |
| 217 } | |
| 218 | |
| 219 InExpression _parseInExpression(Expression left) { | |
| 220 assert(_token.value == 'in'); | |
| 221 if (left is! Identifier) { | |
| 222 throw new ParseException( | |
| 223 "in... statements must start with an identifier"); | |
| 224 } | |
| 225 _advance(); | |
| 226 var right = _parseExpression(); | |
| 227 return _astFactory.inExpr(left, right); | |
| 228 } | |
| 229 | |
| 230 AsExpression _parseAsExpression(Expression left) { | |
| 231 assert(_token.value == 'as'); | |
| 232 _advance(); | |
| 233 var right = _parseExpression(); | |
| 234 if (right is! Identifier) { | |
| 235 throw new ParseException( | |
| 236 "'as' statements must end with an identifier"); | |
| 237 } | |
| 238 return _astFactory.asExpr(left, right); | |
| 239 } | |
| 240 | |
| 241 Expression _parseInvokeOrIdentifier() { | |
| 242 if (_token.value == 'true') { | |
| 243 _advance(); | |
| 244 return _astFactory.literal(true); | |
| 245 } | |
| 246 if (_token.value == 'false') { | |
| 247 _advance(); | |
| 248 return _astFactory.literal(false); | |
| 249 } | |
| 250 if (_token.value == 'null') { | |
| 251 _advance(); | |
| 252 return _astFactory.literal(null); | |
| 253 } | |
| 254 var identifier = _parseIdentifier(); | |
| 255 var args = _parseArguments(); | |
| 256 if (args == null) { | |
| 257 return identifier; | |
| 258 } else { | |
| 259 return _astFactory.invoke(identifier, null, args); | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 Identifier _parseIdentifier() { | |
| 264 if (_token.kind != IDENTIFIER_TOKEN) { | |
| 265 throw new ParseException("expected identifier: $_token.value"); | |
| 266 } | |
| 267 var value = _token.value; | |
| 268 _advance(); | |
| 269 return _astFactory.identifier(value); | |
| 270 } | |
| 271 | |
| 272 List<Expression> _parseArguments() { | |
| 273 if (_token != null && _token.kind == GROUPER_TOKEN && _token.value == '(') { | |
| 274 var args = []; | |
| 275 do { | |
| 276 _advance(); | |
| 277 if (_token.kind == GROUPER_TOKEN && _token.value == ')') { | |
| 278 break; | |
| 279 } | |
| 280 var expr = _parseExpression(); | |
| 281 args.add(expr); | |
| 282 } while(_token != null && _token.value == ','); | |
| 283 _advance(GROUPER_TOKEN, ')'); | |
| 284 return args; | |
| 285 } | |
| 286 return null; | |
| 287 } | |
| 288 | |
| 289 Expression _parseIndex() { | |
| 290 if (_token != null && _token.kind == GROUPER_TOKEN && _token.value == '[') { | |
| 291 _advance(); | |
| 292 var expr = _parseExpression(); | |
| 293 _advance(GROUPER_TOKEN, ']'); | |
| 294 return expr; | |
| 295 } | |
| 296 return null; | |
| 297 } | |
| 298 | |
| 299 ParenthesizedExpression _parseParenthesized() { | |
| 300 _advance(); | |
| 301 var expr = _parseExpression(); | |
| 302 _advance(GROUPER_TOKEN, ')'); | |
| 303 return _astFactory.parenthesized(expr); | |
| 304 } | |
| 305 | |
| 306 Literal<String> _parseString() { | |
| 307 var value = _astFactory.literal(_token.value); | |
| 308 _advance(); | |
| 309 return value; | |
| 310 } | |
| 311 | |
| 312 Literal<int> _parseInteger([String prefix = '']) { | |
| 313 var value = _astFactory.literal(int.parse('$prefix${_token.value}')); | |
| 314 _advance(); | |
| 315 return value; | |
| 316 } | |
| 317 | |
| 318 Literal<double> _parseDecimal([String prefix = '']) { | |
| 319 var value = _astFactory.literal(double.parse('$prefix${_token.value}')); | |
| 320 _advance(); | |
| 321 return value; | |
| 322 } | |
| 323 | |
| 324 } | |
| OLD | NEW |