| OLD | NEW |
| (Empty) | |
| 1 part of angular.core.parser; |
| 2 |
| 3 typedef ParsedGetter(self); |
| 4 typedef ParsedSetter(self, value); |
| 5 |
| 6 typedef Getter([locals]); |
| 7 typedef Setter(value, [locals]); |
| 8 |
| 9 abstract class ParserAST { |
| 10 bool get assignable; |
| 11 } |
| 12 |
| 13 class Token { |
| 14 final int index; |
| 15 final String text; |
| 16 |
| 17 var value; |
| 18 // Tokens should have one of these set. |
| 19 String opKey; |
| 20 String key; |
| 21 |
| 22 Token(this.index, this.text); |
| 23 |
| 24 withOp(op) { |
| 25 this.opKey = op; |
| 26 } |
| 27 |
| 28 withGetterSetter(key) { |
| 29 this.key = key; |
| 30 } |
| 31 |
| 32 withValue(value) { this.value = value; } |
| 33 |
| 34 toString() => "Token($text)"; |
| 35 } |
| 36 |
| 37 typedef Operator(dynamic self, ParserAST a, ParserAST b); |
| 38 |
| 39 Operator NULL_OP = (_, _0, _1) => null; |
| 40 Operator NOT_IMPL_OP = (_, _0, _1) { throw "Op not implemented"; }; |
| 41 |
| 42 // FUNCTIONS USED AT RUNTIME. |
| 43 |
| 44 parserEvalError(String s, String text, stack) => |
| 45 ['Eval Error: $s while evaling [$text]' + |
| 46 (stack != null ? '\n\nFROM:\n$stack' : '')]; |
| 47 |
| 48 // Automatic type conversion. |
| 49 autoConvertAdd(a, b) { |
| 50 if (a != null && b != null) { |
| 51 // TODO(deboer): Support others. |
| 52 if (a is String && b is! String) { |
| 53 return a + b.toString(); |
| 54 } |
| 55 if (a is! String && b is String) { |
| 56 return a.toString() + b; |
| 57 } |
| 58 return a + b; |
| 59 } |
| 60 if (a != null) return a; |
| 61 if (b != null) return b; |
| 62 return null; |
| 63 } |
| 64 |
| 65 objectIndexGetField(o, i, evalError) { |
| 66 if (o == null) throw evalError('Accessing null object'); |
| 67 |
| 68 if (o is List) { |
| 69 return o[i.toInt()]; |
| 70 } else if (o is Map) { |
| 71 return o[i.toString()]; // toString dangerous? |
| 72 } |
| 73 throw evalError("Attempted field access on a non-list, non-map"); |
| 74 } |
| 75 |
| 76 objectIndexSetField(o, i, v, evalError) { |
| 77 if (o is List) { |
| 78 int arrayIndex = i.toInt(); |
| 79 if (o.length <= arrayIndex) { o.length = arrayIndex + 1; } |
| 80 o[arrayIndex] = v; |
| 81 } else if (o is Map) { |
| 82 o[i.toString()] = v; // toString dangerous? |
| 83 } else { |
| 84 throw evalError("Attempting to set a field on a non-list, non-map"); |
| 85 } |
| 86 return v; |
| 87 } |
| 88 |
| 89 safeFunctionCall(userFn, fnName, evalError) { |
| 90 if (userFn == null) { |
| 91 throw evalError("Undefined function $fnName"); |
| 92 } |
| 93 if (userFn is! Function) { |
| 94 throw evalError("$fnName is not a function"); |
| 95 } |
| 96 return userFn; |
| 97 } |
| 98 |
| 99 |
| 100 Map<String, Operator> OPERATORS = { |
| 101 'undefined': NULL_OP, |
| 102 'null': NULL_OP, |
| 103 'true': (self, a, b) => true, |
| 104 'false': (self, a, b) => false, |
| 105 '+': (self, aFn, bFn) { |
| 106 var a = aFn.eval(self); |
| 107 var b = bFn.eval(self); |
| 108 return autoConvertAdd(a, b); |
| 109 }, |
| 110 '-': (self, a, b) { |
| 111 assert(a != null || b != null); |
| 112 var aResult = a != null ? a.eval(self) : null; |
| 113 var bResult = b != null ? b.eval(self) : null; |
| 114 return (aResult == null ? 0 : aResult) - (bResult == null ? 0 : bResult); |
| 115 }, |
| 116 '*': (s, a, b) => a.eval(s) * b.eval(s), |
| 117 '/': (s, a, b) => a.eval(s) / b.eval(s), |
| 118 '~/': (s, a, b) => a.eval(s) ~/ b.eval(s), |
| 119 '%': (s, a, b) => a.eval(s) % b.eval(s), |
| 120 '^': (s, a, b) => a.eval(s) ^ b.eval(s), |
| 121 '=': NULL_OP, |
| 122 '==': (s, a, b) => a.eval(s) == b.eval(s), |
| 123 '!=': (s, a, b) => a.eval(s) != b.eval(s), |
| 124 '<': (s, a, b) => a.eval(s) < b.eval(s), |
| 125 '>': (s, a, b) => a.eval(s) > b.eval(s), |
| 126 '<=': (s, a, b) => a.eval(s) <= b.eval(s), |
| 127 '>=': (s, a, b) => a.eval(s) >= b.eval(s), |
| 128 '&&': (s, a, b) => toBool(a.eval(s)) && toBool(b.eval(s)), |
| 129 '||': (s, a, b) => toBool(a.eval(s)) || toBool(b.eval(s)), |
| 130 '&': (s, a, b) => a.eval(s) & b.eval(s), |
| 131 '|': NOT_IMPL_OP, //b()(a()) |
| 132 '!': (s, a, b) => !toBool(a.eval(s)), |
| 133 '?': (s, c, t, f) => toBool(c.eval(s)) ? t.eval(s) : f.eval(s), |
| 134 }; |
| 135 |
| 136 @NgInjectableService() |
| 137 class DynamicParser implements Parser { |
| 138 final Lexer _lexer; |
| 139 final ParserBackend _b; |
| 140 |
| 141 DynamicParser(this._lexer, this._b); |
| 142 |
| 143 List<Token> _tokens; |
| 144 String _text; |
| 145 var _evalError; |
| 146 |
| 147 Map<String, ParserAST> _cache = {}; |
| 148 |
| 149 ParserAST call(String text) { |
| 150 var value = _cache[text]; |
| 151 if (value != null) { |
| 152 return value; |
| 153 } |
| 154 return _cache[text] = _call(text); |
| 155 } |
| 156 |
| 157 ParserAST _call(String text) { |
| 158 try { |
| 159 if (text == null) text = ''; |
| 160 _tokenSavers = []; |
| 161 _text = text; |
| 162 _tokens = _lexer.call(text); |
| 163 _evalError = (String s, [stack]) => parserEvalError(s, text, stack); |
| 164 |
| 165 ParserAST value = _statements(); |
| 166 |
| 167 if (_tokens.length != 0) { |
| 168 throw _parserError("Unconsumed token ${_tokens[0].text}"); |
| 169 } |
| 170 return value; |
| 171 } finally { |
| 172 _tokens = null; |
| 173 _text = null; |
| 174 _evalError = null; |
| 175 _tokenSavers = null; |
| 176 } |
| 177 } |
| 178 |
| 179 primaryFromToken(Token token, parserError) { |
| 180 if (token.key != null) { |
| 181 return _b.getterSetter(token.key); |
| 182 } |
| 183 if (token.opKey != null) { |
| 184 return _b.fromOperator(token.opKey); |
| 185 } |
| 186 if (token.value != null) { |
| 187 return _b.value(token.value); |
| 188 } |
| 189 if (token.text != null) { |
| 190 return _b.value(token.text); |
| 191 } |
| 192 throw parserError("Internal Angular Error: Tokens should have keys, text or
fns"); |
| 193 } |
| 194 |
| 195 _parserError(String s, [Token t]) { |
| 196 if (t == null && !_tokens.isEmpty) t = _tokens[0]; |
| 197 String location = t == null ? |
| 198 'the end of the expression' : |
| 199 'at column ${t.index + 1} in'; |
| 200 return 'Parser Error: $s $location [$_text]'; |
| 201 } |
| 202 |
| 203 |
| 204 Token _peekToken() { |
| 205 if (_tokens.length == 0) |
| 206 throw "Unexpected end of expression: " + _text; |
| 207 return _tokens[0]; |
| 208 } |
| 209 |
| 210 Token _peek([String e1, String e2, String e3, String e4]) { |
| 211 if (_tokens.length > 0) { |
| 212 Token token = _tokens[0]; |
| 213 String t = token.text; |
| 214 if (t==e1 || t==e2 || t==e3 || t==e4 || |
| 215 (e1 == null && e2 == null && e3 == null && e4 == null)) { |
| 216 return token; |
| 217 } |
| 218 } |
| 219 return null; |
| 220 } |
| 221 |
| 222 /** |
| 223 * Token savers are synchronous lists that allows Parser functions to |
| 224 * access the tokens parsed during some amount of time. They are useful |
| 225 * for printing helpful debugging messages. |
| 226 */ |
| 227 List<List<Token>> _tokenSavers; |
| 228 List<Token> _saveTokens() { var n = []; _tokenSavers.add(n); return n; } |
| 229 _stopSavingTokens(x) { if (!_tokenSavers.remove(x)) { throw 'bad token saver';
} return x; } |
| 230 _tokensText(List x) => x.map((x) => x.text).join(); |
| 231 |
| 232 |
| 233 Token _expect([String e1, String e2, String e3, String e4]){ |
| 234 Token token = _peek(e1, e2, e3, e4); |
| 235 if (token != null) { |
| 236 var consumed = _tokens.removeAt(0); |
| 237 _tokenSavers.forEach((ts) => ts.add(consumed)); |
| 238 return token; |
| 239 } |
| 240 return null; |
| 241 } |
| 242 |
| 243 ParserAST _consume(e1){ |
| 244 if (_expect(e1) == null) { |
| 245 throw _parserError("Missing expected $e1"); |
| 246 } |
| 247 } |
| 248 |
| 249 ParserAST _primary() { |
| 250 var primary; |
| 251 var ts = _saveTokens(); |
| 252 if (_expect('(') != null) { |
| 253 primary = _filterChain(); |
| 254 _consume(')'); |
| 255 } else if (_expect('[') != null) { |
| 256 primary = _arrayDeclaration(); |
| 257 } else if (_expect('{') != null) { |
| 258 primary = _object(); |
| 259 } else { |
| 260 Token token = _expect(); |
| 261 primary = primaryFromToken(token, _parserError); |
| 262 if (primary == null) { |
| 263 throw _parserError("Internal Angular Error: Unreachable code A."); |
| 264 } |
| 265 } |
| 266 |
| 267 var next; |
| 268 while ((next = _expect('(', '[', '.')) != null) { |
| 269 if (next.text == '(') { |
| 270 primary = _functionCall(primary, _tokensText(ts.sublist(0, ts.length - 1
))); |
| 271 } else if (next.text == '[') { |
| 272 primary = _objectIndex(primary); |
| 273 } else if (next.text == '.') { |
| 274 primary = _fieldAccess(primary); |
| 275 } else { |
| 276 throw _parserError("Internal Angular Error: Unreachable code B."); |
| 277 } |
| 278 } |
| 279 _stopSavingTokens(ts); |
| 280 return primary; |
| 281 } |
| 282 |
| 283 ParserAST _binaryFn(ParserAST left, String op, ParserAST right) => |
| 284 _b.binaryFn(left, op, right); |
| 285 |
| 286 ParserAST _unaryFn(String op, ParserAST right) => |
| 287 _b.unaryFn(op, right); |
| 288 |
| 289 ParserAST _unary() { |
| 290 var token; |
| 291 if (_expect('+') != null) { |
| 292 return _primary(); |
| 293 } else if ((token = _expect('-')) != null) { |
| 294 return _binaryFn(_b.zero(), token.opKey, _unary()); |
| 295 } else if ((token = _expect('!')) != null) { |
| 296 return _unaryFn(token.opKey, _unary()); |
| 297 } else { |
| 298 return _primary(); |
| 299 } |
| 300 } |
| 301 |
| 302 ParserAST _multiplicative() { |
| 303 var left = _unary(); |
| 304 var token; |
| 305 while ((token = _expect('*','%','/','~/')) != null) { |
| 306 left = _binaryFn(left, token.opKey, _unary()); |
| 307 } |
| 308 return left; |
| 309 } |
| 310 |
| 311 ParserAST _additive() { |
| 312 var left = _multiplicative(); |
| 313 var token; |
| 314 while ((token = _expect('+','-')) != null) { |
| 315 left = _binaryFn(left, token.opKey, _multiplicative()); |
| 316 } |
| 317 return left; |
| 318 } |
| 319 |
| 320 ParserAST _relational() { |
| 321 var left = _additive(); |
| 322 var token; |
| 323 if ((token = _expect('<', '>', '<=', '>=')) != null) { |
| 324 left = _binaryFn(left, token.opKey, _relational()); |
| 325 } |
| 326 return left; |
| 327 } |
| 328 |
| 329 ParserAST _equality() { |
| 330 var left = _relational(); |
| 331 var token; |
| 332 if ((token = _expect('==','!=')) != null) { |
| 333 left = _binaryFn(left, token.opKey, _equality()); |
| 334 } |
| 335 return left; |
| 336 } |
| 337 |
| 338 ParserAST _logicalAND() { |
| 339 var left = _equality(); |
| 340 var token; |
| 341 if ((token = _expect('&&')) != null) { |
| 342 left = _binaryFn(left, token.opKey, _logicalAND()); |
| 343 } |
| 344 return left; |
| 345 } |
| 346 |
| 347 ParserAST _logicalOR() { |
| 348 var left = _logicalAND(); |
| 349 var token; |
| 350 while(true) { |
| 351 if ((token = _expect('||')) != null) { |
| 352 left = _binaryFn(left, token.opKey, _logicalAND()); |
| 353 } else { |
| 354 return left; |
| 355 } |
| 356 } |
| 357 } |
| 358 |
| 359 ParserAST _ternary() { |
| 360 var ts = _saveTokens(); |
| 361 var cond = _logicalOR(); |
| 362 var token = _expect('?'); |
| 363 if (token != null) { |
| 364 var _true = _expression(); |
| 365 if ((token = _expect(':')) != null) { |
| 366 cond = _b.ternaryFn(cond, _true, _expression()); |
| 367 } else { |
| 368 throw _parserError('Conditional expression ${_tokensText(ts)} requires ' |
| 369 'all 3 expressions'); |
| 370 } |
| 371 } |
| 372 _stopSavingTokens(ts); |
| 373 return cond; |
| 374 } |
| 375 |
| 376 ParserAST _assignment() { |
| 377 var ts = _saveTokens(); |
| 378 var left = _ternary(); |
| 379 _stopSavingTokens(ts); |
| 380 var right; |
| 381 var token; |
| 382 if ((token = _expect('=')) != null) { |
| 383 if (!left.assignable) { |
| 384 throw _parserError('Expression ${_tokensText(ts)} is not assignable', to
ken); |
| 385 } |
| 386 right = _ternary(); |
| 387 return _b.assignment(left, right, _evalError); |
| 388 } else { |
| 389 return left; |
| 390 } |
| 391 } |
| 392 |
| 393 ParserAST _expression() { |
| 394 return _assignment(); |
| 395 } |
| 396 |
| 397 _filterChain() { |
| 398 var left = _expression(); |
| 399 var token; |
| 400 while(true) { |
| 401 if ((token = _expect('|')) != null) { |
| 402 left = _filter(left); |
| 403 } else { |
| 404 return left; |
| 405 } |
| 406 } |
| 407 } |
| 408 |
| 409 ParserAST _filter(ParserAST left) { |
| 410 var token = _expect(); |
| 411 var filterName = token.text; |
| 412 var argsFn = []; |
| 413 while(true) { |
| 414 if ((token = _expect(':')) != null) { |
| 415 argsFn.add(_expression()); |
| 416 } else { |
| 417 return _b.filter(filterName, left, argsFn, _evalError); |
| 418 } |
| 419 } |
| 420 } |
| 421 |
| 422 _statements() { |
| 423 List<ParserAST> statements = []; |
| 424 while (true) { |
| 425 if (_tokens.length > 0 && _peek('}', ')', ';', ']') == null) |
| 426 statements.add(_filterChain()); |
| 427 if (_expect(';') == null) { |
| 428 return statements.length == 1 |
| 429 ? statements[0] |
| 430 : _b.multipleStatements(statements); |
| 431 } |
| 432 } |
| 433 } |
| 434 |
| 435 _functionCall(fn, fnName) { |
| 436 var argsFn = []; |
| 437 if (_peekToken().text != ')') { |
| 438 do { |
| 439 argsFn.add(_expression()); |
| 440 } while (_expect(',') != null); |
| 441 } |
| 442 _consume(')'); |
| 443 return _b.functionCall(fn, fnName, argsFn, _evalError); |
| 444 } |
| 445 |
| 446 // This is used with json array declaration |
| 447 _arrayDeclaration() { |
| 448 var elementFns = []; |
| 449 if (_peekToken().text != ']') { |
| 450 do { |
| 451 elementFns.add(_expression()); |
| 452 } while (_expect(',') != null); |
| 453 } |
| 454 _consume(']'); |
| 455 return _b.arrayDeclaration(elementFns); |
| 456 } |
| 457 |
| 458 _objectIndex(obj) { |
| 459 var indexFn = _expression(); |
| 460 _consume(']'); |
| 461 return _b.objectIndex(obj, indexFn, _evalError); |
| 462 } |
| 463 |
| 464 _fieldAccess(object) { |
| 465 var field = _expect().text; |
| 466 //var getter = getter(field); |
| 467 return _b.fieldAccess(object, field); |
| 468 } |
| 469 |
| 470 _object() { |
| 471 var keyValues = []; |
| 472 if (_peekToken().text != '}') { |
| 473 do { |
| 474 var token = _expect(), |
| 475 key = token.value != null && token.value is String ? token.value : token
.text; |
| 476 _consume(":"); |
| 477 var value = _expression(); |
| 478 keyValues.add({"key":key, "value":value}); |
| 479 } while (_expect(',') != null); |
| 480 } |
| 481 _consume('}'); |
| 482 return _b.object(keyValues); |
| 483 } |
| 484 |
| 485 } |
| OLD | NEW |