Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(135)

Side by Side Diff: pkg/polymer_expressions/lib/eval.dart

Issue 141703024: Refactor of PolymerExpressions. Adds "as" expressions. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: syntax, bindings, and globals tests now passing in Safari Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/pkg.status ('k') | pkg/polymer_expressions/lib/expression.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « pkg/pkg.status ('k') | pkg/polymer_expressions/lib/expression.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698