| Index: dart/pkg/polymer_expressions/lib/eval.dart
|
| ===================================================================
|
| --- dart/pkg/polymer_expressions/lib/eval.dart (revision 37358)
|
| +++ dart/pkg/polymer_expressions/lib/eval.dart (working copy)
|
| @@ -16,20 +16,22 @@
|
| import 'visitor.dart';
|
|
|
| final _BINARY_OPERATORS = {
|
| - '+': (a, b) => a + b,
|
| - '-': (a, b) => a - b,
|
| - '*': (a, b) => a * b,
|
| - '/': (a, b) => a / b,
|
| - '%': (a, b) => a % b,
|
| - '==': (a, b) => a == b,
|
| - '!=': (a, b) => a != b,
|
| - '>': (a, b) => a > b,
|
| - '>=': (a, b) => a >= b,
|
| - '<': (a, b) => a < b,
|
| - '<=': (a, b) => a <= b,
|
| - '||': (a, b) => a || b,
|
| - '&&': (a, b) => a && b,
|
| - '|': (a, f) {
|
| + '+': (a, b) => a + b,
|
| + '-': (a, b) => a - b,
|
| + '*': (a, b) => a * b,
|
| + '/': (a, b) => a / b,
|
| + '%': (a, b) => a % b,
|
| + '==': (a, b) => a == b,
|
| + '!=': (a, b) => a != b,
|
| + '===': (a, b) => identical(a, b),
|
| + '!==': (a, b) => !identical(a, b),
|
| + '>': (a, b) => a > b,
|
| + '>=': (a, b) => a >= b,
|
| + '<': (a, b) => a < b,
|
| + '<=': (a, b) => a <= b,
|
| + '||': (a, b) => a || b,
|
| + '&&': (a, b) => a && b,
|
| + '|': (a, f) {
|
| if (f is Transformer) return f.forward(a);
|
| if (f is Filter) return f(a);
|
| throw new EvalException("Filters must be a one-argument function.");
|
| @@ -47,11 +49,7 @@
|
| /**
|
| * Evaluation [expr] in the context of [scope].
|
| */
|
| -Object eval(Expression expr, Scope scope) {
|
| - var observer = observe(expr, scope);
|
| - new Updater(scope).visit(observer);
|
| - return observer._value;
|
| -}
|
| +Object eval(Expression expr, Scope scope) => new EvalVisitor(scope).visit(expr);
|
|
|
| /**
|
| * Returns an [ExpressionObserver] that evaluates [expr] in the context of
|
| @@ -152,9 +150,6 @@
|
| * and then finally looks up the name as a property in the model.
|
| */
|
| abstract class Scope implements Indexable<String, Object> {
|
| - static int __seq = 1;
|
| - final int _seq = __seq++;
|
| -
|
| Scope._();
|
|
|
| /** Create a scope containing a [model] and all of [variables]. */
|
| @@ -188,9 +183,6 @@
|
| /** Create a new scope extending this scope with an additional variable. */
|
| Scope childScope(String name, Object value) =>
|
| new _LocalVariableScope(name, value, this);
|
| -
|
| - String toString() => 'Scope(seq: $_seq model: $model)';
|
| -
|
| }
|
|
|
| /**
|
| @@ -307,13 +299,16 @@
|
| }
|
| }
|
|
|
| - _observe(Scope scope) {
|
| - // unobserve last value
|
| + _unobserve() {
|
| if (_subscription != null) {
|
| _subscription.cancel();
|
| _subscription = null;
|
| }
|
| + }
|
|
|
| + _observe(Scope scope) {
|
| + _unobserve();
|
| +
|
| var _oldValue = _value;
|
|
|
| // evaluate
|
| @@ -337,6 +332,110 @@
|
| }
|
| }
|
|
|
| +class Closer extends RecursiveVisitor {
|
| + static final _instance = new Closer._();
|
| + factory Closer() => _instance;
|
| + Closer._();
|
| +
|
| + visitExpression(ExpressionObserver e) {
|
| + e._unobserve();
|
| + }
|
| +}
|
| +
|
| +class EvalVisitor extends Visitor {
|
| + final Scope scope;
|
| +
|
| + EvalVisitor(this.scope);
|
| +
|
| + visitEmptyExpression(EmptyExpression e) => scope.model;
|
| +
|
| + visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child);
|
| +
|
| + visitGetter(Getter g) {
|
| + var receiver = visit(g.receiver);
|
| + if (receiver == null) return null;
|
| + var symbol = smoke.nameToSymbol(g.name);
|
| + return smoke.read(receiver, symbol);
|
| + }
|
| +
|
| + visitIndex(Index i) {
|
| + var receiver = visit(i.receiver);
|
| + if (receiver == null) return null;
|
| + var key = visit(i.argument);
|
| + return receiver[key];
|
| + }
|
| +
|
| + visitInvoke(Invoke i) {
|
| + var receiver = visit(i.receiver);
|
| + if (receiver == null) return null;
|
| + var args = (i.arguments == null)
|
| + ? null
|
| + : i.arguments.map(visit).toList(growable: false);
|
| +
|
| + if (i.method == null) {
|
| + assert(receiver is Function);
|
| + return Function.apply(receiver, args);
|
| + }
|
| +
|
| + var symbol = smoke.nameToSymbol(i.method);
|
| + return smoke.invoke(receiver, symbol, args);
|
| + }
|
| +
|
| + visitLiteral(Literal l) => l.value;
|
| +
|
| + visitListLiteral(ListLiteral l) => l.items.map(visit).toList();
|
| +
|
| + visitMapLiteral(MapLiteral l) {
|
| + var map = {};
|
| + for (var entry in l.entries) {
|
| + var key = visit(entry.key);
|
| + var value = visit(entry.entryValue);
|
| + map[key] = value;
|
| + }
|
| + return map;
|
| + }
|
| +
|
| + visitMapLiteralEntry(MapLiteralEntry e) =>
|
| + throw new UnsupportedError("should never be called");
|
| +
|
| + visitIdentifier(Identifier i) => scope[i.value];
|
| +
|
| + visitBinaryOperator(BinaryOperator o) {
|
| + var operator = o.operator;
|
| + var left = visit(o.left);
|
| + var right = visit(o.right);
|
| +
|
| + var f = _BINARY_OPERATORS[operator];
|
| + if (operator == '&&' || operator == '||') {
|
| + // TODO: short-circuit
|
| + return f(_toBool(left), _toBool(right));
|
| + } else if (operator == '==' || operator == '!=') {
|
| + return f(left, right);
|
| + } else if (left == null || right == null) {
|
| + return null;
|
| + }
|
| + return f(left, right);
|
| + }
|
| +
|
| + visitUnaryOperator(UnaryOperator o) {
|
| + var expr = visit(o.child);
|
| + var f = _UNARY_OPERATORS[o.operator];
|
| + if (o.operator == '!') {
|
| + return f(_toBool(expr));
|
| + }
|
| + return (expr == null) ? null : f(expr);
|
| + }
|
| +
|
| + visitTernaryOperator(TernaryOperator o) =>
|
| + visit(o.condition) == true ? visit(o.trueExpr) : visit(o.falseExpr);
|
| +
|
| + visitInExpression(InExpression i) =>
|
| + throw new UnsupportedError("can't eval an 'in' expression");
|
| +
|
| + visitAsExpression(AsExpression i) =>
|
| + throw new UnsupportedError("can't eval an 'as' expression");
|
| +}
|
| +
|
| class ObserverBuilder extends Visitor {
|
| final Queue parents = new Queue();
|
|
|
| @@ -470,7 +569,7 @@
|
| ListLiteralObserver(ListLiteral value, this.items) : super(value);
|
|
|
| _updateSelf(Scope scope) {
|
| - _value = items.map((i) => i._value).toList(growable: false);
|
| + _value = items.map((i) => i._value).toList();
|
| }
|
|
|
| accept(Visitor v) => v.visitListLiteral(this);
|
|
|