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

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: Roll to latest version, simplified much of the scope creation Created 6 years, 8 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
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 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 144
145 145
146 /** 146 /**
147 * A scope in polymer expressions that can map names to objects. Scopes contain 147 * A scope in polymer expressions that can map names to objects. Scopes contain
148 * a set of named variables and a unique model object. The scope structure 148 * a set of named variables and a unique model object. The scope structure
149 * is then used to lookup names using the `[]` operator. The lookup first 149 * is then used to lookup names using the `[]` operator. The lookup first
150 * searches for the name in local variables, then in global variables, 150 * searches for the name in local variables, then in global variables,
151 * and then finally looks up the name as a property in the model. 151 * and then finally looks up the name as a property in the model.
152 */ 152 */
153 abstract class Scope implements Indexable<String, Object> { 153 abstract class Scope implements Indexable<String, Object> {
154 static int __seq = 1;
155 final int _seq = __seq++;
156
154 Scope._(); 157 Scope._();
155 158
156 /** Create a scope containing a [model] and all of [variables]. */ 159 /** Create a scope containing a [model] and all of [variables]. */
157 factory Scope({Object model, Map<String, Object> variables}) { 160 factory Scope({Object model, Map<String, Object> variables}) {
158 var scope = new _ModelScope(model); 161 var scope = new _ModelScope(model);
159 return variables == null ? scope 162 return variables == null ? scope
160 : new _GlobalsScope(new Map<String, Object>.from(variables), scope); 163 : new _GlobalsScope(new Map<String, Object>.from(variables), scope);
161 } 164 }
162 165
163 /** Return the unique model in this scope. */ 166 /** Return the unique model in this scope. */
(...skipping 13 matching lines...) Expand all
177 /** 180 /**
178 * Returns whether [name] is defined in [model], that is, a lookup 181 * Returns whether [name] is defined in [model], that is, a lookup
179 * would not find a variable with that name, but there is a non-null model 182 * would not find a variable with that name, but there is a non-null model
180 * where we can look it up as a property. 183 * where we can look it up as a property.
181 */ 184 */
182 bool _isModelProperty(String name); 185 bool _isModelProperty(String name);
183 186
184 /** Create a new scope extending this scope with an additional variable. */ 187 /** Create a new scope extending this scope with an additional variable. */
185 Scope childScope(String name, Object value) => 188 Scope childScope(String name, Object value) =>
186 new _LocalVariableScope(name, value, this); 189 new _LocalVariableScope(name, value, this);
190
191 String toString() => 'Scope(seq: $_seq model: $model)';
192
187 } 193 }
188 194
189 /** 195 /**
190 * A scope that looks up names in a model object. This kind of scope has no 196 * A scope that looks up names in a model object. This kind of scope has no
191 * parent scope because all our lookup operations stop when we reach the model 197 * parent scope because all our lookup operations stop when we reach the model
192 * object. Any variables added in scope or global variables are added as child 198 * object. Any variables added in scope or global variables are added as child
193 * scopes. 199 * scopes.
194 */ 200 */
195 class _ModelScope extends Scope { 201 class _ModelScope extends Scope {
196 final Object model; 202 final Object model;
197 203
198 _ModelScope(this.model) : super._(); 204 _ModelScope(this.model) : super._();
199 205
200 Object operator[](String name) { 206 Object operator[](String name) {
201 if (name == 'this') return model; 207 if (name == 'this') return model;
202 var symbol = smoke.nameToSymbol(name); 208 var symbol = smoke.nameToSymbol(name);
203 if (model == null || symbol == null) { 209 if (model == null || symbol == null) {
204 throw new EvalException("variable '$name' not found"); 210 throw new EvalException("variable '$name' not found");
205 } 211 }
206 return _convert(smoke.read(model, symbol)); 212 return _convert(smoke.read(model, symbol));
207 } 213 }
208 214
209 Object _isModelProperty(String name) => name != 'this'; 215 Object _isModelProperty(String name) => name != 'this';
216
217 String toString() => 'ModelScope(model: $model)';
210 } 218 }
211 219
212 /** 220 /**
213 * A scope that holds a reference to a single variable. Polymer expressions 221 * A scope that holds a reference to a single variable. Polymer expressions
214 * introduce variables to the scope one at a time. Each time a variable is 222 * introduce variables to the scope one at a time. Each time a variable is
215 * added, a new [_LocalVariableScope] is created. 223 * added, a new [_LocalVariableScope] is created.
216 */ 224 */
217 class _LocalVariableScope extends Scope { 225 class _LocalVariableScope extends Scope {
218 final Scope parent; 226 final Scope parent;
219 final String varName; 227 final String varName;
(...skipping 11 matching lines...) Expand all
231 Object operator[](String name) { 239 Object operator[](String name) {
232 if (varName == name) return _convert(value); 240 if (varName == name) return _convert(value);
233 if (parent != null) return parent[name]; 241 if (parent != null) return parent[name];
234 throw new EvalException("variable '$name' not found"); 242 throw new EvalException("variable '$name' not found");
235 } 243 }
236 244
237 bool _isModelProperty(String name) { 245 bool _isModelProperty(String name) {
238 if (varName == name) return false; 246 if (varName == name) return false;
239 return parent == null ? false : parent._isModelProperty(name); 247 return parent == null ? false : parent._isModelProperty(name);
240 } 248 }
249
250 String toString() => 'LocalVariableScope(varName: $varName, value: $value, par ent: $parent)';
Jennifer Messerly 2014/04/24 00:51:34 long line
justinfagnani 2014/05/28 00:29:37 Done.
241 } 251 }
242 252
243 /** A scope that holds a reference to a global variables. */ 253 /** A scope that holds a reference to a global variables. */
244 class _GlobalsScope extends Scope { 254 class _GlobalsScope extends Scope {
245 final _ModelScope parent; 255 final _ModelScope parent;
246 final Map<String, Object> variables; 256 final Map<String, Object> variables;
247 257
248 _GlobalsScope(this.variables, this.parent) : super._() { 258 _GlobalsScope(this.variables, this.parent) : super._() {
249 if (variables.containsKey('this')) { 259 if (variables.containsKey('this')) {
250 throw new EvalException("'this' cannot be used as a variable name."); 260 throw new EvalException("'this' cannot be used as a variable name.");
251 } 261 }
252 } 262 }
253 263
254 Object get model => parent != null ? parent.model : null; 264 Object get model => parent != null ? parent.model : null;
255 265
256 Object operator[](String name) { 266 Object operator[](String name) {
257 if (variables.containsKey(name)) return _convert(variables[name]); 267 if (variables.containsKey(name)) return _convert(variables[name]);
258 if (parent != null) return parent[name]; 268 if (parent != null) return parent[name];
259 throw new EvalException("variable '$name' not found"); 269 throw new EvalException("variable '$name' not found");
260 } 270 }
261 271
262 bool _isModelProperty(String name) { 272 bool _isModelProperty(String name) {
263 if (variables.containsKey(name)) return false; 273 if (variables.containsKey(name)) return false;
264 return parent == null ? false : parent._isModelProperty(name); 274 return parent == null ? false : parent._isModelProperty(name);
265 } 275 }
276
277 String toString() => 'GlobalsScope(variables: $variables, parent: $parent)';
266 } 278 }
267 279
268 Object _convert(v) => v is Stream ? new StreamBinding(v) : v; 280 Object _convert(v) => v is Stream ? new StreamBinding(v) : v;
269 281
270 abstract class ExpressionObserver<E extends Expression> implements Expression { 282 abstract class ExpressionObserver<E extends Expression> implements Expression {
271 final E _expr; 283 final E _expr;
272 ExpressionObserver _parent; 284 ExpressionObserver _parent;
273 285
274 StreamSubscription _subscription; 286 StreamSubscription _subscription;
275 Object _value; 287 Object _value;
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
315 } 327 }
316 328
317 class Updater extends RecursiveVisitor { 329 class Updater extends RecursiveVisitor {
318 final Scope scope; 330 final Scope scope;
319 331
320 Updater(this.scope); 332 Updater(this.scope);
321 333
322 visitExpression(ExpressionObserver e) { 334 visitExpression(ExpressionObserver e) {
323 e._observe(scope); 335 e._observe(scope);
324 } 336 }
325
326 visitInExpression(InObserver c) {
327 visit(c.right);
328 visitExpression(c);
329 }
330 } 337 }
331 338
332 class ObserverBuilder extends Visitor { 339 class ObserverBuilder extends Visitor {
333 final Scope scope;
334 final Queue parents = new Queue(); 340 final Queue parents = new Queue();
335 341
336 ObserverBuilder(this.scope); 342 ObserverBuilder();
337 343
338 visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e); 344 visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e);
339 345
340 visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child); 346 visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child);
341 347
342 visitGetter(Getter g) { 348 visitGetter(Getter g) {
343 var receiver = visit(g.receiver); 349 var receiver = visit(g.receiver);
344 var getter = new GetterObserver(g, receiver); 350 var getter = new GetterObserver(g, receiver);
345 receiver._parent = getter; 351 receiver._parent = getter;
346 return getter; 352 return getter;
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
414 var trueExpr = visit(o.trueExpr); 420 var trueExpr = visit(o.trueExpr);
415 var falseExpr = visit(o.falseExpr); 421 var falseExpr = visit(o.falseExpr);
416 var ternary = new TernaryObserver(o, condition, trueExpr, falseExpr); 422 var ternary = new TernaryObserver(o, condition, trueExpr, falseExpr);
417 condition._parent = ternary; 423 condition._parent = ternary;
418 trueExpr._parent = ternary; 424 trueExpr._parent = ternary;
419 falseExpr._parent = ternary; 425 falseExpr._parent = ternary;
420 return ternary; 426 return ternary;
421 } 427 }
422 428
423 visitInExpression(InExpression i) { 429 visitInExpression(InExpression i) {
424 // don't visit the left. It's an identifier, but we don't want to evaluate 430 throw new UnsupportedError("can't eval an 'in' expression");
425 // it, we just want to add it to the comprehension object 431 }
426 var left = visit(i.left); 432
427 var right = visit(i.right); 433 visitAsExpression(AsExpression i) {
428 var inexpr = new InObserver(i, left, right); 434 throw new UnsupportedError("can't eval an 'as' expression");
429 right._parent = inexpr;
430 return inexpr;
431 } 435 }
432 } 436 }
433 437
434 class EmptyObserver extends ExpressionObserver<EmptyExpression> 438 class EmptyObserver extends ExpressionObserver<EmptyExpression>
435 implements EmptyExpression { 439 implements EmptyExpression {
436 440
437 EmptyObserver(EmptyExpression value) : super(value); 441 EmptyObserver(EmptyExpression value) : super(value);
438 442
439 _updateSelf(Scope scope) { 443 _updateSelf(Scope scope) {
440 _value = scope.model; 444 _value = scope.model;
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after
698 _invalidate(scope); 702 _invalidate(scope);
699 } 703 }
700 }); 704 });
701 } 705 }
702 } 706 }
703 } 707 }
704 708
705 accept(Visitor v) => v.visitInvoke(this); 709 accept(Visitor v) => v.visitInvoke(this);
706 } 710 }
707 711
708 class InObserver extends ExpressionObserver<InExpression>
709 implements InExpression {
710 IdentifierObserver left;
711 ExpressionObserver right;
712
713 InObserver(Expression expr, this.left, this.right) : super(expr);
714
715 _updateSelf(Scope scope) {
716 Identifier identifier = left;
717 var iterable = right._value;
718
719 if (iterable is! Iterable && iterable != null) {
720 throw new EvalException("right side of 'in' is not an iterator");
721 }
722
723 if (iterable is ObservableList) {
724 _subscription = iterable.listChanges.listen((_) => _invalidate(scope));
725 }
726
727 // TODO: make Comprehension observable and update it
728 _value = new Comprehension(identifier.value, iterable);
729 }
730
731 accept(Visitor v) => v.visitInExpression(this);
732 }
733 712
734 _toBool(v) => (v == null) ? false : v; 713 _toBool(v) => (v == null) ? false : v;
735 714
736 /**
737 * A comprehension declaration ("a in b"). [identifier] is the loop variable
738 * that's added to the scope during iteration. [iterable] is the set of
739 * objects to iterate over.
740 */
741 class Comprehension {
742 final String identifier;
743 final Iterable iterable;
744
745 Comprehension(this.identifier, Iterable iterable)
746 : iterable = (iterable != null) ? iterable : const [];
747 }
748
749 class EvalException implements Exception { 715 class EvalException implements Exception {
750 final String message; 716 final String message;
751 EvalException(this.message); 717 EvalException(this.message);
752 String toString() => "EvalException: $message"; 718 String toString() => "EvalException: $message";
753 } 719 }
OLDNEW
« no previous file with comments | « no previous file | pkg/polymer_expressions/lib/expression.dart » ('j') | pkg/polymer_expressions/lib/polymer_expressions.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698