class DynamicParser implements Parser {
final Lexer _lexer;
final ParserBackend _b;
DynamicParser(Lexer this._lexer, ParserBackend this._b);
List<Token> _tokens;
String _text;
var _evalError;
ParserAST call(String text) {
try {
if (text == null) text = '';
_tokenSavers = [];
_text = text;
_tokens = _lexer.call(text);
_evalError = (String s, [stack]) => parserEvalError(s, text, stack);
ParserAST value = _statements();
if (_tokens.length != 0) {
throw _parserError("Unconsumed token ${_tokens[0].text}");
}
return value;
} finally {
_tokens = null;
_text = null;
_evalError = null;
_tokenSavers = null;
}
}
primaryFromToken(Token token, parserError) {
if (token.key != null) {
return _b.getterSetter(token.key);
}
if (token.opKey != null) {
return _b.fromOperator(token.opKey);
}
if (token.value != null) {
return _b.value(token.value);
}
if (token.text != null) {
return _b.value(token.text);
}
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 = _filter(left);
} else {
return left;
}
}
}
ParserAST _filter(ParserAST left) {
var token = _expect();
var filterName = token.text;
var argsFn = [];
while(true) {
if ((token = _expect(':')) != null) {
argsFn.add(_expression());
} else {
return _b.filter(filterName, left, argsFn, _evalError);
}
}
}
_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);
}
}