| OLD | NEW | 
|---|
| 1 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. | 
| 4 | 4 | 
| 5 library polymer_expressions.eval; | 5 library polymer_expressions.eval; | 
| 6 | 6 | 
| 7 import 'dart:async'; | 7 import 'dart:async'; | 
| 8 import 'dart:collection'; | 8 import 'dart:collection'; | 
| 9 | 9 | 
| 10 import 'package:observe/observe.dart'; | 10 import 'package:observe/observe.dart'; | 
| 11 import 'package:smoke/smoke.dart' as smoke; | 11 import 'package:smoke/smoke.dart' as smoke; | 
| 12 | 12 | 
| 13 import 'async.dart'; | 13 import 'async.dart'; | 
| 14 import 'expression.dart'; | 14 import 'expression.dart'; | 
| 15 import 'filter.dart'; | 15 import 'filter.dart'; | 
| 16 import 'visitor.dart'; | 16 import 'visitor.dart'; | 
| 17 | 17 | 
| 18 final _BINARY_OPERATORS = { | 18 final _BINARY_OPERATORS = { | 
| 19   '+':  (a, b) => a + b, | 19   '+':   (a, b) => a + b, | 
| 20   '-':  (a, b) => a - b, | 20   '-':   (a, b) => a - b, | 
| 21   '*':  (a, b) => a * b, | 21   '*':   (a, b) => a * b, | 
| 22   '/':  (a, b) => a / b, | 22   '/':   (a, b) => a / b, | 
| 23   '%':  (a, b) => a % b, | 23   '%':   (a, b) => a % b, | 
| 24   '==': (a, b) => a == b, | 24   '==':  (a, b) => a == b, | 
| 25   '!=': (a, b) => a != b, | 25   '!=':  (a, b) => a != b, | 
| 26   '>':  (a, b) => a > b, | 26   '===': (a, b) => identical(a, b), | 
| 27   '>=': (a, b) => a >= b, | 27   '!==': (a, b) => !identical(a, b), | 
| 28   '<':  (a, b) => a < b, | 28   '>':   (a, b) => a > b, | 
| 29   '<=': (a, b) => a <= b, | 29   '>=':  (a, b) => a >= b, | 
| 30   '||': (a, b) => a || b, | 30   '<':   (a, b) => a < b, | 
| 31   '&&': (a, b) => a && b, | 31   '<=':  (a, b) => a <= b, | 
| 32   '|':  (a, f) { | 32   '||':  (a, b) => a || b, | 
|  | 33   '&&':  (a, b) => a && b, | 
|  | 34   '|':   (a, f) { | 
| 33     if (f is Transformer) return f.forward(a); | 35     if (f is Transformer) return f.forward(a); | 
| 34     if (f is Filter) return f(a); | 36     if (f is Filter) return f(a); | 
| 35     throw new EvalException("Filters must be a one-argument function."); | 37     throw new EvalException("Filters must be a one-argument function."); | 
| 36   } | 38   } | 
| 37 }; | 39 }; | 
| 38 | 40 | 
| 39 final _UNARY_OPERATORS = { | 41 final _UNARY_OPERATORS = { | 
| 40   '+': (a) => a, | 42   '+': (a) => a, | 
| 41   '-': (a) => -a, | 43   '-': (a) => -a, | 
| 42   '!': (a) => !a, | 44   '!': (a) => !a, | 
| 43 }; | 45 }; | 
| 44 | 46 | 
| 45 final _BOOLEAN_OPERATORS = ['!', '||', '&&']; | 47 final _BOOLEAN_OPERATORS = ['!', '||', '&&']; | 
| 46 | 48 | 
| 47 /** | 49 /** | 
| 48  * Evaluation [expr] in the context of [scope]. | 50  * Evaluation [expr] in the context of [scope]. | 
| 49  */ | 51  */ | 
| 50 Object eval(Expression expr, Scope scope) { | 52 Object eval(Expression expr, Scope scope) => new EvalVisitor(scope).visit(expr); | 
| 51   var observer = observe(expr, scope); |  | 
| 52   new Updater(scope).visit(observer); |  | 
| 53   return observer._value; |  | 
| 54 } |  | 
| 55 | 53 | 
| 56 /** | 54 /** | 
| 57  * Returns an [ExpressionObserver] that evaluates [expr] in the context of | 55  * Returns an [ExpressionObserver] that evaluates [expr] in the context of | 
| 58  * scope] and listens for any changes on [Observable] values that are | 56  * scope] and listens for any changes on [Observable] values that are | 
| 59  * returned from sub-expressions. When a value changes the expression is | 57  * returned from sub-expressions. When a value changes the expression is | 
| 60  * reevaluated and the new result is sent to the [onUpdate] stream of the | 58  * reevaluated and the new result is sent to the [onUpdate] stream of the | 
| 61  * [ExpressionObsserver]. | 59  * [ExpressionObsserver]. | 
| 62  */ | 60  */ | 
| 63 ExpressionObserver observe(Expression expr, Scope scope) { | 61 ExpressionObserver observe(Expression expr, Scope scope) { | 
| 64   var observer = new ObserverBuilder().visit(expr); | 62   var observer = new ObserverBuilder().visit(expr); | 
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 145 | 143 | 
| 146 | 144 | 
| 147 /** | 145 /** | 
| 148  * A scope in polymer expressions that can map names to objects. Scopes contain | 146  * A scope in polymer expressions that can map names to objects. Scopes contain | 
| 149  * a set of named variables and a unique model object. The scope structure | 147  * a set of named variables and a unique model object. The scope structure | 
| 150  * is then used to lookup names using the `[]` operator. The lookup first | 148  * is then used to lookup names using the `[]` operator. The lookup first | 
| 151  * searches for the name in local variables, then in global variables, | 149  * searches for the name in local variables, then in global variables, | 
| 152  * and then finally looks up the name as a property in the model. | 150  * and then finally looks up the name as a property in the model. | 
| 153  */ | 151  */ | 
| 154 abstract class Scope implements Indexable<String, Object> { | 152 abstract class Scope implements Indexable<String, Object> { | 
| 155   static int __seq = 1; |  | 
| 156   final int _seq = __seq++; |  | 
| 157 |  | 
| 158   Scope._(); | 153   Scope._(); | 
| 159 | 154 | 
| 160   /** Create a scope containing a [model] and all of [variables]. */ | 155   /** Create a scope containing a [model] and all of [variables]. */ | 
| 161   factory Scope({Object model, Map<String, Object> variables}) { | 156   factory Scope({Object model, Map<String, Object> variables}) { | 
| 162     var scope = new _ModelScope(model); | 157     var scope = new _ModelScope(model); | 
| 163     return variables == null ? scope | 158     return variables == null ? scope | 
| 164         : new _GlobalsScope(new Map<String, Object>.from(variables), scope); | 159         : new _GlobalsScope(new Map<String, Object>.from(variables), scope); | 
| 165   } | 160   } | 
| 166 | 161 | 
| 167   /** Return the unique model in this scope. */ | 162   /** Return the unique model in this scope. */ | 
| (...skipping 13 matching lines...) Expand all  Loading... | 
| 181   /** | 176   /** | 
| 182    * Returns whether [name] is defined in [model], that is, a lookup | 177    * Returns whether [name] is defined in [model], that is, a lookup | 
| 183    * would not find a variable with that name, but there is a non-null model | 178    * would not find a variable with that name, but there is a non-null model | 
| 184    * where we can look it up as a property. | 179    * where we can look it up as a property. | 
| 185    */ | 180    */ | 
| 186   bool _isModelProperty(String name); | 181   bool _isModelProperty(String name); | 
| 187 | 182 | 
| 188   /** Create a new scope extending this scope with an additional variable. */ | 183   /** Create a new scope extending this scope with an additional variable. */ | 
| 189   Scope childScope(String name, Object value) => | 184   Scope childScope(String name, Object value) => | 
| 190       new _LocalVariableScope(name, value, this); | 185       new _LocalVariableScope(name, value, this); | 
| 191 |  | 
| 192   String toString() => 'Scope(seq: $_seq model: $model)'; |  | 
| 193 |  | 
| 194 } | 186 } | 
| 195 | 187 | 
| 196 /** | 188 /** | 
| 197  * A scope that looks up names in a model object. This kind of scope has no | 189  * A scope that looks up names in a model object. This kind of scope has no | 
| 198  * parent scope because all our lookup operations stop when we reach the model | 190  * parent scope because all our lookup operations stop when we reach the model | 
| 199  * object. Any variables added in scope or global variables are added as child | 191  * object. Any variables added in scope or global variables are added as child | 
| 200  * scopes. | 192  * scopes. | 
| 201  */ | 193  */ | 
| 202 class _ModelScope extends Scope { | 194 class _ModelScope extends Scope { | 
| 203   final Object model; | 195   final Object model; | 
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 300 | 292 | 
| 301   _updateSelf(Scope scope) {} | 293   _updateSelf(Scope scope) {} | 
| 302 | 294 | 
| 303   _invalidate(Scope scope) { | 295   _invalidate(Scope scope) { | 
| 304     _observe(scope); | 296     _observe(scope); | 
| 305     if (_parent != null) { | 297     if (_parent != null) { | 
| 306       _parent._invalidate(scope); | 298       _parent._invalidate(scope); | 
| 307     } | 299     } | 
| 308   } | 300   } | 
| 309 | 301 | 
|  | 302   _unobserve() { | 
|  | 303     if (_subscription != null) { | 
|  | 304       _subscription.cancel(); | 
|  | 305       _subscription = null; | 
|  | 306     } | 
|  | 307   } | 
|  | 308 | 
| 310   _observe(Scope scope) { | 309   _observe(Scope scope) { | 
| 311     // unobserve last value | 310     _unobserve(); | 
| 312     if (_subscription != null) { |  | 
| 313       _subscription.cancel(); |  | 
| 314       _subscription = null; |  | 
| 315     } |  | 
| 316 | 311 | 
| 317     var _oldValue = _value; | 312     var _oldValue = _value; | 
| 318 | 313 | 
| 319     // evaluate | 314     // evaluate | 
| 320     _updateSelf(scope); | 315     _updateSelf(scope); | 
| 321 | 316 | 
| 322     if (!identical(_value, _oldValue)) { | 317     if (!identical(_value, _oldValue)) { | 
| 323       _controller.add(_value); | 318       _controller.add(_value); | 
| 324     } | 319     } | 
| 325   } | 320   } | 
| 326 | 321 | 
| 327   String toString() => _expr.toString(); | 322   String toString() => _expr.toString(); | 
| 328 } | 323 } | 
| 329 | 324 | 
| 330 class Updater extends RecursiveVisitor { | 325 class Updater extends RecursiveVisitor { | 
| 331   final Scope scope; | 326   final Scope scope; | 
| 332 | 327 | 
| 333   Updater(this.scope); | 328   Updater(this.scope); | 
| 334 | 329 | 
| 335   visitExpression(ExpressionObserver e) { | 330   visitExpression(ExpressionObserver e) { | 
| 336     e._observe(scope); | 331     e._observe(scope); | 
| 337   } | 332   } | 
| 338 } | 333 } | 
| 339 | 334 | 
|  | 335 class Closer extends RecursiveVisitor { | 
|  | 336   static final _instance = new Closer._(); | 
|  | 337   factory Closer() => _instance; | 
|  | 338   Closer._(); | 
|  | 339 | 
|  | 340   visitExpression(ExpressionObserver e) { | 
|  | 341     e._unobserve(); | 
|  | 342   } | 
|  | 343 } | 
|  | 344 | 
|  | 345 class EvalVisitor extends Visitor { | 
|  | 346   final Scope scope; | 
|  | 347 | 
|  | 348   EvalVisitor(this.scope); | 
|  | 349 | 
|  | 350   visitEmptyExpression(EmptyExpression e) => scope.model; | 
|  | 351 | 
|  | 352   visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child); | 
|  | 353 | 
|  | 354   visitGetter(Getter g) { | 
|  | 355     var receiver = visit(g.receiver); | 
|  | 356     if (receiver == null) return null; | 
|  | 357     var symbol = smoke.nameToSymbol(g.name); | 
|  | 358     return smoke.read(receiver, symbol); | 
|  | 359   } | 
|  | 360 | 
|  | 361   visitIndex(Index i) { | 
|  | 362     var receiver = visit(i.receiver); | 
|  | 363     if (receiver == null) return null; | 
|  | 364     var key = visit(i.argument); | 
|  | 365     return receiver[key]; | 
|  | 366   } | 
|  | 367 | 
|  | 368   visitInvoke(Invoke i) { | 
|  | 369     var receiver = visit(i.receiver); | 
|  | 370     if (receiver == null) return null; | 
|  | 371     var args = (i.arguments == null) | 
|  | 372         ? null | 
|  | 373         : i.arguments.map(visit).toList(growable: false); | 
|  | 374 | 
|  | 375     if (i.method == null) { | 
|  | 376       assert(receiver is Function); | 
|  | 377       return Function.apply(receiver, args); | 
|  | 378     } | 
|  | 379 | 
|  | 380     var symbol = smoke.nameToSymbol(i.method); | 
|  | 381     return smoke.invoke(receiver, symbol, args); | 
|  | 382   } | 
|  | 383 | 
|  | 384   visitLiteral(Literal l) => l.value; | 
|  | 385 | 
|  | 386   visitListLiteral(ListLiteral l) => l.items.map(visit).toList(); | 
|  | 387 | 
|  | 388   visitMapLiteral(MapLiteral l) { | 
|  | 389     var map = {}; | 
|  | 390     for (var entry in l.entries) { | 
|  | 391       var key = visit(entry.key); | 
|  | 392       var value = visit(entry.entryValue); | 
|  | 393       map[key] = value; | 
|  | 394     } | 
|  | 395     return map; | 
|  | 396   } | 
|  | 397 | 
|  | 398   visitMapLiteralEntry(MapLiteralEntry e) => | 
|  | 399       throw new UnsupportedError("should never be called"); | 
|  | 400 | 
|  | 401   visitIdentifier(Identifier i) => scope[i.value]; | 
|  | 402 | 
|  | 403   visitBinaryOperator(BinaryOperator o) { | 
|  | 404     var operator = o.operator; | 
|  | 405     var left = visit(o.left); | 
|  | 406     var right = visit(o.right); | 
|  | 407 | 
|  | 408     var f = _BINARY_OPERATORS[operator]; | 
|  | 409     if (operator == '&&' || operator == '||') { | 
|  | 410       // TODO: short-circuit | 
|  | 411       return f(_toBool(left), _toBool(right)); | 
|  | 412     } else if (operator == '==' || operator == '!=') { | 
|  | 413       return f(left, right); | 
|  | 414     } else if (left == null || right == null) { | 
|  | 415       return null; | 
|  | 416     } | 
|  | 417     return f(left, right); | 
|  | 418   } | 
|  | 419 | 
|  | 420   visitUnaryOperator(UnaryOperator o) { | 
|  | 421     var expr = visit(o.child); | 
|  | 422     var f = _UNARY_OPERATORS[o.operator]; | 
|  | 423     if (o.operator == '!') { | 
|  | 424       return f(_toBool(expr)); | 
|  | 425     } | 
|  | 426     return (expr == null) ? null : f(expr); | 
|  | 427   } | 
|  | 428 | 
|  | 429   visitTernaryOperator(TernaryOperator o) => | 
|  | 430       visit(o.condition) == true ? visit(o.trueExpr) : visit(o.falseExpr); | 
|  | 431 | 
|  | 432   visitInExpression(InExpression i) => | 
|  | 433       throw new UnsupportedError("can't eval an 'in' expression"); | 
|  | 434 | 
|  | 435   visitAsExpression(AsExpression i) => | 
|  | 436       throw new UnsupportedError("can't eval an 'as' expression"); | 
|  | 437 } | 
|  | 438 | 
| 340 class ObserverBuilder extends Visitor { | 439 class ObserverBuilder extends Visitor { | 
| 341   final Queue parents = new Queue(); | 440   final Queue parents = new Queue(); | 
| 342 | 441 | 
| 343   ObserverBuilder(); | 442   ObserverBuilder(); | 
| 344 | 443 | 
| 345   visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e); | 444   visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e); | 
| 346 | 445 | 
| 347   visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child); | 446   visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child); | 
| 348 | 447 | 
| 349   visitGetter(Getter g) { | 448   visitGetter(Getter g) { | 
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 463 } | 562 } | 
| 464 | 563 | 
| 465 class ListLiteralObserver extends ExpressionObserver<ListLiteral> | 564 class ListLiteralObserver extends ExpressionObserver<ListLiteral> | 
| 466     implements ListLiteral { | 565     implements ListLiteral { | 
| 467 | 566 | 
| 468   final List<ExpressionObserver> items; | 567   final List<ExpressionObserver> items; | 
| 469 | 568 | 
| 470   ListLiteralObserver(ListLiteral value, this.items) : super(value); | 569   ListLiteralObserver(ListLiteral value, this.items) : super(value); | 
| 471 | 570 | 
| 472   _updateSelf(Scope scope) { | 571   _updateSelf(Scope scope) { | 
| 473     _value = items.map((i) => i._value).toList(growable: false); | 572     _value = items.map((i) => i._value).toList(); | 
| 474   } | 573   } | 
| 475 | 574 | 
| 476   accept(Visitor v) => v.visitListLiteral(this); | 575   accept(Visitor v) => v.visitListLiteral(this); | 
| 477 } | 576 } | 
| 478 | 577 | 
| 479 class MapLiteralObserver extends ExpressionObserver<MapLiteral> | 578 class MapLiteralObserver extends ExpressionObserver<MapLiteral> | 
| 480     implements MapLiteral { | 579     implements MapLiteral { | 
| 481 | 580 | 
| 482   final List<MapLiteralEntryObserver> entries; | 581   final List<MapLiteralEntryObserver> entries; | 
| 483 | 582 | 
| (...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 714   accept(Visitor v) => v.visitInvoke(this); | 813   accept(Visitor v) => v.visitInvoke(this); | 
| 715 } | 814 } | 
| 716 | 815 | 
| 717 _toBool(v) => (v == null) ? false : v; | 816 _toBool(v) => (v == null) ? false : v; | 
| 718 | 817 | 
| 719 class EvalException implements Exception { | 818 class EvalException implements Exception { | 
| 720   final String message; | 819   final String message; | 
| 721   EvalException(this.message); | 820   EvalException(this.message); | 
| 722   String toString() => "EvalException: $message"; | 821   String toString() => "EvalException: $message"; | 
| 723 } | 822 } | 
| OLD | NEW | 
|---|