anchor.diff:  }
   
   class Token {
  -  bool json;
  -  int index;
  -  String text;
  +  final int index;
  +  final String text;
  +
     var value;
     // Tokens should have one of these set.
     String opKey;
  
positive.code: };
  
  class DynamicParser implements Parser {
    Profiler _perf;
    Lexer _lexer;
    ParserBackend _b;
  
    DynamicParser(Profiler this._perf, Lexer this._lexer, ParserBackend this._b);
  
    primaryFromToken(Token token, parserError) {
      if (token.key != null) {
  
negative.code:     throw parserError("Internal Angular Error: Tokens should have keys, text or fns");
    }
  
    _parserError(String s, [Token t]) {
      if (t == null && !_tokens.isEmpty) t = _tokens[0];
      String location = t == null ?
      'the end of the expression' :
      'at column ${t.index + 1} in';
      return 'Parser Error: $s $location [$_text]';
    }
  
  
    Token _peekToken() {
      if (_tokens.length == 0)
        throw "Unexpected end of expression: " + _text;
      return _tokens[0];
    }
  
    Token _peek([String e1, String e2, String e3, String e4]) {
      if (_tokens.length > 0) {
        Token token = _tokens[0];
        String t = token.text;
        if (t==e1 || t==e2 || t==e3 || t==e4 ||
        (e1 == null && e2 == null && e3 == null && e4 == null)) {
          return token;
        }
      }
      return null;
    }
  
    /**
     * Token savers are synchronous lists that allows Parser functions to
     * access the tokens parsed during some amount of time.  They are useful
     * for printing helpful debugging messages.
     */
    List<List<Token>> _tokenSavers;
    List<Token> _saveTokens() { var n = []; _tokenSavers.add(n); return n; }
    _stopSavingTokens(x) { if (!_tokenSavers.remove(x)) { throw 'bad token saver'; } return x; }
    _tokensText(List x) => x.map((x) => x.text).join();
  
  
    Token _expect([String e1, String e2, String e3, String e4]){
      Token token = _peek(e1, e2, e3, e4);
      if (token != null) {
        var consumed = _tokens.removeAt(0);
        _tokenSavers.forEach((ts) => ts.add(consumed));
        return token;
      }
      return null;
    }
  
    ParserAST _consume(e1){
      if (_expect(e1) == null) {
        throw _parserError("Missing expected $e1");
      }
    }
  
    ParserAST _primary() {
      var primary;
      var ts = _saveTokens();
      if (_expect('(') != null) {
        primary = _filterChain();
        _consume(')');
      } else if (_expect('[') != null) {
        primary = _arrayDeclaration();
      } else if (_expect('{') != null) {
        primary = _object();
      } else {
        Token token = _expect();
        primary = primaryFromToken(token, _parserError);
        if (primary == null) {
          throw _parserError("Internal Angular Error: Unreachable code A.");
        }
      }
  
      var next;
      while ((next = _expect('(', '[', '.')) != null) {
        if (next.text == '(') {
          primary = _functionCall(primary, _tokensText(ts.sublist(0, ts.length - 1)));
        } else if (next.text == '[') {
          primary = _objectIndex(primary);
        } else if (next.text == '.') {
          primary = _fieldAccess(primary);
        } else {
          throw _parserError("Internal Angular Error: Unreachable code B.");
        }
      }
      _stopSavingTokens(ts);
      return primary;
    }
  
    ParserAST _binaryFn(ParserAST left, String op, ParserAST right) =>
        _b.binaryFn(left, op, right);
  
    ParserAST _unaryFn(String op, ParserAST right) =>
        _b.unaryFn(op, right);
  
    ParserAST _unary() {
      var token;
      if (_expect('+') != null) {
        return _primary();
      } else if ((token = _expect('-')) != null) {
        return _binaryFn(_b.zero(), token.opKey, _unary());
      } else if ((token = _expect('!')) != null) {
        return _unaryFn(token.opKey, _unary());
      } else {
        return _primary();
      }
    }
  
    ParserAST _multiplicative() {
      var left = _unary();
      var token;
      while ((token = _expect('*','/','%')) != null) {
        left = _binaryFn(left, token.opKey, _unary());
      }
      return left;
    }
  
    ParserAST _additive() {
      var left = _multiplicative();
      var token;
      while ((token = _expect('+','-')) != null) {
        left = _binaryFn(left, token.opKey, _multiplicative());
      }
      return left;
    }
  
    ParserAST _relational() {
      var left = _additive();
      var token;
      if ((token = _expect('<', '>', '<=', '>=')) != null) {
        left = _binaryFn(left, token.opKey, _relational());
      }
      return left;
    }
  
    ParserAST _equality() {
      var left = _relational();
      var token;
      if ((token = _expect('==','!=')) != null) {
        left = _binaryFn(left, token.opKey, _equality());
      }
      return left;
    }
  
    ParserAST _logicalAND() {
      var left = _equality();
      var token;
      if ((token = _expect('&&')) != null) {
        left = _binaryFn(left, token.opKey, _logicalAND());
      }
      return left;
    }
  
    ParserAST _logicalOR() {
      var left = _logicalAND();
      var token;
      while(true) {
        if ((token = _expect('||')) != null) {
          left = _binaryFn(left, token.opKey, _logicalAND());
        } else {
          return left;
        }
      }
    }
  
    ParserAST _assignment() {
      var ts = _saveTokens();
      var left = _logicalOR();
      _stopSavingTokens(ts);
      var right;
      var token;
      if ((token = _expect('=')) != null) {
        if (!left.assignable) {
          throw _parserError('Expression ${_tokensText(ts)} is not assignable', token);
        }
        right = _logicalOR();
        return _b.assignment(left, right, _evalError);
      } else {
        return left;
      }
    }
  
  
    ParserAST _expression() {
      return _assignment();
    }
  
    _filterChain() {
      var left = _expression();
      var token;
      while(true) {
        if ((token = _expect('|')) != null) {
          //left = binaryFn(left, token.opKey, filter());
          throw _parserError("Filters are not implemented", token);
        } else {
          return left;
        }
      }
    }
  
    _statements() {
      List<ParserAST> statements = [];
      while (true) {
        if (_tokens.length > 0 && _peek('}', ')', ';', ']') == null)
          statements.add(_filterChain());
        if (_expect(';') == null) {
          return statements.length == 1
          ? statements[0]
          : _b.multipleStatements(statements);
        }
      }
    }
  
    _functionCall(fn, fnName) {
      var argsFn = [];
      if (_peekToken().text != ')') {
        do {
          argsFn.add(_expression());
        } while (_expect(',') != null);
      }
      _consume(')');
      return _b.functionCall(fn, fnName, argsFn, _evalError);
    }
  
    // This is used with json array declaration
    _arrayDeclaration() {
      var elementFns = [];
      if (_peekToken().text != ']') {
        do {
          elementFns.add(_expression());
        } while (_expect(',') != null);
      }
      _consume(']');
      return _b.arrayDeclaration(elementFns);
    }
  
    _objectIndex(obj) {
      var indexFn = _expression();
      _consume(']');
      return _b.objectIndex(obj, indexFn, _evalError);
    }
  
    _fieldAccess(object) {
      var field = _expect().text;
      //var getter = getter(field);
      return _b.fieldAccess(object, field);
    }
  
    _object() {
      var keyValues = [];
      if (_peekToken().text != '}') {
        do {
          var token = _expect(),
          key = token.value != null && token.value is String ? token.value : token.text;
          _consume(":");
          var value = _expression();
          keyValues.add({"key":key, "value":value});
        } while (_expect(',') != null);
      }
      _consume('}');
      return _b.object(keyValues);
    }
  
  }
  