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

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

Issue 335943003: merge to trunk all changes from 36817 until 37378 under the packages: polymer, (Closed) Base URL: http://dart.googlecode.com/svn/trunk/dart/
Patch Set: 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/polymer_expressions/CHANGELOG.md ('k') | pkg/polymer_expressions/lib/parser.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';
11 import 'package:smoke/smoke.dart' as smoke; 11 import 'package:smoke/smoke.dart' as smoke;
12 12
13 import 'async.dart'; 13 import 'async.dart';
14 import 'expression.dart'; 14 import 'expression.dart';
15 import 'filter.dart'; 15 import 'filter.dart';
16 import 'visitor.dart'; 16 import 'visitor.dart';
17 17
18 final _BINARY_OPERATORS = { 18 final _BINARY_OPERATORS = {
19 '+': (a, b) => a + b, 19 '+': (a, b) => a + b,
20 '-': (a, b) => a - b, 20 '-': (a, b) => a - b,
21 '*': (a, b) => a * b, 21 '*': (a, b) => a * b,
22 '/': (a, b) => a / b, 22 '/': (a, b) => a / b,
23 '%': (a, b) => a % b, 23 '%': (a, b) => a % b,
24 '==': (a, b) => a == b, 24 '==': (a, b) => a == b,
25 '!=': (a, b) => a != b, 25 '!=': (a, b) => a != b,
26 '>': (a, b) => a > b, 26 '===': (a, b) => identical(a, b),
27 '>=': (a, b) => a >= b, 27 '!==': (a, b) => !identical(a, b),
28 '<': (a, b) => a < b, 28 '>': (a, b) => a > b,
29 '<=': (a, b) => a <= b, 29 '>=': (a, b) => a >= b,
30 '||': (a, b) => a || b, 30 '<': (a, b) => a < b,
31 '&&': (a, b) => a && b, 31 '<=': (a, b) => a <= b,
32 '|': (a, f) { 32 '||': (a, b) => a || b,
33 '&&': (a, b) => a && b,
34 '|': (a, f) {
33 if (f is Transformer) return f.forward(a); 35 if (f is Transformer) return f.forward(a);
34 if (f is Filter) return f(a); 36 if (f is Filter) return f(a);
35 throw new EvalException("Filters must be a one-argument function."); 37 throw new EvalException("Filters must be a one-argument function.");
36 } 38 }
37 }; 39 };
38 40
39 final _UNARY_OPERATORS = { 41 final _UNARY_OPERATORS = {
40 '+': (a) => a, 42 '+': (a) => a,
41 '-': (a) => -a, 43 '-': (a) => -a,
42 '!': (a) => !a, 44 '!': (a) => !a,
43 }; 45 };
44 46
45 final _BOOLEAN_OPERATORS = ['!', '||', '&&']; 47 final _BOOLEAN_OPERATORS = ['!', '||', '&&'];
46 48
47 /** 49 /**
48 * Evaluation [expr] in the context of [scope]. 50 * Evaluation [expr] in the context of [scope].
49 */ 51 */
50 Object eval(Expression expr, Scope scope) { 52 Object eval(Expression expr, Scope scope) => new EvalVisitor(scope).visit(expr);
51 var observer = observe(expr, scope);
52 new Updater(scope).visit(observer);
53 return observer._value;
54 }
55 53
56 /** 54 /**
57 * Returns an [ExpressionObserver] that evaluates [expr] in the context of 55 * Returns an [ExpressionObserver] that evaluates [expr] in the context of
58 * scope] and listens for any changes on [Observable] values that are 56 * scope] and listens for any changes on [Observable] values that are
59 * returned from sub-expressions. When a value changes the expression is 57 * returned from sub-expressions. When a value changes the expression is
60 * reevaluated and the new result is sent to the [onUpdate] stream of the 58 * reevaluated and the new result is sent to the [onUpdate] stream of the
61 * [ExpressionObsserver]. 59 * [ExpressionObsserver].
62 */ 60 */
63 ExpressionObserver observe(Expression expr, Scope scope) { 61 ExpressionObserver observe(Expression expr, Scope scope) {
64 var observer = new ObserverBuilder().visit(expr); 62 var observer = new ObserverBuilder().visit(expr);
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
145 143
146 144
147 /** 145 /**
148 * A scope in polymer expressions that can map names to objects. Scopes contain 146 * 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 147 * 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 148 * is then used to lookup names using the `[]` operator. The lookup first
151 * searches for the name in local variables, then in global variables, 149 * 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. 150 * and then finally looks up the name as a property in the model.
153 */ 151 */
154 abstract class Scope implements Indexable<String, Object> { 152 abstract class Scope implements Indexable<String, Object> {
155 static int __seq = 1;
156 final int _seq = __seq++;
157
158 Scope._(); 153 Scope._();
159 154
160 /** Create a scope containing a [model] and all of [variables]. */ 155 /** Create a scope containing a [model] and all of [variables]. */
161 factory Scope({Object model, Map<String, Object> variables}) { 156 factory Scope({Object model, Map<String, Object> variables}) {
162 var scope = new _ModelScope(model); 157 var scope = new _ModelScope(model);
163 return variables == null ? scope 158 return variables == null ? scope
164 : new _GlobalsScope(new Map<String, Object>.from(variables), scope); 159 : new _GlobalsScope(new Map<String, Object>.from(variables), scope);
165 } 160 }
166 161
167 /** Return the unique model in this scope. */ 162 /** Return the unique model in this scope. */
(...skipping 13 matching lines...) Expand all
181 /** 176 /**
182 * Returns whether [name] is defined in [model], that is, a lookup 177 * Returns whether [name] is defined in [model], that is, a lookup
183 * would not find a variable with that name, but there is a non-null model 178 * would not find a variable with that name, but there is a non-null model
184 * where we can look it up as a property. 179 * where we can look it up as a property.
185 */ 180 */
186 bool _isModelProperty(String name); 181 bool _isModelProperty(String name);
187 182
188 /** Create a new scope extending this scope with an additional variable. */ 183 /** Create a new scope extending this scope with an additional variable. */
189 Scope childScope(String name, Object value) => 184 Scope childScope(String name, Object value) =>
190 new _LocalVariableScope(name, value, this); 185 new _LocalVariableScope(name, value, this);
191
192 String toString() => 'Scope(seq: $_seq model: $model)';
193
194 } 186 }
195 187
196 /** 188 /**
197 * A scope that looks up names in a model object. This kind of scope has no 189 * A scope that looks up names in a model object. This kind of scope has no
198 * parent scope because all our lookup operations stop when we reach the model 190 * parent scope because all our lookup operations stop when we reach the model
199 * object. Any variables added in scope or global variables are added as child 191 * object. Any variables added in scope or global variables are added as child
200 * scopes. 192 * scopes.
201 */ 193 */
202 class _ModelScope extends Scope { 194 class _ModelScope extends Scope {
203 final Object model; 195 final Object model;
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
300 292
301 _updateSelf(Scope scope) {} 293 _updateSelf(Scope scope) {}
302 294
303 _invalidate(Scope scope) { 295 _invalidate(Scope scope) {
304 _observe(scope); 296 _observe(scope);
305 if (_parent != null) { 297 if (_parent != null) {
306 _parent._invalidate(scope); 298 _parent._invalidate(scope);
307 } 299 }
308 } 300 }
309 301
302 _unobserve() {
303 if (_subscription != null) {
304 _subscription.cancel();
305 _subscription = null;
306 }
307 }
308
310 _observe(Scope scope) { 309 _observe(Scope scope) {
311 // unobserve last value 310 _unobserve();
312 if (_subscription != null) {
313 _subscription.cancel();
314 _subscription = null;
315 }
316 311
317 var _oldValue = _value; 312 var _oldValue = _value;
318 313
319 // evaluate 314 // evaluate
320 _updateSelf(scope); 315 _updateSelf(scope);
321 316
322 if (!identical(_value, _oldValue)) { 317 if (!identical(_value, _oldValue)) {
323 _controller.add(_value); 318 _controller.add(_value);
324 } 319 }
325 } 320 }
326 321
327 String toString() => _expr.toString(); 322 String toString() => _expr.toString();
328 } 323 }
329 324
330 class Updater extends RecursiveVisitor { 325 class Updater extends RecursiveVisitor {
331 final Scope scope; 326 final Scope scope;
332 327
333 Updater(this.scope); 328 Updater(this.scope);
334 329
335 visitExpression(ExpressionObserver e) { 330 visitExpression(ExpressionObserver e) {
336 e._observe(scope); 331 e._observe(scope);
337 } 332 }
338 } 333 }
339 334
335 class Closer extends RecursiveVisitor {
336 static final _instance = new Closer._();
337 factory Closer() => _instance;
338 Closer._();
339
340 visitExpression(ExpressionObserver e) {
341 e._unobserve();
342 }
343 }
344
345 class EvalVisitor extends Visitor {
346 final Scope scope;
347
348 EvalVisitor(this.scope);
349
350 visitEmptyExpression(EmptyExpression e) => scope.model;
351
352 visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child);
353
354 visitGetter(Getter g) {
355 var receiver = visit(g.receiver);
356 if (receiver == null) return null;
357 var symbol = smoke.nameToSymbol(g.name);
358 return smoke.read(receiver, symbol);
359 }
360
361 visitIndex(Index i) {
362 var receiver = visit(i.receiver);
363 if (receiver == null) return null;
364 var key = visit(i.argument);
365 return receiver[key];
366 }
367
368 visitInvoke(Invoke i) {
369 var receiver = visit(i.receiver);
370 if (receiver == null) return null;
371 var args = (i.arguments == null)
372 ? null
373 : i.arguments.map(visit).toList(growable: false);
374
375 if (i.method == null) {
376 assert(receiver is Function);
377 return Function.apply(receiver, args);
378 }
379
380 var symbol = smoke.nameToSymbol(i.method);
381 return smoke.invoke(receiver, symbol, args);
382 }
383
384 visitLiteral(Literal l) => l.value;
385
386 visitListLiteral(ListLiteral l) => l.items.map(visit).toList();
387
388 visitMapLiteral(MapLiteral l) {
389 var map = {};
390 for (var entry in l.entries) {
391 var key = visit(entry.key);
392 var value = visit(entry.entryValue);
393 map[key] = value;
394 }
395 return map;
396 }
397
398 visitMapLiteralEntry(MapLiteralEntry e) =>
399 throw new UnsupportedError("should never be called");
400
401 visitIdentifier(Identifier i) => scope[i.value];
402
403 visitBinaryOperator(BinaryOperator o) {
404 var operator = o.operator;
405 var left = visit(o.left);
406 var right = visit(o.right);
407
408 var f = _BINARY_OPERATORS[operator];
409 if (operator == '&&' || operator == '||') {
410 // TODO: short-circuit
411 return f(_toBool(left), _toBool(right));
412 } else if (operator == '==' || operator == '!=') {
413 return f(left, right);
414 } else if (left == null || right == null) {
415 return null;
416 }
417 return f(left, right);
418 }
419
420 visitUnaryOperator(UnaryOperator o) {
421 var expr = visit(o.child);
422 var f = _UNARY_OPERATORS[o.operator];
423 if (o.operator == '!') {
424 return f(_toBool(expr));
425 }
426 return (expr == null) ? null : f(expr);
427 }
428
429 visitTernaryOperator(TernaryOperator o) =>
430 visit(o.condition) == true ? visit(o.trueExpr) : visit(o.falseExpr);
431
432 visitInExpression(InExpression i) =>
433 throw new UnsupportedError("can't eval an 'in' expression");
434
435 visitAsExpression(AsExpression i) =>
436 throw new UnsupportedError("can't eval an 'as' expression");
437 }
438
340 class ObserverBuilder extends Visitor { 439 class ObserverBuilder extends Visitor {
341 final Queue parents = new Queue(); 440 final Queue parents = new Queue();
342 441
343 ObserverBuilder(); 442 ObserverBuilder();
344 443
345 visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e); 444 visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e);
346 445
347 visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child); 446 visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child);
348 447
349 visitGetter(Getter g) { 448 visitGetter(Getter g) {
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
463 } 562 }
464 563
465 class ListLiteralObserver extends ExpressionObserver<ListLiteral> 564 class ListLiteralObserver extends ExpressionObserver<ListLiteral>
466 implements ListLiteral { 565 implements ListLiteral {
467 566
468 final List<ExpressionObserver> items; 567 final List<ExpressionObserver> items;
469 568
470 ListLiteralObserver(ListLiteral value, this.items) : super(value); 569 ListLiteralObserver(ListLiteral value, this.items) : super(value);
471 570
472 _updateSelf(Scope scope) { 571 _updateSelf(Scope scope) {
473 _value = items.map((i) => i._value).toList(growable: false); 572 _value = items.map((i) => i._value).toList();
474 } 573 }
475 574
476 accept(Visitor v) => v.visitListLiteral(this); 575 accept(Visitor v) => v.visitListLiteral(this);
477 } 576 }
478 577
479 class MapLiteralObserver extends ExpressionObserver<MapLiteral> 578 class MapLiteralObserver extends ExpressionObserver<MapLiteral>
480 implements MapLiteral { 579 implements MapLiteral {
481 580
482 final List<MapLiteralEntryObserver> entries; 581 final List<MapLiteralEntryObserver> entries;
483 582
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after
714 accept(Visitor v) => v.visitInvoke(this); 813 accept(Visitor v) => v.visitInvoke(this);
715 } 814 }
716 815
717 _toBool(v) => (v == null) ? false : v; 816 _toBool(v) => (v == null) ? false : v;
718 817
719 class EvalException implements Exception { 818 class EvalException implements Exception {
720 final String message; 819 final String message;
721 EvalException(this.message); 820 EvalException(this.message);
722 String toString() => "EvalException: $message"; 821 String toString() => "EvalException: $message";
723 } 822 }
OLDNEW
« no previous file with comments | « pkg/polymer_expressions/CHANGELOG.md ('k') | pkg/polymer_expressions/lib/parser.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698