| 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'; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 53 } | 53 } |
| 54 | 54 |
| 55 /** | 55 /** |
| 56 * Returns an [ExpressionObserver] that evaluates [expr] in the context of | 56 * Returns an [ExpressionObserver] that evaluates [expr] in the context of |
| 57 * scope] and listens for any changes on [Observable] values that are | 57 * scope] and listens for any changes on [Observable] values that are |
| 58 * returned from sub-expressions. When a value changes the expression is | 58 * returned from sub-expressions. When a value changes the expression is |
| 59 * reevaluated and the new result is sent to the [onUpdate] stream of the | 59 * reevaluated and the new result is sent to the [onUpdate] stream of the |
| 60 * [ExpressionObsserver]. | 60 * [ExpressionObsserver]. |
| 61 */ | 61 */ |
| 62 ExpressionObserver observe(Expression expr, Scope scope) { | 62 ExpressionObserver observe(Expression expr, Scope scope) { |
| 63 var observer = new ObserverBuilder(scope).visit(expr); | 63 var observer = new ObserverBuilder().visit(expr); |
| 64 return observer; | 64 return observer; |
| 65 } | 65 } |
| 66 | 66 |
| 67 /** | 67 /** |
| 68 * Causes [expr] to be reevaluated a returns it's value. | 68 * Causes [expr] to be reevaluated a returns it's value. |
| 69 */ | 69 */ |
| 70 Object update(ExpressionObserver expr, Scope scope) { | 70 Object update(ExpressionObserver expr, Scope scope) { |
| 71 new Updater(scope).visit(expr); | 71 new Updater(scope).visit(expr); |
| 72 return expr.currentValue; | 72 return expr.currentValue; |
| 73 } | 73 } |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 145 | 145 |
| 146 | 146 |
| 147 /** | 147 /** |
| 148 * A scope in polymer expressions that can map names to objects. Scopes contain | 148 * 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 | 149 * 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 | 150 * is then used to lookup names using the `[]` operator. The lookup first |
| 151 * searches for the name in local variables, then in global variables, | 151 * 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. | 152 * and then finally looks up the name as a property in the model. |
| 153 */ | 153 */ |
| 154 abstract class Scope implements Indexable<String, Object> { | 154 abstract class Scope implements Indexable<String, Object> { |
| 155 static int __seq = 1; |
| 156 final int _seq = __seq++; |
| 157 |
| 155 Scope._(); | 158 Scope._(); |
| 156 | 159 |
| 157 /** Create a scope containing a [model] and all of [variables]. */ | 160 /** Create a scope containing a [model] and all of [variables]. */ |
| 158 factory Scope({Object model, Map<String, Object> variables}) { | 161 factory Scope({Object model, Map<String, Object> variables}) { |
| 159 var scope = new _ModelScope(model); | 162 var scope = new _ModelScope(model); |
| 160 return variables == null ? scope | 163 return variables == null ? scope |
| 161 : new _GlobalsScope(new Map<String, Object>.from(variables), scope); | 164 : new _GlobalsScope(new Map<String, Object>.from(variables), scope); |
| 162 } | 165 } |
| 163 | 166 |
| 164 /** Return the unique model in this scope. */ | 167 /** Return the unique model in this scope. */ |
| (...skipping 13 matching lines...) Expand all Loading... |
| 178 /** | 181 /** |
| 179 * Returns whether [name] is defined in [model], that is, a lookup | 182 * Returns whether [name] is defined in [model], that is, a lookup |
| 180 * would not find a variable with that name, but there is a non-null model | 183 * would not find a variable with that name, but there is a non-null model |
| 181 * where we can look it up as a property. | 184 * where we can look it up as a property. |
| 182 */ | 185 */ |
| 183 bool _isModelProperty(String name); | 186 bool _isModelProperty(String name); |
| 184 | 187 |
| 185 /** Create a new scope extending this scope with an additional variable. */ | 188 /** Create a new scope extending this scope with an additional variable. */ |
| 186 Scope childScope(String name, Object value) => | 189 Scope childScope(String name, Object value) => |
| 187 new _LocalVariableScope(name, value, this); | 190 new _LocalVariableScope(name, value, this); |
| 191 |
| 192 String toString() => 'Scope(seq: $_seq model: $model)'; |
| 193 |
| 188 } | 194 } |
| 189 | 195 |
| 190 /** | 196 /** |
| 191 * A scope that looks up names in a model object. This kind of scope has no | 197 * A scope that looks up names in a model object. This kind of scope has no |
| 192 * parent scope because all our lookup operations stop when we reach the model | 198 * parent scope because all our lookup operations stop when we reach the model |
| 193 * object. Any variables added in scope or global variables are added as child | 199 * object. Any variables added in scope or global variables are added as child |
| 194 * scopes. | 200 * scopes. |
| 195 */ | 201 */ |
| 196 class _ModelScope extends Scope { | 202 class _ModelScope extends Scope { |
| 197 final Object model; | 203 final Object model; |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 322 } | 328 } |
| 323 | 329 |
| 324 class Updater extends RecursiveVisitor { | 330 class Updater extends RecursiveVisitor { |
| 325 final Scope scope; | 331 final Scope scope; |
| 326 | 332 |
| 327 Updater(this.scope); | 333 Updater(this.scope); |
| 328 | 334 |
| 329 visitExpression(ExpressionObserver e) { | 335 visitExpression(ExpressionObserver e) { |
| 330 e._observe(scope); | 336 e._observe(scope); |
| 331 } | 337 } |
| 332 | |
| 333 visitInExpression(InObserver c) { | |
| 334 visit(c.right); | |
| 335 visitExpression(c); | |
| 336 } | |
| 337 } | 338 } |
| 338 | 339 |
| 339 class ObserverBuilder extends Visitor { | 340 class ObserverBuilder extends Visitor { |
| 340 final Scope scope; | |
| 341 final Queue parents = new Queue(); | 341 final Queue parents = new Queue(); |
| 342 | 342 |
| 343 ObserverBuilder(this.scope); | 343 ObserverBuilder(); |
| 344 | 344 |
| 345 visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e); | 345 visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e); |
| 346 | 346 |
| 347 visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child); | 347 visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child); |
| 348 | 348 |
| 349 visitGetter(Getter g) { | 349 visitGetter(Getter g) { |
| 350 var receiver = visit(g.receiver); | 350 var receiver = visit(g.receiver); |
| 351 var getter = new GetterObserver(g, receiver); | 351 var getter = new GetterObserver(g, receiver); |
| 352 receiver._parent = getter; | 352 receiver._parent = getter; |
| 353 return getter; | 353 return getter; |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 421 var trueExpr = visit(o.trueExpr); | 421 var trueExpr = visit(o.trueExpr); |
| 422 var falseExpr = visit(o.falseExpr); | 422 var falseExpr = visit(o.falseExpr); |
| 423 var ternary = new TernaryObserver(o, condition, trueExpr, falseExpr); | 423 var ternary = new TernaryObserver(o, condition, trueExpr, falseExpr); |
| 424 condition._parent = ternary; | 424 condition._parent = ternary; |
| 425 trueExpr._parent = ternary; | 425 trueExpr._parent = ternary; |
| 426 falseExpr._parent = ternary; | 426 falseExpr._parent = ternary; |
| 427 return ternary; | 427 return ternary; |
| 428 } | 428 } |
| 429 | 429 |
| 430 visitInExpression(InExpression i) { | 430 visitInExpression(InExpression i) { |
| 431 // don't visit the left. It's an identifier, but we don't want to evaluate | 431 throw new UnsupportedError("can't eval an 'in' expression"); |
| 432 // it, we just want to add it to the comprehension object | 432 } |
| 433 var left = visit(i.left); | 433 |
| 434 var right = visit(i.right); | 434 visitAsExpression(AsExpression i) { |
| 435 var inexpr = new InObserver(i, left, right); | 435 throw new UnsupportedError("can't eval an 'as' expression"); |
| 436 right._parent = inexpr; | |
| 437 return inexpr; | |
| 438 } | 436 } |
| 439 } | 437 } |
| 440 | 438 |
| 441 class EmptyObserver extends ExpressionObserver<EmptyExpression> | 439 class EmptyObserver extends ExpressionObserver<EmptyExpression> |
| 442 implements EmptyExpression { | 440 implements EmptyExpression { |
| 443 | 441 |
| 444 EmptyObserver(EmptyExpression value) : super(value); | 442 EmptyObserver(EmptyExpression value) : super(value); |
| 445 | 443 |
| 446 _updateSelf(Scope scope) { | 444 _updateSelf(Scope scope) { |
| 447 _value = scope.model; | 445 _value = scope.model; |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 507 | 505 |
| 508 class IdentifierObserver extends ExpressionObserver<Identifier> | 506 class IdentifierObserver extends ExpressionObserver<Identifier> |
| 509 implements Identifier { | 507 implements Identifier { |
| 510 | 508 |
| 511 IdentifierObserver(Identifier value) : super(value); | 509 IdentifierObserver(Identifier value) : super(value); |
| 512 | 510 |
| 513 String get value => _expr.value; | 511 String get value => _expr.value; |
| 514 | 512 |
| 515 _updateSelf(Scope scope) { | 513 _updateSelf(Scope scope) { |
| 516 _value = scope[value]; | 514 _value = scope[value]; |
| 517 | |
| 518 if (!scope._isModelProperty(value)) return; | 515 if (!scope._isModelProperty(value)) return; |
| 519 var model = scope.model; | 516 var model = scope.model; |
| 520 if (model is! Observable) return; | 517 if (model is! Observable) return; |
| 521 var symbol = smoke.nameToSymbol(value); | 518 var symbol = smoke.nameToSymbol(value); |
| 522 _subscription = (model as Observable).changes.listen((changes) { | 519 _subscription = (model as Observable).changes.listen((changes) { |
| 523 if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) { | 520 if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) { |
| 524 _invalidate(scope); | 521 _invalidate(scope); |
| 525 } | 522 } |
| 526 }); | 523 }); |
| 527 } | 524 } |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 710 _invalidate(scope); | 707 _invalidate(scope); |
| 711 } | 708 } |
| 712 }); | 709 }); |
| 713 } | 710 } |
| 714 } | 711 } |
| 715 } | 712 } |
| 716 | 713 |
| 717 accept(Visitor v) => v.visitInvoke(this); | 714 accept(Visitor v) => v.visitInvoke(this); |
| 718 } | 715 } |
| 719 | 716 |
| 720 class InObserver extends ExpressionObserver<InExpression> | |
| 721 implements InExpression { | |
| 722 IdentifierObserver left; | |
| 723 ExpressionObserver right; | |
| 724 | |
| 725 InObserver(Expression expr, this.left, this.right) : super(expr); | |
| 726 | |
| 727 _updateSelf(Scope scope) { | |
| 728 Identifier identifier = left; | |
| 729 var iterable = right._value; | |
| 730 | |
| 731 if (iterable is! Iterable && iterable != null) { | |
| 732 throw new EvalException("right side of 'in' is not an iterator"); | |
| 733 } | |
| 734 | |
| 735 if (iterable is ObservableList) { | |
| 736 _subscription = iterable.listChanges.listen((_) => _invalidate(scope)); | |
| 737 } | |
| 738 | |
| 739 var name = identifier.value; | |
| 740 _value = iterable == null ? const [] : | |
| 741 iterable.map((i) => scope.childScope(name, i)).toList(growable: false); | |
| 742 } | |
| 743 | |
| 744 accept(Visitor v) => v.visitInExpression(this); | |
| 745 } | |
| 746 | |
| 747 _toBool(v) => (v == null) ? false : v; | 717 _toBool(v) => (v == null) ? false : v; |
| 748 | 718 |
| 749 class EvalException implements Exception { | 719 class EvalException implements Exception { |
| 750 final String message; | 720 final String message; |
| 751 EvalException(this.message); | 721 EvalException(this.message); |
| 752 String toString() => "EvalException: $message"; | 722 String toString() => "EvalException: $message"; |
| 753 } | 723 } |
| OLD | NEW |