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

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

Issue 207433002: Changes in polymer-expressions to prepare for codegen in polymer: (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 9 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 @MirrorsUsed(
11 metaTargets: const [Reflectable, ObservableProperty],
12 override: 'smoke.mirrors')
13 import 'dart:mirrors' show MirrorsUsed;
14
15 import 'package:observe/observe.dart'; 10 import 'package:observe/observe.dart';
16 import 'package:smoke/smoke.dart' as smoke; 11 import 'package:smoke/smoke.dart' as smoke;
17 12
18 import 'async.dart'; 13 import 'async.dart';
19 import 'expression.dart'; 14 import 'expression.dart';
20 import 'filter.dart'; 15 import 'filter.dart';
21 import 'visitor.dart'; 16 import 'visitor.dart';
22 17
23 final _BINARY_OPERATORS = { 18 final _BINARY_OPERATORS = {
24 '+': (a, b) => a + b, 19 '+': (a, b) => a + b,
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
140 // make the assignment 135 // make the assignment
141 var o = eval(expression, scope); 136 var o = eval(expression, scope);
142 if (o == null) throw new EvalException("Can't assign to null: $expression"); 137 if (o == null) throw new EvalException("Can't assign to null: $expression");
143 if (isIndex) { 138 if (isIndex) {
144 o[property] = value; 139 o[property] = value;
145 } else { 140 } else {
146 smoke.write(o, smoke.nameToSymbol(property), value); 141 smoke.write(o, smoke.nameToSymbol(property), value);
147 } 142 }
148 } 143 }
149 144
145
150 /** 146 /**
151 * A mapping of names to objects. Scopes contain a set of named [variables] and 147 * A scope in polymer expressions that can map names to objects. Scopes contain
152 * a single [model] object (which can be thought of as the "this" reference). 148 * a set of named variables and a unique model object. The scope structure
153 * Names are currently looked up in [variables] first, then the [model]. 149 * is then used to lookup names using the `[]` operator. The lookup first
154 * 150 * searches for the name in local variables, then in global variables,
155 * Scopes can be nested by giving them a [parent]. If a name in not found in a 151 * and then finally looks up the name as a property in the model.
156 * Scope, it will look for it in it's parent.
157 */ 152 */
158 class Scope { 153 abstract class Scope {
159 final Scope parent; 154 Scope._();
155
156 /** Create a scope containing a [model] and all of [variables]. */
157 factory Scope({Object model, Map<String, Object> variables}) {
158 var scope = new _ModelScope(model);
159 return variables == null ? scope
160 : new _GlobalsScope(new Map<String, Object>.from(variables), scope);
161 }
162
163 /** Return the unique model in this scope. */
164 Object get model;
165
166 /**
167 * Lookup the value of [name] in the current scope. If [name] is 'this', then
168 * we return the [model]. For any other name, this finds the first variable
169 * matching [name] or, if none exists, the property [name] in the [model].
170 */
171 Object operator[](String name);
172
173 /**
174 * Returns whether [name] is defined in [model], that is, a lookup
175 * would not find a variable with that name, but there is a non-null model
176 * where we can look it up as a property.
177 */
178 bool _isModelProperty(String name);
179
180 /** Create a new scope extending this scope with an additional variable. */
181 Scope childScope(String name, Object value) =>
182 new _LocalVariableScope(name, value, this);
183 }
184
185 /**
186 * A scope that looks up names in a model object. This kind of scope has no
187 * parent scope because all our lookup operations stop when we reach the model
188 * object. Any variables added in scope or global variables are added as child
189 * scopes.
190 */
191 class _ModelScope extends Scope {
160 final Object model; 192 final Object model;
161 // TODO(justinfagnani): disallow adding/removing names
162 final ObservableMap<String, Object> _variables;
163 193
164 Scope({this.model, Map<String, Object> variables, this.parent}) 194 _ModelScope(this.model) : super._();
165 : _variables = new ObservableMap.from(variables == null ? {} : variables);
166 195
167 Object operator[](String name) { 196 Object operator[](String name) {
168 if (name == 'this') { 197 if (name == 'this') return model;
169 return model; 198 var symbol = smoke.nameToSymbol(name);
170 } else if (_variables.containsKey(name)) { 199 if (model == null || symbol == null) {
171 return _convert(_variables[name]); 200 throw new EvalException("variable '$name' not found");
172 } else {
173 var symbol = smoke.nameToSymbol(name);
174 if (model != null && smoke.hasGetter(model.runtimeType, symbol)) {
175 return _convert(smoke.read(model, symbol));
176 }
177 } 201 }
178 if (parent != null) { 202 return _convert(smoke.read(model, symbol));
179 return _convert(parent[name]); 203 }
180 } else { 204
181 throw new EvalException("variable '$name' not found"); 205 Object _isModelProperty(String name) => name != 'this';
206 }
207
208 /**
209 * A scope that holds a reference to a single variable. Polymer expressions
210 * introduce variables to the scope one at a time. Each time a variable is
211 * added, a new [_LocalVariableScope] is created.
212 */
213 class _LocalVariableScope extends Scope {
214 final Scope parent;
215 final String varName;
216 // TODO(sigmund,justinfagnani): make this @observable?
217 final Object value;
218
219 _LocalVariableScope(this.varName, this.value, this.parent) : super._() {
220 if (varName == 'this') {
221 throw new EvalException("'this' cannot be used as a variable name.");
182 } 222 }
183 } 223 }
184 224
185 Object ownerOf(String name) { 225 Object get model => parent != null ? parent.model : null;
186 if (name == 'this') { 226
187 // we could return the Scope if it were Observable, but since assigning 227 Object operator[](String name) {
188 // a model to a template destroys and recreates the instance, it doesn't 228 if (varName == name) return _convert(value);
189 // seem neccessary 229 if (parent != null) return parent[name];
190 return null; 230 throw new EvalException("variable '$name' not found");
191 } else if (_variables.containsKey(name)) { 231 }
192 return _variables; 232
193 } else if (smoke.hasGetter(model.runtimeType, smoke.nameToSymbol(name))) { 233 bool _isModelProperty(String name) {
194 return model; 234 if (varName == name) return false;
195 } 235 return parent == null ? false : parent._isModelProperty(name);
196 if (parent != null) { 236 }
197 return parent.ownerOf(name); 237 }
238
239 /** A scope that holds a reference to a global variables. */
240 class _GlobalsScope extends Scope {
241 final _ModelScope parent;
242 final Map<String, Object> variables;
243
244 _GlobalsScope(this.variables, this.parent) : super._() {
245 if (variables.containsKey('this')) {
246 throw new EvalException("'this' cannot be used as a variable name.");
198 } 247 }
199 } 248 }
200 249
201 bool contains(String name) { 250 Object get model => parent != null ? parent.model : null;
202 if (_variables.containsKey(name) || 251
203 smoke.hasGetter(model.runtimeType, smoke.nameToSymbol(name))) { 252 Object operator[](String name) {
204 return true; 253 if (variables.containsKey(name)) return _convert(variables[name]);
205 } 254 if (parent != null) return parent[name];
206 if (parent != null) { 255 throw new EvalException("variable '$name' not found");
207 return parent.contains(name); 256 }
208 } 257
209 return false; 258 bool _isModelProperty(String name) {
259 if (variables.containsKey(name)) return false;
260 return parent == null ? false : parent._isModelProperty(name);
210 } 261 }
211 } 262 }
212 263
213 Object _convert(v) { 264 Object _convert(v) => v is Stream ? new StreamBinding(v) : v;
214 if (v is Stream) return new StreamBinding(v);
215 return v;
216 }
217 265
218 abstract class ExpressionObserver<E extends Expression> implements Expression { 266 abstract class ExpressionObserver<E extends Expression> implements Expression {
219 final E _expr; 267 final E _expr;
220 ExpressionObserver _parent; 268 ExpressionObserver _parent;
221 269
222 StreamSubscription _subscription; 270 StreamSubscription _subscription;
223 Object _value; 271 Object _value;
224 272
225 StreamController _controller = new StreamController.broadcast(); 273 StreamController _controller = new StreamController.broadcast();
226 Stream get onUpdate => _controller.stream; 274 Stream get onUpdate => _controller.stream;
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after
449 class IdentifierObserver extends ExpressionObserver<Identifier> 497 class IdentifierObserver extends ExpressionObserver<Identifier>
450 implements Identifier { 498 implements Identifier {
451 499
452 IdentifierObserver(Identifier value) : super(value); 500 IdentifierObserver(Identifier value) : super(value);
453 501
454 String get value => _expr.value; 502 String get value => _expr.value;
455 503
456 _updateSelf(Scope scope) { 504 _updateSelf(Scope scope) {
457 _value = scope[value]; 505 _value = scope[value];
458 506
459 var owner = scope.ownerOf(value); 507 if (!scope._isModelProperty(value)) return;
460 if (owner is Observable) { 508 var model = scope.model;
461 var symbol = smoke.nameToSymbol(value); 509 if (model is! Observable) return;
462 _subscription = (owner as Observable).changes.listen((changes) { 510 var symbol = smoke.nameToSymbol(value);
463 if (changes.any( 511 _subscription = (model as Observable).changes.listen((changes) {
464 (c) => c is PropertyChangeRecord && c.name == symbol)) { 512 if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) {
465 _invalidate(scope); 513 _invalidate(scope);
466 } 514 }
467 }); 515 });
468 }
469 } 516 }
470 517
471 accept(Visitor v) => v.visitIdentifier(this); 518 accept(Visitor v) => v.visitIdentifier(this);
472 } 519 }
473 520
474 class ParenthesizedObserver extends ExpressionObserver<ParenthesizedExpression> 521 class ParenthesizedObserver extends ExpressionObserver<ParenthesizedExpression>
475 implements ParenthesizedExpression { 522 implements ParenthesizedExpression {
476 final ExpressionObserver child; 523 final ExpressionObserver child;
477 524
478 ParenthesizedObserver(ParenthesizedExpression expr, this.child) : super(expr); 525 ParenthesizedObserver(ParenthesizedExpression expr, this.child) : super(expr);
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after
693 740
694 Comprehension(this.identifier, Iterable iterable) 741 Comprehension(this.identifier, Iterable iterable)
695 : iterable = (iterable != null) ? iterable : const []; 742 : iterable = (iterable != null) ? iterable : const [];
696 } 743 }
697 744
698 class EvalException implements Exception { 745 class EvalException implements Exception {
699 final String message; 746 final String message;
700 EvalException(this.message); 747 EvalException(this.message);
701 String toString() => "EvalException: $message"; 748 String toString() => "EvalException: $message";
702 } 749 }
OLDNEW
« no previous file with comments | « pkg/polymer_expressions/example/example.dart ('k') | pkg/polymer_expressions/lib/polymer_expressions.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698