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

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

Issue 22950008: move fancy_syntax into Dart SVN (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 4 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/lib/async.dart ('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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library polymer_expressions.eval;
6
7 import 'dart:async';
8 import 'dart:collection';
9 import 'dart:mirrors';
10
11 import 'package:observe/observe.dart';
12
13 import 'async.dart';
14 import 'expression.dart';
15 import 'filter.dart';
16 import 'visitor.dart';
17 import 'src/mirrors.dart';
18
19 final _BINARY_OPERATORS = {
20 '+': (a, b) => a + b,
21 '-': (a, b) => a - b,
22 '*': (a, b) => a * b,
23 '/': (a, b) => a / b,
24 '==': (a, b) => a == b,
25 '!=': (a, b) => a != b,
26 '>': (a, b) => a > b,
27 '>=': (a, b) => a >= b,
28 '<': (a, b) => a < b,
29 '<=': (a, b) => a <= b,
30 '||': (a, b) => a || b,
31 '&&': (a, b) => a && b,
32 '|': (a, f) {
33 if (f is Transformer) return f.forward(a);
34 if (f is Filter) return f(a);
35 throw new EvalException("Filters must be a one-argument function.");
36 }
37 };
38
39 final _UNARY_OPERATORS = {
40 '+': (a) => a,
41 '-': (a) => -a,
42 '!': (a) => !a,
43 };
44
45 final _BOOLEAN_OPERATORS = ['!', '||', '&&'];
46
47 /**
48 * Evaluation [expr] in the context of [scope].
49 */
50 Object eval(Expression expr, Scope scope) => observe(expr, scope)._value;
51
52
53 ExpressionObserver observe(Expression expr, Scope scope) {
54 var observer = new ObserverBuilder(scope).visit(expr);
55 new Updater(scope).visit(observer);
56 return observer;
57 }
58
59 /**
60 * Assign [value] to the variable or field referenced by [expr] in the context
61 * of [scope].
62 *
63 * [expr] must be an /assignable/ expression, it must not contain
64 * operators or function invocations, and any index operations must use a
65 * literal index.
66 */
67 void assign(Expression expr, Object value, Scope scope) {
68
69 notAssignable() =>
70 throw new EvalException("Expression is not assignable: $expr");
71
72 Expression expression;
73 var property;
74 bool isIndex = false;
75 var filters = <Expression>[]; // reversed order for assignment
76
77 while (expr is BinaryOperator && expr.operator == '|') {
78 filters.add(expr.right);
79 expr = expr.left;
80 }
81
82 if (expr is Identifier) {
83 expression = empty();
84 property = expr.value;
85 } else if (expr is Invoke) {
86 expression = expr.receiver;
87 if (expr.method == '[]') {
88 if (expr.arguments[0] is! Literal) notAssignable();
89 Literal l = expr.arguments[0];
90 property = l.value;
91 isIndex = true;
92 } else if (expr.method != null) {
93 if (expr.arguments != null) notAssignable();
94 property = expr.method;
95 } else {
96 notAssignable();
97 }
98 } else {
99 notAssignable();
100 }
101
102 // transform the values backwards through the filters
103 for (var filterExpr in filters) {
104 var filter = eval(filterExpr, scope);
105 if (filter is! Transformer) {
106 throw new EvalException("filter must implement Transformer: $filterExpr");
107 }
108 value = filter.reverse(value);
109 }
110 // make the assignment
111 var o = eval(expression, scope);
112 if (o == null) throw new EvalException("Can't assign to null: $expression");
113 if (isIndex) {
114 o[property] = value;
115 } else {
116 reflect(o).setField(new Symbol(property), value);
117 }
118 }
119
120 /**
121 * A mapping of names to objects. Scopes contain a set of named [variables] and
122 * a single [model] object (which can be thought of as the "this" reference).
123 * Names are currently looked up in [variables] first, then the [model].
124 *
125 * Scopes can be nested by giving them a [parent]. If a name in not found in a
126 * Scope, it will look for it in it's parent.
127 */
128 class Scope extends Object {
129 final Scope parent;
130 final Object model;
131 // TODO(justinfagnani): disallow adding/removing names
132 final ObservableMap<String, Object> _variables;
133 InstanceMirror __modelMirror;
134
135 Scope({this.model, Map<String, Object> variables: const {}, this.parent})
136 : _variables = new ObservableMap.from(variables);
137
138 InstanceMirror get _modelMirror {
139 if (__modelMirror != null) return __modelMirror;
140 __modelMirror = reflect(model);
141 return __modelMirror;
142 }
143
144 Object operator[](String name) {
145 if (name == 'this') {
146 return model;
147 } else if (_variables.containsKey(name)) {
148 return _convert(_variables[name]);
149 } else if (model != null) {
150 var symbol = new Symbol(name);
151 var classMirror = _modelMirror.type;
152 var memberMirror = getMemberMirror(classMirror, symbol);
153 if (memberMirror is VariableMirror ||
154 (memberMirror is MethodMirror && memberMirror.isGetter)) {
155 return _convert(_modelMirror.getField(symbol).reflectee);
156 } else if (memberMirror is MethodMirror) {
157 return new Method(_modelMirror, symbol);
158 }
159 }
160 if (parent != null) {
161 return _convert(parent[name]);
162 } else {
163 throw new EvalException("variable not found: $name in $hashCode");
164 }
165 }
166
167 Object ownerOf(String name) {
168 if (name == 'this') {
169 // we could return the Scope if it were Observable, but since assigning
170 // a model to a template destroys and recreates the instance, it doesn't
171 // seem neccessary
172 return null;
173 } else if (_variables.containsKey(name)) {
174 return _variables;
175 } else {
176 var symbol = new Symbol(name);
177 var classMirror = _modelMirror.type;
178 if (getMemberMirror(classMirror, symbol) != null) {
179 return model;
180 }
181 }
182 if (parent != null) {
183 return parent.ownerOf(name);
184 }
185 }
186
187 bool contains(String name) {
188 if (_variables.containsKey(name)) {
189 return true;
190 } else {
191 var symbol = new Symbol(name);
192 var classMirror = _modelMirror.type;
193 if (getMemberMirror(classMirror, symbol) != null) {
194 return true;
195 }
196 }
197 if (parent != null) {
198 return parent.contains(name);
199 }
200 return false;
201 }
202
203 String toString() => 'Scope($hashCode $parent)';
204 }
205
206 Object _convert(v) {
207 if (v is Stream) return new StreamBinding(v);
208 return v;
209 }
210
211 abstract class ExpressionObserver<E extends Expression> implements Expression {
212 final E _expr;
213 ExpressionObserver _parent;
214
215 StreamSubscription _subscription;
216 Object _value;
217
218 StreamController _controller = new StreamController.broadcast();
219 Stream get onUpdate => _controller.stream;
220
221 ExpressionObserver(this._expr);
222
223 Object get currentValue => _value;
224
225 update(Scope scope) => _updateSelf(scope);
226
227 _updateSelf(Scope scope) {}
228
229 _invalidate(Scope scope) {
230 _observe(scope);
231 if (_parent != null) {
232 _parent._invalidate(scope);
233 }
234 }
235
236 _observe(Scope scope) {
237 // unobserve last value
238 if (_subscription != null) {
239 _subscription.cancel();
240 _subscription = null;
241 }
242
243 var _oldValue = _value;
244
245 // evaluate
246 _updateSelf(scope);
247
248 if (!identical(_value, _oldValue)) {
249 _controller.add(_value);
250 }
251 }
252
253 String toString() => _expr.toString();
254 }
255
256 class Updater extends RecursiveVisitor<ExpressionObserver> {
257 final Scope scope;
258
259 Updater(this.scope);
260
261 visitExpression(ExpressionObserver e) {
262 e._observe(scope);
263 }
264
265 visitInExpression(InObserver c) {
266 visit(c.right);
267 visitExpression(c);
268 }
269 }
270
271 class ObserverBuilder extends Visitor {
272 final Scope scope;
273 final Queue parents = new Queue();
274
275 ObserverBuilder(this.scope);
276
277 visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e);
278
279 visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child);
280
281 visitInvoke(Invoke i) {
282 var receiver = visit(i.receiver);
283 var args = (i.arguments == null)
284 ? null
285 : i.arguments.map(visit).toList(growable: false);
286 var invoke = new InvokeObserver(i, receiver, args);
287 receiver._parent = invoke;
288 if (args != null) args.forEach((a) => a._parent = invoke);
289 return invoke;
290 }
291
292 visitLiteral(Literal l) => new LiteralObserver(l);
293
294 visitMapLiteral(MapLiteral l) {
295 var entries = l.entries.map(visit).toList(growable: false);
296 var map = new MapLiteralObserver(l, entries);
297 entries.forEach((e) => e._parent = map);
298 return map;
299 }
300
301 visitMapLiteralEntry(MapLiteralEntry e) {
302 var key = visit(e.key);
303 var value = visit(e.entryValue);
304 var entry = new MapLiteralEntryObserver(e, key, value);
305 key._parent = entry;
306 value._parent = entry;
307 return entry;
308 }
309
310 visitIdentifier(Identifier i) => new IdentifierObserver(i);
311
312 visitBinaryOperator(BinaryOperator o) {
313 var left = visit(o.left);
314 var right = visit(o.right);
315 var binary = new BinaryObserver(o, left, right);
316 left._parent = binary;
317 right._parent = binary;
318 return binary;
319 }
320
321 visitUnaryOperator(UnaryOperator o) {
322 var expr = visit(o.child);
323 var unary = new UnaryObserver(o, expr);
324 expr._parent = unary;
325 return unary;
326 }
327
328 visitInExpression(InExpression i) {
329 // don't visit the left. It's an identifier, but we don't want to evaluate
330 // it, we just want to add it to the comprehension object
331 var left = visit(i.left);
332 var right = visit(i.right);
333 var inexpr = new InObserver(i, left, right);
334 right._parent = inexpr;
335 return inexpr;
336 }
337 }
338
339 class EmptyObserver extends ExpressionObserver<EmptyExpression>
340 implements EmptyExpression {
341
342 EmptyObserver(EmptyExpression value) : super(value);
343
344 _updateSelf(Scope scope) {
345 _value = scope.model;
346 // TODO(justin): listen for scope.model changes?
347 }
348
349 accept(Visitor v) => v.visitEmptyExpression(this);
350 }
351
352 class LiteralObserver extends ExpressionObserver<Literal> implements Literal {
353
354 LiteralObserver(Literal value) : super(value);
355
356 dynamic get value => _expr.value;
357
358 _updateSelf(Scope scope) {
359 _value = _expr.value;
360 }
361
362 accept(Visitor v) => v.visitLiteral(this);
363 }
364
365 class MapLiteralObserver extends ExpressionObserver<MapLiteral>
366 implements MapLiteral {
367
368 final List<MapLiteralEntryObserver> entries;
369
370 MapLiteralObserver(MapLiteral value, this.entries) : super(value);
371
372 _updateSelf(Scope scope) {
373 _value = entries.fold(new Map(),
374 (m, e) => m..[e.key._value] = e.entryValue._value);
375 }
376
377 accept(Visitor v) => v.visitMapLiteral(this);
378 }
379
380 class MapLiteralEntryObserver extends ExpressionObserver<MapLiteralEntry>
381 implements MapLiteralEntry {
382
383 final LiteralObserver key;
384 final ExpressionObserver entryValue;
385
386 MapLiteralEntryObserver(MapLiteralEntry value, this.key, this.entryValue)
387 : super(value);
388
389 accept(Visitor v) => v.visitMapLiteralEntry(this);
390 }
391
392 class IdentifierObserver extends ExpressionObserver<Identifier>
393 implements Identifier {
394
395 IdentifierObserver(Identifier value) : super(value);
396
397 dynamic get value => _expr.value;
398
399 _updateSelf(Scope scope) {
400 _value = scope[_expr.value];
401
402 var owner = scope.ownerOf(_expr.value);
403 if (owner is Observable) {
404 _subscription = (owner as Observable).changes.listen(
405 (List<ChangeRecord> changes) {
406 var symbol = new Symbol(_expr.value);
407 if (changes.any((c) => c.changes(symbol))) {
408 _invalidate(scope);
409 }
410 });
411 }
412 }
413
414 accept(Visitor v) => v.visitIdentifier(this);
415 }
416
417 class ParenthesizedObserver extends ExpressionObserver<ParenthesizedExpression>
418 implements ParenthesizedExpression {
419 final ExpressionObserver child;
420
421 ParenthesizedObserver(ExpressionObserver expr, this.child) : super(expr);
422
423
424 _updateSelf(Scope scope) {
425 _value = child._value;
426 }
427
428 accept(Visitor v) => v.visitParenthesizedExpression(this);
429 }
430
431 class UnaryObserver extends ExpressionObserver<UnaryOperator>
432 implements UnaryOperator {
433 final ExpressionObserver child;
434
435 UnaryObserver(UnaryOperator expr, this.child) : super(expr);
436
437 String get operator => _expr.operator;
438
439 _updateSelf(Scope scope) {
440 var f = _UNARY_OPERATORS[_expr.operator];
441 if (operator == '!') {
442 _value = f(_toBool(child._value));
443 } else {
444 _value = (child._value == null) ? null : f(child._value);
445 }
446 }
447
448 accept(Visitor v) => v.visitUnaryOperator(this);
449 }
450
451 class BinaryObserver extends ExpressionObserver<BinaryOperator>
452 implements BinaryOperator {
453
454 final ExpressionObserver left;
455 final ExpressionObserver right;
456
457 BinaryObserver(BinaryOperator expr, this.left, this.right)
458 : super(expr);
459
460 String get operator => _expr.operator;
461
462 _updateSelf(Scope scope) {
463 var f = _BINARY_OPERATORS[operator];
464 if (operator == '&&' || operator == '||') {
465 _value = f(_toBool(left._value), _toBool(right._value));
466 } else {
467 _value = (left._value == null || right._value == null)
468 ? null : f(left._value, right._value);
469 }
470 }
471
472 accept(Visitor v) => v.visitBinaryOperator(this);
473
474 }
475
476 class InvokeObserver extends ExpressionObserver<Invoke> implements Invoke {
477 final ExpressionObserver receiver;
478 List<ExpressionObserver> arguments;
479
480 InvokeObserver(Expression expr, this.receiver, [this.arguments])
481 : super(expr);
482
483 bool get isGetter => _expr.isGetter;
484
485 String get method => _expr.method;
486
487 _updateSelf(Scope scope) {
488 var args = (arguments == null)
489 ? []
490 : arguments.map((a) => a._value)
491 .toList(growable: false);
492 var receiverValue = receiver._value;
493 if (receiverValue == null) {
494 _value = null;
495 } else if (_expr.method == null) {
496 if (_expr.isGetter) {
497 // getter, but not a top-level identifier
498 // TODO(justin): listen to the receiver's owner
499 _value = receiverValue;
500 } else {
501 // top-level function or model method
502 // TODO(justin): listen to model changes to see if the method has
503 // changed? listen to the scope to see if the top-level method has
504 // changed?
505 assert(receiverValue is Function);
506 _value = call(receiverValue, args);
507 }
508 } else {
509 // special case [] because we don't need mirrors
510 if (_expr.method == '[]') {
511 assert(args.length == 1);
512 var key = args[0];
513 _value = receiverValue[key];
514
515 if (receiverValue is Observable) {
516 _subscription = (receiverValue as Observable).changes.listen(
517 (List<ChangeRecord> changes) {
518 if (changes.any((c) =>
519 c is MapChangeRecord && c.changes(key))) {
520 _invalidate(scope);
521 }
522 });
523 }
524 } else {
525 var mirror = reflect(receiverValue);
526 var symbol = new Symbol(_expr.method);
527 _value = (_expr.isGetter)
528 ? mirror.getField(symbol).reflectee
529 : mirror.invoke(symbol, args, null).reflectee;
530
531 if (receiverValue is Observable) {
532 _subscription = (receiverValue as Observable).changes.listen(
533 (List<ChangeRecord> changes) {
534 if (changes.any((c) => c.changes(symbol))) {
535 _invalidate(scope);
536 }
537 });
538 }
539 }
540 }
541 }
542
543 accept(Visitor v) => v.visitInvoke(this);
544 }
545
546 class InObserver extends ExpressionObserver<InExpression>
547 implements InExpression {
548 IdentifierObserver left;
549 ExpressionObserver right;
550
551 InObserver(Expression expr, this.left, this.right) : super(expr);
552
553 _updateSelf(Scope scope) {
554 Identifier identifier = left;
555 var iterable = right._value;
556
557 if (iterable is! Iterable && iterable != null) {
558 throw new EvalException("right side of 'in' is not an iterator");
559 }
560
561 if (iterable is ObservableList) {
562 _subscription = (iterable as ObservableList).changes.listen(
563 (List<ChangeRecord> changes) {
564 if (changes.any((c) => c is ListChangeRecord)) {
565 _invalidate(scope);
566 }
567 });
568 }
569
570 // TODO: make Comprehension observable and update it
571 _value = new Comprehension(identifier.value, iterable);
572 }
573
574 accept(Visitor v) => v.visitInExpression(this);
575 }
576
577 _toBool(v) => (v == null) ? false : v;
578
579 call(dynamic receiver, List args) {
580 if (receiver is Method) {
581 return
582 _convert(receiver.mirror.invoke(receiver.symbol, args, null).reflectee);
583 } else {
584 return _convert(Function.apply(receiver, args, null));
585 }
586 }
587
588 /**
589 * A comprehension declaration ("a in b").
590 */
591 class Comprehension {
592 final String identifier;
593 final Iterable iterable;
594 Comprehension(this.identifier, this.iterable);
595 }
596
597 /**
598 * A method on a model object in a [Scope].
599 */
600 class Method { //implements _FunctionWrapper {
601 final InstanceMirror mirror;
602 final Symbol symbol;
603
604 Method(this.mirror, this.symbol);
605
606 dynamic call(List args) => mirror.invoke(symbol, args, null).reflectee;
607 }
608
609 class EvalException implements Exception {
610 final String message;
611 EvalException(this.message);
612 String toString() => "EvalException: $message";
613 }
OLDNEW
« no previous file with comments | « pkg/polymer_expressions/lib/async.dart ('k') | pkg/polymer_expressions/lib/expression.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698