From c67af8a03819004c4aaa775805badd1e631af738 Mon Sep 17 00:00:00 2001 From: Igor Minar Date: Wed, 27 Oct 2010 15:20:05 -0700 Subject: rename src/Parser.js to src/parser.js --- src/Parser.js | 663 ---------------------------------------------------------- src/parser.js | 663 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 663 insertions(+), 663 deletions(-) delete mode 100644 src/Parser.js create mode 100644 src/parser.js (limited to 'src') diff --git a/src/Parser.js b/src/Parser.js deleted file mode 100644 index 8dc8f9c2..00000000 --- a/src/Parser.js +++ /dev/null @@ -1,663 +0,0 @@ -var OPERATORS = { - 'null':function(self){return _null;}, - 'true':function(self){return true;}, - 'false':function(self){return false;}, - $undefined:noop, - '+':function(self, a,b){return (isDefined(a)?a:0)+(isDefined(b)?b:0);}, - '-':function(self, a,b){return (isDefined(a)?a:0)-(isDefined(b)?b:0);}, - '*':function(self, a,b){return a*b;}, - '/':function(self, a,b){return a/b;}, - '%':function(self, a,b){return a%b;}, - '^':function(self, a,b){return a^b;}, - '=':function(self, a,b){return setter(self, a, b);}, - '==':function(self, a,b){return a==b;}, - '!=':function(self, a,b){return a!=b;}, - '<':function(self, a,b){return a':function(self, a,b){return a>b;}, - '<=':function(self, a,b){return a<=b;}, - '>=':function(self, a,b){return a>=b;}, - '&&':function(self, a,b){return a&&b;}, - '||':function(self, a,b){return a||b;}, - '&':function(self, a,b){return a&b;}, -// '|':function(self, a,b){return a|b;}, - '|':function(self, a,b){return b(self, a);}, - '!':function(self, a){return !a;} -}; -var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; - -function lex(text, parseStringsForObjects){ - var dateParseLength = parseStringsForObjects ? 20 : -1, - tokens = [], - token, - index = 0, - json = [], - ch, - lastCh = ':'; // can start regexp - - while (index < text.length) { - ch = text.charAt(index); - if (is('"\'')) { - readString(ch); - } else if (isNumber(ch) || is('.') && isNumber(peek())) { - readNumber(); - } else if ( was('({[:,;') && is('/') ) { - readRegexp(); - } else if (isIdent(ch)) { - readIdent(); - if (was('{,') && json[0]=='{' && - (token=tokens[tokens.length-1])) { - token.json = token.text.indexOf('.') == -1; - } - } else if (is('(){}[].,;:')) { - tokens.push({index:index, text:ch, json:is('{}[]:,')}); - if (is('{[')) json.unshift(ch); - if (is('}]')) json.shift(); - index++; - } else if (isWhitespace(ch)) { - index++; - continue; - } else { - var ch2 = ch + peek(), - fn = OPERATORS[ch], - fn2 = OPERATORS[ch2]; - if (fn2) { - tokens.push({index:index, text:ch2, fn:fn2}); - index += 2; - } else if (fn) { - tokens.push({index:index, text:ch, fn:fn, json: was('[,:') && is('+-')}); - index += 1; - } else { - throw "Lexer Error: Unexpected next character [" + - text.substring(index) + - "] in expression '" + text + - "' at column '" + (index+1) + "'."; - } - } - lastCh = ch; - } - return tokens; - - function is(chars) { - return chars.indexOf(ch) != -1; - } - - function was(chars) { - return chars.indexOf(lastCh) != -1; - } - - function peek() { - return index + 1 < text.length ? text.charAt(index + 1) : false; - } - function isNumber(ch) { - return '0' <= ch && ch <= '9'; - } - function isWhitespace(ch) { - return ch == ' ' || ch == '\r' || ch == '\t' || - ch == '\n' || ch == '\v' || ch == '\u00A0'; // IE treats non-breaking space as \u00A0 - } - function isIdent(ch) { - return 'a' <= ch && ch <= 'z' || - 'A' <= ch && ch <= 'Z' || - '_' == ch || ch == '$'; - } - function isExpOperator(ch) { - return ch == '-' || ch == '+'; - } - function readNumber() { - var number = ""; - var start = index; - while (index < text.length) { - var ch = text.charAt(index); - if (ch == '.' || isNumber(ch)) { - number += ch; - } else { - var peekCh = peek(); - if (ch == 'E' && isExpOperator(peekCh)) { - number += ch; - } else if (isExpOperator(ch) && - peekCh && isNumber(peekCh) && - number.charAt(number.length - 1) == 'E') { - number += ch; - } else if (isExpOperator(ch) && - (!peekCh || !isNumber(peekCh)) && - number.charAt(number.length - 1) == 'E') { - throw 'Lexer found invalid exponential value "' + text + '"'; - } else { - break; - } - } - index++; - } - number = 1 * number; - tokens.push({index:start, text:number, json:true, - fn:function(){return number;}}); - } - function readIdent() { - var ident = ""; - var start = index; - while (index < text.length) { - var ch = text.charAt(index); - if (ch == '.' || isIdent(ch) || isNumber(ch)) { - ident += ch; - } else { - break; - } - index++; - } - var fn = OPERATORS[ident]; - if (!fn) { - fn = getterFn(ident); - fn.isAssignable = ident; - } - tokens.push({index:start, text:ident, fn:fn, json: OPERATORS[ident]}); - } - - function readString(quote) { - var start = index; - index++; - var string = ""; - var rawString = quote; - var escape = false; - while (index < text.length) { - var ch = text.charAt(index); - rawString += ch; - if (escape) { - if (ch == 'u') { - var hex = text.substring(index + 1, index + 5); - if (!hex.match(/[\da-f]{4}/i)) - throw "Lexer Error: Invalid unicode escape [\\u" + - hex + "] starting at column '" + - start + "' in expression '" + text + "'."; - index += 4; - string += String.fromCharCode(parseInt(hex, 16)); - } else { - var rep = ESCAPE[ch]; - if (rep) { - string += rep; - } else { - string += ch; - } - } - escape = false; - } else if (ch == '\\') { - escape = true; - } else if (ch == quote) { - index++; - tokens.push({index:start, text:rawString, string:string, json:true, - fn:function(){ - return (string.length == dateParseLength) ? - angular['String']['toDate'](string) : string; - }}); - return; - } else { - string += ch; - } - index++; - } - throw "Lexer Error: Unterminated quote [" + - text.substring(start) + "] starting at column '" + - (start+1) + "' in expression '" + text + "'."; - } - function readRegexp(quote) { - var start = index; - index++; - var regexp = ""; - var escape = false; - while (index < text.length) { - var ch = text.charAt(index); - if (escape) { - regexp += ch; - escape = false; - } else if (ch === '\\') { - regexp += ch; - escape = true; - } else if (ch === '/') { - index++; - var flags = ""; - if (isIdent(text.charAt(index))) { - readIdent(); - flags = tokens.pop().text; - } - var compiledRegexp = new RegExp(regexp, flags); - tokens.push({index:start, text:regexp, flags:flags, - fn:function(){return compiledRegexp;}}); - return; - } else { - regexp += ch; - } - index++; - } - throw "Lexer Error: Unterminated RegExp [" + - text.substring(start) + "] starting at column '" + - (start+1) + "' in expression '" + text + "'."; - } -} - -///////////////////////////////////////// - -function parser(text, json){ - var ZERO = valueFn(0), - tokens = lex(text, json); - return { - assertAllConsumed: assertAllConsumed, - primary: primary, - statements: statements, - validator: validator, - filter: filter, - watch: watch - }; - - /////////////////////////////////// - - function error(msg, token) { - throw "Token '" + token.text + - "' is " + msg + " at column='" + - (token.index + 1) + "' of expression '" + - text + "' starting at '" + text.substring(token.index) + "'."; - } - - function peekToken() { - if (tokens.length === 0) - throw "Unexpected end of expression: " + text; - return tokens[0]; - } - - function peek(e1, e2, e3, e4) { - if (tokens.length > 0) { - var token = tokens[0]; - var t = token.text; - if (t==e1 || t==e2 || t==e3 || t==e4 || - (!e1 && !e2 && !e3 && !e4)) { - return token; - } - } - return false; - } - - function expect(e1, e2, e3, e4){ - var token = peek(e1, e2, e3, e4); - if (token) { - if (json && !token.json) { - index = token.index; - throw "Expression at column='" + - token.index + "' of expression '" + - text + "' starting at '" + text.substring(token.index) + - "' is not valid json."; - } - tokens.shift(); - this.currentToken = token; - return token; - } - return false; - } - - function consume(e1){ - if (!expect(e1)) { - var token = peek(); - throw "Expecting '" + e1 + "' at column '" + - (token.index+1) + "' in '" + - text + "' got '" + - text.substring(token.index) + "'."; - } - } - - function unaryFn(fn, right) { - return function(self) { - return fn(self, right(self)); - }; - } - - function binaryFn(left, fn, right) { - return function(self) { - return fn(self, left(self), right(self)); - }; - } - - function hasTokens () { - return tokens.length > 0; - } - - function assertAllConsumed(){ - if (tokens.length !== 0) { - throw "Did not understand '" + text.substring(tokens[0].index) + - "' while evaluating '" + text + "'."; - } - } - - function statements(){ - var statements = []; - while(true) { - if (tokens.length > 0 && !peek('}', ')', ';', ']')) - statements.push(filterChain()); - if (!expect(';')) { - return function (self){ - var value; - for ( var i = 0; i < statements.length; i++) { - var statement = statements[i]; - if (statement) - value = statement(self); - } - return value; - }; - } - } - } - - function filterChain(){ - var left = expression(); - var token; - while(true) { - if ((token = expect('|'))) { - left = binaryFn(left, token.fn, filter()); - } else { - return left; - } - } - } - - function filter(){ - return pipeFunction(angularFilter); - } - - function validator(){ - return pipeFunction(angularValidator); - } - - function pipeFunction(fnScope){ - var fn = functionIdent(fnScope); - var argsFn = []; - var token; - while(true) { - if ((token = expect(':'))) { - argsFn.push(expression()); - } else { - var fnInvoke = function(self, input){ - var args = [input]; - for ( var i = 0; i < argsFn.length; i++) { - args.push(argsFn[i](self)); - } - return fn.apply(self, args); - }; - return function(){ - return fnInvoke; - }; - } - } - } - - function expression(){ - return throwStmt(); - } - - function throwStmt(){ - if (expect('throw')) { - var throwExp = assignment(); - return function (self) { - throw throwExp(self); - }; - } else { - return assignment(); - } - } - - function assignment(){ - var left = logicalOR(); - var token; - if (token = expect('=')) { - if (!left.isAssignable) { - throw "Left hand side '" + - text.substring(0, token.index) + "' of assignment '" + - text.substring(token.index) + "' is not assignable."; - } - var ident = function(){return left.isAssignable;}; - return binaryFn(ident, token.fn, logicalOR()); - } else { - return left; - } - } - - function logicalOR(){ - var left = logicalAND(); - var token; - while(true) { - if ((token = expect('||'))) { - left = binaryFn(left, token.fn, logicalAND()); - } else { - return left; - } - } - } - - function logicalAND(){ - var left = equality(); - var token; - if ((token = expect('&&'))) { - left = binaryFn(left, token.fn, logicalAND()); - } - return left; - } - - function equality(){ - var left = relational(); - var token; - if ((token = expect('==','!='))) { - left = binaryFn(left, token.fn, equality()); - } - return left; - } - - function relational(){ - var left = additive(); - var token; - if (token = expect('<', '>', '<=', '>=')) { - left = binaryFn(left, token.fn, relational()); - } - return left; - } - - function additive(){ - var left = multiplicative(); - var token; - while(token = expect('+','-')) { - left = binaryFn(left, token.fn, multiplicative()); - } - return left; - } - - function multiplicative(){ - var left = unary(); - var token; - while(token = expect('*','/','%')) { - left = binaryFn(left, token.fn, unary()); - } - return left; - } - - function unary(){ - var token; - if (expect('+')) { - return primary(); - } else if (token = expect('-')) { - return binaryFn(ZERO, token.fn, unary()); - } else if (token = expect('!')) { - return unaryFn(token.fn, unary()); - } else { - return primary(); - } - } - - function functionIdent(fnScope) { - var token = expect(); - var element = token.text.split('.'); - var instance = fnScope; - var key; - for ( var i = 0; i < element.length; i++) { - key = element[i]; - if (instance) - instance = instance[key]; - } - if (typeof instance != $function) { - throw "Function '" + token.text + "' at column '" + - (token.index+1) + "' in '" + text + "' is not defined."; - } - return instance; - } - - function primary() { - var primary; - if (expect('(')) { - var expression = filterChain(); - consume(')'); - primary = expression; - } else if (expect('[')) { - primary = arrayDeclaration(); - } else if (expect('{')) { - primary = object(); - } else { - var token = expect(); - primary = token.fn; - if (!primary) { - error("not a primary expression", token); - } - } - var next; - while (next = expect('(', '[', '.')) { - if (next.text === '(') { - primary = functionCall(primary); - } else if (next.text === '[') { - primary = objectIndex(primary); - } else if (next.text === '.') { - primary = fieldAccess(primary); - } else { - throw "IMPOSSIBLE"; - } - } - return primary; - } - - function fieldAccess(object) { - var field = expect().text; - var getter = getterFn(field); - var fn = function (self){ - return getter(object(self)); - }; - fn.isAssignable = field; - return fn; - } - - function objectIndex(obj) { - var indexFn = expression(); - consume(']'); - if (expect('=')) { - var rhs = expression(); - return function (self){ - return obj(self)[indexFn(self)] = rhs(self); - }; - } else { - return function (self){ - var o = obj(self); - var i = indexFn(self); - return (o) ? o[i] : _undefined; - }; - } - } - - function functionCall(fn) { - var argsFn = []; - if (peekToken().text != ')') { - do { - argsFn.push(expression()); - } while (expect(',')); - } - consume(')'); - return function (self){ - var args = []; - for ( var i = 0; i < argsFn.length; i++) { - args.push(argsFn[i](self)); - } - var fnPtr = fn(self) || noop; - // IE stupidity! - return fnPtr.apply ? - fnPtr.apply(self, args) : - fnPtr(args[0], args[1], args[2], args[3], args[4]); - }; - } - - // This is used with json array declaration - function arrayDeclaration () { - var elementFns = []; - if (peekToken().text != ']') { - do { - elementFns.push(expression()); - } while (expect(',')); - } - consume(']'); - return function (self){ - var array = []; - for ( var i = 0; i < elementFns.length; i++) { - array.push(elementFns[i](self)); - } - return array; - }; - } - - function object () { - var keyValues = []; - if (peekToken().text != '}') { - do { - var token = expect(), - key = token.string || token.text; - consume(":"); - var value = expression(); - keyValues.push({key:key, value:value}); - } while (expect(',')); - } - consume('}'); - return function (self){ - var object = {}; - for ( var i = 0; i < keyValues.length; i++) { - var keyValue = keyValues[i]; - var value = keyValue.value(self); - object[keyValue.key] = value; - } - return object; - }; - } - - function watch () { - var decl = []; - while(hasTokens()) { - decl.push(watchDecl()); - if (!expect(';')) { - assertAllConsumed(); - } - } - assertAllConsumed(); - return function (self){ - for ( var i = 0; i < decl.length; i++) { - var d = decl[i](self); - self.addListener(d.name, d.fn); - } - }; - } - - function watchDecl () { - var anchorName = expect().text; - consume(":"); - var expressionFn; - if (peekToken().text == '{') { - consume("{"); - expressionFn = statements(); - consume("}"); - } else { - expressionFn = expression(); - } - return function(self) { - return {name:anchorName, fn:expressionFn}; - }; - } -} - - - - diff --git a/src/parser.js b/src/parser.js new file mode 100644 index 00000000..8dc8f9c2 --- /dev/null +++ b/src/parser.js @@ -0,0 +1,663 @@ +var OPERATORS = { + 'null':function(self){return _null;}, + 'true':function(self){return true;}, + 'false':function(self){return false;}, + $undefined:noop, + '+':function(self, a,b){return (isDefined(a)?a:0)+(isDefined(b)?b:0);}, + '-':function(self, a,b){return (isDefined(a)?a:0)-(isDefined(b)?b:0);}, + '*':function(self, a,b){return a*b;}, + '/':function(self, a,b){return a/b;}, + '%':function(self, a,b){return a%b;}, + '^':function(self, a,b){return a^b;}, + '=':function(self, a,b){return setter(self, a, b);}, + '==':function(self, a,b){return a==b;}, + '!=':function(self, a,b){return a!=b;}, + '<':function(self, a,b){return a':function(self, a,b){return a>b;}, + '<=':function(self, a,b){return a<=b;}, + '>=':function(self, a,b){return a>=b;}, + '&&':function(self, a,b){return a&&b;}, + '||':function(self, a,b){return a||b;}, + '&':function(self, a,b){return a&b;}, +// '|':function(self, a,b){return a|b;}, + '|':function(self, a,b){return b(self, a);}, + '!':function(self, a){return !a;} +}; +var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; + +function lex(text, parseStringsForObjects){ + var dateParseLength = parseStringsForObjects ? 20 : -1, + tokens = [], + token, + index = 0, + json = [], + ch, + lastCh = ':'; // can start regexp + + while (index < text.length) { + ch = text.charAt(index); + if (is('"\'')) { + readString(ch); + } else if (isNumber(ch) || is('.') && isNumber(peek())) { + readNumber(); + } else if ( was('({[:,;') && is('/') ) { + readRegexp(); + } else if (isIdent(ch)) { + readIdent(); + if (was('{,') && json[0]=='{' && + (token=tokens[tokens.length-1])) { + token.json = token.text.indexOf('.') == -1; + } + } else if (is('(){}[].,;:')) { + tokens.push({index:index, text:ch, json:is('{}[]:,')}); + if (is('{[')) json.unshift(ch); + if (is('}]')) json.shift(); + index++; + } else if (isWhitespace(ch)) { + index++; + continue; + } else { + var ch2 = ch + peek(), + fn = OPERATORS[ch], + fn2 = OPERATORS[ch2]; + if (fn2) { + tokens.push({index:index, text:ch2, fn:fn2}); + index += 2; + } else if (fn) { + tokens.push({index:index, text:ch, fn:fn, json: was('[,:') && is('+-')}); + index += 1; + } else { + throw "Lexer Error: Unexpected next character [" + + text.substring(index) + + "] in expression '" + text + + "' at column '" + (index+1) + "'."; + } + } + lastCh = ch; + } + return tokens; + + function is(chars) { + return chars.indexOf(ch) != -1; + } + + function was(chars) { + return chars.indexOf(lastCh) != -1; + } + + function peek() { + return index + 1 < text.length ? text.charAt(index + 1) : false; + } + function isNumber(ch) { + return '0' <= ch && ch <= '9'; + } + function isWhitespace(ch) { + return ch == ' ' || ch == '\r' || ch == '\t' || + ch == '\n' || ch == '\v' || ch == '\u00A0'; // IE treats non-breaking space as \u00A0 + } + function isIdent(ch) { + return 'a' <= ch && ch <= 'z' || + 'A' <= ch && ch <= 'Z' || + '_' == ch || ch == '$'; + } + function isExpOperator(ch) { + return ch == '-' || ch == '+'; + } + function readNumber() { + var number = ""; + var start = index; + while (index < text.length) { + var ch = text.charAt(index); + if (ch == '.' || isNumber(ch)) { + number += ch; + } else { + var peekCh = peek(); + if (ch == 'E' && isExpOperator(peekCh)) { + number += ch; + } else if (isExpOperator(ch) && + peekCh && isNumber(peekCh) && + number.charAt(number.length - 1) == 'E') { + number += ch; + } else if (isExpOperator(ch) && + (!peekCh || !isNumber(peekCh)) && + number.charAt(number.length - 1) == 'E') { + throw 'Lexer found invalid exponential value "' + text + '"'; + } else { + break; + } + } + index++; + } + number = 1 * number; + tokens.push({index:start, text:number, json:true, + fn:function(){return number;}}); + } + function readIdent() { + var ident = ""; + var start = index; + while (index < text.length) { + var ch = text.charAt(index); + if (ch == '.' || isIdent(ch) || isNumber(ch)) { + ident += ch; + } else { + break; + } + index++; + } + var fn = OPERATORS[ident]; + if (!fn) { + fn = getterFn(ident); + fn.isAssignable = ident; + } + tokens.push({index:start, text:ident, fn:fn, json: OPERATORS[ident]}); + } + + function readString(quote) { + var start = index; + index++; + var string = ""; + var rawString = quote; + var escape = false; + while (index < text.length) { + var ch = text.charAt(index); + rawString += ch; + if (escape) { + if (ch == 'u') { + var hex = text.substring(index + 1, index + 5); + if (!hex.match(/[\da-f]{4}/i)) + throw "Lexer Error: Invalid unicode escape [\\u" + + hex + "] starting at column '" + + start + "' in expression '" + text + "'."; + index += 4; + string += String.fromCharCode(parseInt(hex, 16)); + } else { + var rep = ESCAPE[ch]; + if (rep) { + string += rep; + } else { + string += ch; + } + } + escape = false; + } else if (ch == '\\') { + escape = true; + } else if (ch == quote) { + index++; + tokens.push({index:start, text:rawString, string:string, json:true, + fn:function(){ + return (string.length == dateParseLength) ? + angular['String']['toDate'](string) : string; + }}); + return; + } else { + string += ch; + } + index++; + } + throw "Lexer Error: Unterminated quote [" + + text.substring(start) + "] starting at column '" + + (start+1) + "' in expression '" + text + "'."; + } + function readRegexp(quote) { + var start = index; + index++; + var regexp = ""; + var escape = false; + while (index < text.length) { + var ch = text.charAt(index); + if (escape) { + regexp += ch; + escape = false; + } else if (ch === '\\') { + regexp += ch; + escape = true; + } else if (ch === '/') { + index++; + var flags = ""; + if (isIdent(text.charAt(index))) { + readIdent(); + flags = tokens.pop().text; + } + var compiledRegexp = new RegExp(regexp, flags); + tokens.push({index:start, text:regexp, flags:flags, + fn:function(){return compiledRegexp;}}); + return; + } else { + regexp += ch; + } + index++; + } + throw "Lexer Error: Unterminated RegExp [" + + text.substring(start) + "] starting at column '" + + (start+1) + "' in expression '" + text + "'."; + } +} + +///////////////////////////////////////// + +function parser(text, json){ + var ZERO = valueFn(0), + tokens = lex(text, json); + return { + assertAllConsumed: assertAllConsumed, + primary: primary, + statements: statements, + validator: validator, + filter: filter, + watch: watch + }; + + /////////////////////////////////// + + function error(msg, token) { + throw "Token '" + token.text + + "' is " + msg + " at column='" + + (token.index + 1) + "' of expression '" + + text + "' starting at '" + text.substring(token.index) + "'."; + } + + function peekToken() { + if (tokens.length === 0) + throw "Unexpected end of expression: " + text; + return tokens[0]; + } + + function peek(e1, e2, e3, e4) { + if (tokens.length > 0) { + var token = tokens[0]; + var t = token.text; + if (t==e1 || t==e2 || t==e3 || t==e4 || + (!e1 && !e2 && !e3 && !e4)) { + return token; + } + } + return false; + } + + function expect(e1, e2, e3, e4){ + var token = peek(e1, e2, e3, e4); + if (token) { + if (json && !token.json) { + index = token.index; + throw "Expression at column='" + + token.index + "' of expression '" + + text + "' starting at '" + text.substring(token.index) + + "' is not valid json."; + } + tokens.shift(); + this.currentToken = token; + return token; + } + return false; + } + + function consume(e1){ + if (!expect(e1)) { + var token = peek(); + throw "Expecting '" + e1 + "' at column '" + + (token.index+1) + "' in '" + + text + "' got '" + + text.substring(token.index) + "'."; + } + } + + function unaryFn(fn, right) { + return function(self) { + return fn(self, right(self)); + }; + } + + function binaryFn(left, fn, right) { + return function(self) { + return fn(self, left(self), right(self)); + }; + } + + function hasTokens () { + return tokens.length > 0; + } + + function assertAllConsumed(){ + if (tokens.length !== 0) { + throw "Did not understand '" + text.substring(tokens[0].index) + + "' while evaluating '" + text + "'."; + } + } + + function statements(){ + var statements = []; + while(true) { + if (tokens.length > 0 && !peek('}', ')', ';', ']')) + statements.push(filterChain()); + if (!expect(';')) { + return function (self){ + var value; + for ( var i = 0; i < statements.length; i++) { + var statement = statements[i]; + if (statement) + value = statement(self); + } + return value; + }; + } + } + } + + function filterChain(){ + var left = expression(); + var token; + while(true) { + if ((token = expect('|'))) { + left = binaryFn(left, token.fn, filter()); + } else { + return left; + } + } + } + + function filter(){ + return pipeFunction(angularFilter); + } + + function validator(){ + return pipeFunction(angularValidator); + } + + function pipeFunction(fnScope){ + var fn = functionIdent(fnScope); + var argsFn = []; + var token; + while(true) { + if ((token = expect(':'))) { + argsFn.push(expression()); + } else { + var fnInvoke = function(self, input){ + var args = [input]; + for ( var i = 0; i < argsFn.length; i++) { + args.push(argsFn[i](self)); + } + return fn.apply(self, args); + }; + return function(){ + return fnInvoke; + }; + } + } + } + + function expression(){ + return throwStmt(); + } + + function throwStmt(){ + if (expect('throw')) { + var throwExp = assignment(); + return function (self) { + throw throwExp(self); + }; + } else { + return assignment(); + } + } + + function assignment(){ + var left = logicalOR(); + var token; + if (token = expect('=')) { + if (!left.isAssignable) { + throw "Left hand side '" + + text.substring(0, token.index) + "' of assignment '" + + text.substring(token.index) + "' is not assignable."; + } + var ident = function(){return left.isAssignable;}; + return binaryFn(ident, token.fn, logicalOR()); + } else { + return left; + } + } + + function logicalOR(){ + var left = logicalAND(); + var token; + while(true) { + if ((token = expect('||'))) { + left = binaryFn(left, token.fn, logicalAND()); + } else { + return left; + } + } + } + + function logicalAND(){ + var left = equality(); + var token; + if ((token = expect('&&'))) { + left = binaryFn(left, token.fn, logicalAND()); + } + return left; + } + + function equality(){ + var left = relational(); + var token; + if ((token = expect('==','!='))) { + left = binaryFn(left, token.fn, equality()); + } + return left; + } + + function relational(){ + var left = additive(); + var token; + if (token = expect('<', '>', '<=', '>=')) { + left = binaryFn(left, token.fn, relational()); + } + return left; + } + + function additive(){ + var left = multiplicative(); + var token; + while(token = expect('+','-')) { + left = binaryFn(left, token.fn, multiplicative()); + } + return left; + } + + function multiplicative(){ + var left = unary(); + var token; + while(token = expect('*','/','%')) { + left = binaryFn(left, token.fn, unary()); + } + return left; + } + + function unary(){ + var token; + if (expect('+')) { + return primary(); + } else if (token = expect('-')) { + return binaryFn(ZERO, token.fn, unary()); + } else if (token = expect('!')) { + return unaryFn(token.fn, unary()); + } else { + return primary(); + } + } + + function functionIdent(fnScope) { + var token = expect(); + var element = token.text.split('.'); + var instance = fnScope; + var key; + for ( var i = 0; i < element.length; i++) { + key = element[i]; + if (instance) + instance = instance[key]; + } + if (typeof instance != $function) { + throw "Function '" + token.text + "' at column '" + + (token.index+1) + "' in '" + text + "' is not defined."; + } + return instance; + } + + function primary() { + var primary; + if (expect('(')) { + var expression = filterChain(); + consume(')'); + primary = expression; + } else if (expect('[')) { + primary = arrayDeclaration(); + } else if (expect('{')) { + primary = object(); + } else { + var token = expect(); + primary = token.fn; + if (!primary) { + error("not a primary expression", token); + } + } + var next; + while (next = expect('(', '[', '.')) { + if (next.text === '(') { + primary = functionCall(primary); + } else if (next.text === '[') { + primary = objectIndex(primary); + } else if (next.text === '.') { + primary = fieldAccess(primary); + } else { + throw "IMPOSSIBLE"; + } + } + return primary; + } + + function fieldAccess(object) { + var field = expect().text; + var getter = getterFn(field); + var fn = function (self){ + return getter(object(self)); + }; + fn.isAssignable = field; + return fn; + } + + function objectIndex(obj) { + var indexFn = expression(); + consume(']'); + if (expect('=')) { + var rhs = expression(); + return function (self){ + return obj(self)[indexFn(self)] = rhs(self); + }; + } else { + return function (self){ + var o = obj(self); + var i = indexFn(self); + return (o) ? o[i] : _undefined; + }; + } + } + + function functionCall(fn) { + var argsFn = []; + if (peekToken().text != ')') { + do { + argsFn.push(expression()); + } while (expect(',')); + } + consume(')'); + return function (self){ + var args = []; + for ( var i = 0; i < argsFn.length; i++) { + args.push(argsFn[i](self)); + } + var fnPtr = fn(self) || noop; + // IE stupidity! + return fnPtr.apply ? + fnPtr.apply(self, args) : + fnPtr(args[0], args[1], args[2], args[3], args[4]); + }; + } + + // This is used with json array declaration + function arrayDeclaration () { + var elementFns = []; + if (peekToken().text != ']') { + do { + elementFns.push(expression()); + } while (expect(',')); + } + consume(']'); + return function (self){ + var array = []; + for ( var i = 0; i < elementFns.length; i++) { + array.push(elementFns[i](self)); + } + return array; + }; + } + + function object () { + var keyValues = []; + if (peekToken().text != '}') { + do { + var token = expect(), + key = token.string || token.text; + consume(":"); + var value = expression(); + keyValues.push({key:key, value:value}); + } while (expect(',')); + } + consume('}'); + return function (self){ + var object = {}; + for ( var i = 0; i < keyValues.length; i++) { + var keyValue = keyValues[i]; + var value = keyValue.value(self); + object[keyValue.key] = value; + } + return object; + }; + } + + function watch () { + var decl = []; + while(hasTokens()) { + decl.push(watchDecl()); + if (!expect(';')) { + assertAllConsumed(); + } + } + assertAllConsumed(); + return function (self){ + for ( var i = 0; i < decl.length; i++) { + var d = decl[i](self); + self.addListener(d.name, d.fn); + } + }; + } + + function watchDecl () { + var anchorName = expect().text; + consume(":"); + var expressionFn; + if (peekToken().text == '{') { + consume("{"); + expressionFn = statements(); + consume("}"); + } else { + expressionFn = expression(); + } + return function(self) { + return {name:anchorName, fn:expressionFn}; + }; + } +} + + + + -- cgit v1.2.3