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