| OLD | NEW |
| 1 part of angular.core.parser; | 1 library angular.core.parser; |
| 2 | 2 |
| 3 typedef ParsedGetter(self); | 3 export 'package:angular/core/parser/syntax.dart' |
| 4 typedef ParsedSetter(self, value); | 4 show Visitor, Expression, BoundExpression; |
| 5 export 'package:angular/core/parser/dynamic_parser.dart' |
| 6 show DynamicParser, DynamicParserBackend, ClosureMap; |
| 7 export 'package:angular/core/parser/static_parser.dart' |
| 8 show StaticParser, StaticParserFunctions; |
| 5 | 9 |
| 6 typedef Getter([locals]); | 10 typedef LocalsWrapper(context, locals); |
| 7 typedef Setter(value, [locals]); | 11 typedef Getter(self); |
| 12 typedef Setter(self, value); |
| 8 | 13 |
| 9 abstract class ParserAST { | 14 /// Placeholder for DI. The parser you are looking for is [DynamicParser]. |
| 10 bool get assignable; | 15 abstract class Parser<T> { |
| 11 } | 16 T call(String input); |
| 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 } | 17 } |
| 98 | 18 |
| 99 | 19 |
| 100 Map<String, Operator> OPERATORS = { | 20 abstract class ParserBackend<T> { |
| 101 'undefined': NULL_OP, | 21 bool isAssignable(T expression); |
| 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 | 22 |
| 136 @NgInjectableService() | 23 T newChain(List expressions) |
| 137 class DynamicParser implements Parser { | 24 => null; |
| 138 final Lexer _lexer; | 25 T newFilter(T expression, String name, List arguments) |
| 139 final ParserBackend _b; | 26 => null; |
| 140 | 27 |
| 141 DynamicParser(this._lexer, this._b); | 28 T newAssign(T target, T value) |
| 29 => null; |
| 30 T newConditional(T condition, T yes, T no) |
| 31 => null; |
| 142 | 32 |
| 143 List<Token> _tokens; | 33 T newAccessScope(String name) |
| 144 String _text; | 34 => null; |
| 145 var _evalError; | 35 T newAccessMember(T object, String name) |
| 36 => null; |
| 37 T newAccessKeyed(T object, T key) |
| 38 => null; |
| 146 | 39 |
| 147 Map<String, ParserAST> _cache = {}; | 40 T newCallScope(String name, List arguments) |
| 41 => null; |
| 42 T newCallFunction(T function, List arguments) |
| 43 => null; |
| 44 T newCallMember(T object, String name, List arguments) |
| 45 => null; |
| 148 | 46 |
| 149 ParserAST call(String text) { | 47 T newPrefix(String operation, T expression) |
| 150 var value = _cache[text]; | 48 => null; |
| 151 if (value != null) { | 49 T newPrefixPlus(T expression) |
| 152 return value; | 50 => expression; |
| 153 } | 51 T newPrefixMinus(T expression) |
| 154 return _cache[text] = _call(text); | 52 => newBinaryMinus(newLiteralZero(), expression); |
| 155 } | 53 T newPrefixNot(T expression) |
| 54 => newPrefix('!', expression); |
| 156 | 55 |
| 157 ParserAST _call(String text) { | 56 T newBinary(String operation, T left, T right) |
| 158 try { | 57 => null; |
| 159 if (text == null) text = ''; | 58 T newBinaryPlus(T left, T right) |
| 160 _tokenSavers = []; | 59 => newBinary('+', left, right); |
| 161 _text = text; | 60 T newBinaryMinus(T left, T right) |
| 162 _tokens = _lexer.call(text); | 61 => newBinary('-', left, right); |
| 163 _evalError = (String s, [stack]) => parserEvalError(s, text, stack); | 62 T newBinaryMultiply(T left, T right) |
| 63 => newBinary('*', left, right); |
| 64 T newBinaryDivide(T left, T right) |
| 65 => newBinary('/', left, right); |
| 66 T newBinaryModulo(T left, T right) |
| 67 => newBinary('%', left, right); |
| 68 T newBinaryTruncatingDivide(T left, T right) |
| 69 => newBinary('~/', left, right); |
| 70 T newBinaryLogicalAnd(T left, T right) |
| 71 => newBinary('&&', left, right); |
| 72 T newBinaryLogicalOr(T left, T right) |
| 73 => newBinary('||', left, right); |
| 74 T newBinaryEqual(T left, T right) |
| 75 => newBinary('==', left, right); |
| 76 T newBinaryNotEqual(T left, T right) |
| 77 => newBinary('!=', left, right); |
| 78 T newBinaryLessThan(T left, T right) |
| 79 => newBinary('<', left, right); |
| 80 T newBinaryGreaterThan(T left, T right) |
| 81 => newBinary('>', left, right); |
| 82 T newBinaryLessThanEqual(T left, T right) |
| 83 => newBinary('<=', left, right); |
| 84 T newBinaryGreaterThanEqual(T left, T right) |
| 85 => newBinary('>=', left, right); |
| 164 | 86 |
| 165 ParserAST value = _statements(); | 87 T newLiteralPrimitive(value) |
| 166 | 88 => null; |
| 167 if (_tokens.length != 0) { | 89 T newLiteralArray(List elements) |
| 168 throw _parserError("Unconsumed token ${_tokens[0].text}"); | 90 => null; |
| 169 } | 91 T newLiteralObject(List<String> keys, List values) |
| 170 return value; | 92 => null; |
| 171 } finally { | 93 T newLiteralNull() |
| 172 _tokens = null; | 94 => newLiteralPrimitive(null); |
| 173 _text = null; | 95 T newLiteralZero() |
| 174 _evalError = null; | 96 => newLiteralNumber(0); |
| 175 _tokenSavers = null; | 97 T newLiteralBoolean(bool value) |
| 176 } | 98 => newLiteralPrimitive(value); |
| 177 } | 99 T newLiteralNumber(num value) |
| 178 | 100 => newLiteralPrimitive(value); |
| 179 primaryFromToken(Token token, parserError) { | 101 T newLiteralString(String value) |
| 180 if (token.key != null) { | 102 => 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 } | 103 } |
| OLD | NEW |