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 |