Chromium Code Reviews| 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 @MirrorsUsed( | 10 @MirrorsUsed( |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 58 } | 58 } |
| 59 | 59 |
| 60 /** | 60 /** |
| 61 * Returns an [ExpressionObserver] that evaluates [expr] in the context of | 61 * Returns an [ExpressionObserver] that evaluates [expr] in the context of |
| 62 * scope] and listens for any changes on [Observable] values that are | 62 * scope] and listens for any changes on [Observable] values that are |
| 63 * returned from sub-expressions. When a value changes the expression is | 63 * returned from sub-expressions. When a value changes the expression is |
| 64 * reevaluated and the new result is sent to the [onUpdate] stream of the | 64 * reevaluated and the new result is sent to the [onUpdate] stream of the |
| 65 * [ExpressionObsserver]. | 65 * [ExpressionObsserver]. |
| 66 */ | 66 */ |
| 67 ExpressionObserver observe(Expression expr, Scope scope) { | 67 ExpressionObserver observe(Expression expr, Scope scope) { |
| 68 var observer = new ObserverBuilder(scope).visit(expr); | 68 var observer = new ObserverBuilder(/*scope*/).visit(expr); |
|
Jennifer Messerly
2014/03/17 23:10:08
did you mean to remove the scope argument to "obse
justinfagnani
2014/04/24 00:26:13
yep
| |
| 69 return observer; | 69 return observer; |
| 70 } | 70 } |
| 71 | 71 |
| 72 /** | 72 /** |
| 73 * Causes [expr] to be reevaluated a returns it's value. | 73 * Causes [expr] to be reevaluated a returns it's value. |
| 74 */ | 74 */ |
| 75 Object update(ExpressionObserver expr, Scope scope) { | 75 Object update(ExpressionObserver expr, Scope scope) { |
| 76 new Updater(scope).visit(expr); | 76 new Updater(scope).visit(expr); |
| 77 return expr.currentValue; | 77 return expr.currentValue; |
| 78 } | 78 } |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 157 */ | 157 */ |
| 158 class Scope { | 158 class Scope { |
| 159 final Scope parent; | 159 final Scope parent; |
| 160 final Object model; | 160 final Object model; |
| 161 // TODO(justinfagnani): disallow adding/removing names | 161 // TODO(justinfagnani): disallow adding/removing names |
| 162 final ObservableMap<String, Object> _variables; | 162 final ObservableMap<String, Object> _variables; |
| 163 | 163 |
| 164 Scope({this.model, Map<String, Object> variables, this.parent}) | 164 Scope({this.model, Map<String, Object> variables, this.parent}) |
| 165 : _variables = new ObservableMap.from(variables == null ? {} : variables); | 165 : _variables = new ObservableMap.from(variables == null ? {} : variables); |
| 166 | 166 |
| 167 get variables => _variables; | |
|
Jennifer Messerly
2014/03/17 23:10:08
another option is just to make _variables public.
justinfagnani
2014/04/24 00:26:13
Done.
| |
| 168 | |
| 167 Object operator[](String name) { | 169 Object operator[](String name) { |
| 168 if (name == 'this') { | 170 if (name == 'this') { |
| 169 return model; | 171 return model; |
| 170 } else if (_variables.containsKey(name)) { | 172 } else if (_variables.containsKey(name)) { |
| 171 return _convert(_variables[name]); | 173 return _convert(_variables[name]); |
| 172 } else { | 174 } else { |
| 173 var symbol = smoke.nameToSymbol(name); | 175 var symbol = smoke.nameToSymbol(name); |
| 174 if (model != null && smoke.hasGetter(model.runtimeType, symbol)) { | 176 if (model != null && smoke.hasGetter(model.runtimeType, symbol)) { |
| 175 return _convert(smoke.read(model, symbol)); | 177 return _convert(smoke.read(model, symbol)); |
| 176 } | 178 } |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 189 // seem neccessary | 191 // seem neccessary |
| 190 return null; | 192 return null; |
| 191 } else if (_variables.containsKey(name)) { | 193 } else if (_variables.containsKey(name)) { |
| 192 return _variables; | 194 return _variables; |
| 193 } else if (smoke.hasGetter(model.runtimeType, smoke.nameToSymbol(name))) { | 195 } else if (smoke.hasGetter(model.runtimeType, smoke.nameToSymbol(name))) { |
| 194 return model; | 196 return model; |
| 195 } | 197 } |
| 196 if (parent != null) { | 198 if (parent != null) { |
| 197 return parent.ownerOf(name); | 199 return parent.ownerOf(name); |
| 198 } | 200 } |
| 201 return null; | |
| 199 } | 202 } |
| 200 | 203 |
| 201 bool contains(String name) { | 204 String toString() => 'Scope(parent: $parent, model: $model, ' |
| 202 if (_variables.containsKey(name) || | 205 'variables: $variables)'; |
| 203 smoke.hasGetter(model.runtimeType, smoke.nameToSymbol(name))) { | |
| 204 return true; | |
| 205 } | |
| 206 if (parent != null) { | |
| 207 return parent.contains(name); | |
| 208 } | |
| 209 return false; | |
| 210 } | |
| 211 } | 206 } |
| 212 | 207 |
| 213 Object _convert(v) { | 208 Object _convert(v) { |
| 214 if (v is Stream) return new StreamBinding(v); | 209 if (v is Stream) return new StreamBinding(v); |
| 215 return v; | 210 return v; |
| 216 } | 211 } |
| 217 | 212 |
| 218 abstract class ExpressionObserver<E extends Expression> implements Expression { | 213 abstract class ExpressionObserver<E extends Expression> implements Expression { |
| 219 final E _expr; | 214 final E _expr; |
| 220 ExpressionObserver _parent; | 215 ExpressionObserver _parent; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 263 } | 258 } |
| 264 | 259 |
| 265 class Updater extends RecursiveVisitor { | 260 class Updater extends RecursiveVisitor { |
| 266 final Scope scope; | 261 final Scope scope; |
| 267 | 262 |
| 268 Updater(this.scope); | 263 Updater(this.scope); |
| 269 | 264 |
| 270 visitExpression(ExpressionObserver e) { | 265 visitExpression(ExpressionObserver e) { |
| 271 e._observe(scope); | 266 e._observe(scope); |
| 272 } | 267 } |
| 273 | |
| 274 visitInExpression(InObserver c) { | |
| 275 visit(c.right); | |
| 276 visitExpression(c); | |
| 277 } | |
| 278 } | 268 } |
| 279 | 269 |
| 280 class ObserverBuilder extends Visitor { | 270 class ObserverBuilder extends Visitor { |
| 281 final Scope scope; | 271 // final Scope scope; |
| 282 final Queue parents = new Queue(); | 272 final Queue parents = new Queue(); |
| 283 | 273 |
| 284 ObserverBuilder(this.scope); | 274 ObserverBuilder(/*this.scope*/); |
| 285 | 275 |
| 286 visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e); | 276 visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e); |
| 287 | 277 |
| 288 visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child); | 278 visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child); |
| 289 | 279 |
| 290 visitGetter(Getter g) { | 280 visitGetter(Getter g) { |
| 291 var receiver = visit(g.receiver); | 281 var receiver = visit(g.receiver); |
| 292 var getter = new GetterObserver(g, receiver); | 282 var getter = new GetterObserver(g, receiver); |
| 293 receiver._parent = getter; | 283 receiver._parent = getter; |
| 294 return getter; | 284 return getter; |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 362 var trueExpr = visit(o.trueExpr); | 352 var trueExpr = visit(o.trueExpr); |
| 363 var falseExpr = visit(o.falseExpr); | 353 var falseExpr = visit(o.falseExpr); |
| 364 var ternary = new TernaryObserver(o, condition, trueExpr, falseExpr); | 354 var ternary = new TernaryObserver(o, condition, trueExpr, falseExpr); |
| 365 condition._parent = ternary; | 355 condition._parent = ternary; |
| 366 trueExpr._parent = ternary; | 356 trueExpr._parent = ternary; |
| 367 falseExpr._parent = ternary; | 357 falseExpr._parent = ternary; |
| 368 return ternary; | 358 return ternary; |
| 369 } | 359 } |
| 370 | 360 |
| 371 visitInExpression(InExpression i) { | 361 visitInExpression(InExpression i) { |
| 372 // don't visit the left. It's an identifier, but we don't want to evaluate | 362 throw new UnsupportedError("can't eval an 'in' expression"); |
| 373 // it, we just want to add it to the comprehension object | |
| 374 var left = visit(i.left); | |
| 375 var right = visit(i.right); | |
| 376 var inexpr = new InObserver(i, left, right); | |
| 377 right._parent = inexpr; | |
| 378 return inexpr; | |
| 379 } | 363 } |
| 364 | |
| 365 visitAsExpression(AsExpression i) { | |
| 366 throw new UnsupportedError("can't eval an 'as' expression"); | |
| 367 } | |
| 368 | |
| 380 } | 369 } |
| 381 | 370 |
| 382 class EmptyObserver extends ExpressionObserver<EmptyExpression> | 371 class EmptyObserver extends ExpressionObserver<EmptyExpression> |
| 383 implements EmptyExpression { | 372 implements EmptyExpression { |
| 384 | 373 |
| 385 EmptyObserver(EmptyExpression value) : super(value); | 374 EmptyObserver(EmptyExpression value) : super(value); |
| 386 | 375 |
| 387 _updateSelf(Scope scope) { | 376 _updateSelf(Scope scope) { |
| 388 _value = scope.model; | 377 _value = scope.model; |
| 389 // TODO(justin): listen for scope.model changes? | 378 // TODO(justin): listen for scope.model changes? |
| (...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 647 _invalidate(scope); | 636 _invalidate(scope); |
| 648 } | 637 } |
| 649 }); | 638 }); |
| 650 } | 639 } |
| 651 } | 640 } |
| 652 } | 641 } |
| 653 | 642 |
| 654 accept(Visitor v) => v.visitInvoke(this); | 643 accept(Visitor v) => v.visitInvoke(this); |
| 655 } | 644 } |
| 656 | 645 |
| 657 class InObserver extends ExpressionObserver<InExpression> | |
| 658 implements InExpression { | |
| 659 IdentifierObserver left; | |
| 660 ExpressionObserver right; | |
| 661 | |
| 662 InObserver(Expression expr, this.left, this.right) : super(expr); | |
| 663 | |
| 664 _updateSelf(Scope scope) { | |
| 665 Identifier identifier = left; | |
| 666 var iterable = right._value; | |
| 667 | |
| 668 if (iterable is! Iterable && iterable != null) { | |
| 669 throw new EvalException("right side of 'in' is not an iterator"); | |
| 670 } | |
| 671 | |
| 672 if (iterable is ObservableList) { | |
| 673 _subscription = iterable.listChanges.listen((_) => _invalidate(scope)); | |
| 674 } | |
| 675 | |
| 676 // TODO: make Comprehension observable and update it | |
| 677 _value = new Comprehension(identifier.value, iterable); | |
| 678 } | |
| 679 | |
| 680 accept(Visitor v) => v.visitInExpression(this); | |
| 681 } | |
| 682 | |
| 683 _toBool(v) => (v == null) ? false : v; | 646 _toBool(v) => (v == null) ? false : v; |
| 684 | 647 |
| 685 /** | |
| 686 * A comprehension declaration ("a in b"). [identifier] is the loop variable | |
| 687 * that's added to the scope during iteration. [iterable] is the set of | |
| 688 * objects to iterate over. | |
| 689 */ | |
| 690 class Comprehension { | |
| 691 final String identifier; | |
| 692 final Iterable iterable; | |
| 693 | |
| 694 Comprehension(this.identifier, Iterable iterable) | |
| 695 : iterable = (iterable != null) ? iterable : const []; | |
| 696 } | |
| 697 | |
| 698 class EvalException implements Exception { | 648 class EvalException implements Exception { |
| 699 final String message; | 649 final String message; |
| 700 EvalException(this.message); | 650 EvalException(this.message); |
| 701 String toString() => "EvalException: $message"; | 651 String toString() => "EvalException: $message"; |
| 702 } | 652 } |
| OLD | NEW |