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

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 the variable set (even if these are global
155 * Scopes can be nested by giving them a [parent]. If a name in not found in a 151 * variables), otherwise it 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 if (variables == null) return scope;
160 variables.forEach((name, value) {
161 scope = new _VariableScope(name, value, scope);
Jennifer Messerly 2014/03/21 20:08:54 hmmm. Is the Map used for globals? We might want s
Siggi Cherem (dart-lang) 2014/03/21 21:09:47 good point. Added _GlobalsScope for this.
162 });
163 return scope;
164 }
165
166 /** Return the unique model in this scope. */
167 Object get model;
168
169 /**
170 * Lookup the value of [name] in the current scope. If [name] is 'this', then
171 * we return the [model]. For any other name, this finds the first variable
172 * matching [name] or, if none exists, the property [name] in the [model].
173 */
174 Object operator[](String name);
175
176 /**
177 * Return the object that defines the value of [name]. The result may be a
178 * [_VariableScope] if it is a variable on this scope, [model] if it is a
179 * member of the model object, a [_ModelScope] if name is 'this', or null if
180 * the name can't be found and [model] is null.
Siggi Cherem (dart-lang) 2014/03/21 01:43:46 Justin - a general question here: I was wondering
Siggi Cherem (dart-lang) 2014/03/21 21:09:47 BWT - after adding the GlobalsScope and with the d
181 */
182 Object _ownerOf(String name);
183
184 /** Create a new scope extending this scope with an additional variable. */
185 Scope childScope(String name, Object value) =>
186 new _VariableScope(name, value, this);
187 }
188
189 /**
190 * 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
192 * object. Any variables added in scope or global variables are added as child
193 * scopes.
194 */
195 class _ModelScope extends Scope {
160 final Object model; 196 final Object model;
161 // TODO(justinfagnani): disallow adding/removing names
162 final ObservableMap<String, Object> _variables;
163 197
164 Scope({this.model, Map<String, Object> variables, this.parent}) 198 _ModelScope(this.model) : super._();
165 : _variables = new ObservableMap.from(variables == null ? {} : variables);
166 199
167 Object operator[](String name) { 200 Object operator[](String name) {
168 if (name == 'this') { 201 if (name == 'this') return model;
169 return model; 202 var symbol = smoke.nameToSymbol(name);
170 } else if (_variables.containsKey(name)) { 203 if (model == null || symbol == null) {
171 return _convert(_variables[name]); 204 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 } 205 }
178 if (parent != null) { 206 return _convert(smoke.read(model, symbol));
179 return _convert(parent[name]); 207 }
180 } else { 208
181 throw new EvalException("variable '$name' not found"); 209 Object _ownerOf(String name) => name == 'this' ? this : model;
210 }
211
212 /**
213 * 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
215 * added, a new [_VariableScope] is created.
216 */
217 class _VariableScope extends Scope {
218 final Scope parent;
219 final String varName;
220 // TODO(sigmund,justinfagnani): make this @observable?
221 final Object value;
Siggi Cherem (dart-lang) 2014/03/21 01:43:46 I had this with @observable first (matching what y
Jennifer Messerly 2014/03/21 20:08:54 Not sure I understand the TODO. Shouldn't values a
Siggi Cherem (dart-lang) 2014/03/21 21:09:47 I was thinking if we were to make those constructs
Jennifer Messerly 2014/03/22 00:37:56 It doesn't currently reuse nodes if the model chan
222
223 _VariableScope(this.varName, this.value, this.parent) : super._() {
224 if (varName == 'this') {
225 throw new EvalException("'this' cannot be used as a variable name.");
182 } 226 }
183 } 227 }
184 228
185 Object ownerOf(String name) { 229 Object get model => parent != null ? parent.model : null;
186 if (name == 'this') { 230
187 // we could return the Scope if it were Observable, but since assigning 231 Object operator[](String name) {
188 // a model to a template destroys and recreates the instance, it doesn't 232 if (varName == name) return _convert(value);
189 // seem neccessary 233 if (parent != null) return parent[name];
Siggi Cherem (dart-lang) 2014/03/21 01:43:46 (see comment re _ownerOf)
190 return null; 234 throw new EvalException("variable '$name' not found");
191 } else if (_variables.containsKey(name)) {
192 return _variables;
193 } else if (smoke.hasGetter(model.runtimeType, smoke.nameToSymbol(name))) {
194 return model;
195 }
196 if (parent != null) {
197 return parent.ownerOf(name);
198 }
199 } 235 }
200 236
201 bool contains(String name) { 237 Object _ownerOf(String name) {
202 if (_variables.containsKey(name) || 238 if (varName == name) return this;
203 smoke.hasGetter(model.runtimeType, smoke.nameToSymbol(name))) { 239 if (parent != null) return parent._ownerOf(name);
204 return true; 240 return null;
205 }
206 if (parent != null) {
207 return parent.contains(name);
208 }
209 return false;
210 } 241 }
211 } 242 }
212 243
213 Object _convert(v) { 244 Object _convert(v) => v is Stream ? new StreamBinding(v) : v;
214 if (v is Stream) return new StreamBinding(v);
215 return v;
216 }
217 245
218 abstract class ExpressionObserver<E extends Expression> implements Expression { 246 abstract class ExpressionObserver<E extends Expression> implements Expression {
219 final E _expr; 247 final E _expr;
220 ExpressionObserver _parent; 248 ExpressionObserver _parent;
221 249
222 StreamSubscription _subscription; 250 StreamSubscription _subscription;
223 Object _value; 251 Object _value;
224 252
225 StreamController _controller = new StreamController.broadcast(); 253 StreamController _controller = new StreamController.broadcast();
226 Stream get onUpdate => _controller.stream; 254 Stream get onUpdate => _controller.stream;
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after
449 class IdentifierObserver extends ExpressionObserver<Identifier> 477 class IdentifierObserver extends ExpressionObserver<Identifier>
450 implements Identifier { 478 implements Identifier {
451 479
452 IdentifierObserver(Identifier value) : super(value); 480 IdentifierObserver(Identifier value) : super(value);
453 481
454 String get value => _expr.value; 482 String get value => _expr.value;
455 483
456 _updateSelf(Scope scope) { 484 _updateSelf(Scope scope) {
457 _value = scope[value]; 485 _value = scope[value];
458 486
459 var owner = scope.ownerOf(value); 487 var owner = scope._ownerOf(value);
460 if (owner is Observable) { 488 if (owner is Observable) {
461 var symbol = smoke.nameToSymbol(value); 489 var symbol = smoke.nameToSymbol(value);
462 _subscription = (owner as Observable).changes.listen((changes) { 490 _subscription = (owner as Observable).changes.listen((changes) {
463 if (changes.any( 491 if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) {
464 (c) => c is PropertyChangeRecord && c.name == symbol)) {
465 _invalidate(scope); 492 _invalidate(scope);
466 } 493 }
467 }); 494 });
468 } 495 }
469 } 496 }
470 497
471 accept(Visitor v) => v.visitIdentifier(this); 498 accept(Visitor v) => v.visitIdentifier(this);
472 } 499 }
473 500
474 class ParenthesizedObserver extends ExpressionObserver<ParenthesizedExpression> 501 class ParenthesizedObserver extends ExpressionObserver<ParenthesizedExpression>
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
693 720
694 Comprehension(this.identifier, Iterable iterable) 721 Comprehension(this.identifier, Iterable iterable)
695 : iterable = (iterable != null) ? iterable : const []; 722 : iterable = (iterable != null) ? iterable : const [];
696 } 723 }
697 724
698 class EvalException implements Exception { 725 class EvalException implements Exception {
699 final String message; 726 final String message;
700 EvalException(this.message); 727 EvalException(this.message);
701 String toString() => "EvalException: $message"; 728 String toString() => "EvalException: $message";
702 } 729 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698