aboutsummaryrefslogtreecommitdiffstats
path: root/src/Parser.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/Parser.js')
-rw-r--r--src/Parser.js1292
1 files changed, 649 insertions, 643 deletions
diff --git a/src/Parser.js b/src/Parser.js
index cdece11e..333b8413 100644
--- a/src/Parser.js
+++ b/src/Parser.js
@@ -1,4 +1,4 @@
-Lexer = function(text, parsStrings){
+function Lexer(text, parsStrings){
this.text = text;
// UTC dates have 20 characters, we send them through parser
this.dateParseLength = parsStrings ? 20 : -1;
@@ -30,210 +30,214 @@ Lexer.OPERATORS = {
'|':function(self, a,b){return b(self, a);},
'!':function(self, a){return !a;}
};
+Lexer.ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
-Lexer.prototype.peek = function() {
- if (this.index + 1 < this.text.length) {
- return this.text.charAt(this.index + 1);
- } else {
- return false;
- }
-};
-
-Lexer.prototype.parse = function() {
- var tokens = this.tokens;
- var OPERATORS = Lexer.OPERATORS;
- var canStartRegExp = true;
- while (this.index < this.text.length) {
- var ch = this.text.charAt(this.index);
- if (ch == '"' || ch == "'") {
- this.readString(ch);
- canStartRegExp = true;
- } else if (ch == '(' || ch == '[') {
- tokens.push({index:this.index, text:ch});
- this.index++;
- } else if (ch == '{' ) {
- var peekCh = this.peek();
- if (peekCh == ':' || peekCh == '(') {
- tokens.push({index:this.index, text:ch + peekCh});
+Lexer.prototype = {
+ peek: function() {
+ if (this.index + 1 < this.text.length) {
+ return this.text.charAt(this.index + 1);
+ } else {
+ return false;
+ }
+ },
+
+ parse: function() {
+ var tokens = this.tokens;
+ var OPERATORS = Lexer.OPERATORS;
+ var canStartRegExp = true;
+ while (this.index < this.text.length) {
+ var ch = this.text.charAt(this.index);
+ if (ch == '"' || ch == "'") {
+ this.readString(ch);
+ canStartRegExp = true;
+ } else if (ch == '(' || ch == '[') {
+ tokens.push({index:this.index, text:ch});
this.index++;
- } else {
+ } else if (ch == '{' ) {
+ var peekCh = this.peek();
+ if (peekCh == ':' || peekCh == '(') {
+ tokens.push({index:this.index, text:ch + peekCh});
+ this.index++;
+ } else {
+ tokens.push({index:this.index, text:ch});
+ }
+ this.index++;
+ canStartRegExp = true;
+ } else if (ch == ')' || ch == ']' || ch == '}' ) {
tokens.push({index:this.index, text:ch});
+ this.index++;
+ canStartRegExp = false;
+ } else if ( ch == ':' || ch == '.' || ch == ',' || ch == ';') {
+ tokens.push({index:this.index, text:ch});
+ this.index++;
+ canStartRegExp = true;
+ } else if ( canStartRegExp && ch == '/' ) {
+ this.readRegexp();
+ canStartRegExp = false;
+ } else if ( this.isNumber(ch) ) {
+ this.readNumber();
+ canStartRegExp = false;
+ } else if (this.isIdent(ch)) {
+ this.readIdent();
+ canStartRegExp = false;
+ } else if (this.isWhitespace(ch)) {
+ this.index++;
+ } else {
+ var ch2 = ch + this.peek();
+ var fn = OPERATORS[ch];
+ var fn2 = OPERATORS[ch2];
+ if (fn2) {
+ tokens.push({index:this.index, text:ch2, fn:fn2});
+ this.index += 2;
+ } else if (fn) {
+ tokens.push({index:this.index, text:ch, fn:fn});
+ this.index += 1;
+ } else {
+ throw "Lexer Error: Unexpected next character [" +
+ this.text.substring(this.index) +
+ "] in expression '" + this.text +
+ "' at column '" + (this.index+1) + "'.";
+ }
+ canStartRegExp = true;
}
- this.index++;
- canStartRegExp = true;
- } else if (ch == ')' || ch == ']' || ch == '}' ) {
- tokens.push({index:this.index, text:ch});
- this.index++;
- canStartRegExp = false;
- } else if ( ch == ':' || ch == '.' || ch == ',' || ch == ';') {
- tokens.push({index:this.index, text:ch});
- this.index++;
- canStartRegExp = true;
- } else if ( canStartRegExp && ch == '/' ) {
- this.readRegexp();
- canStartRegExp = false;
- } else if ( this.isNumber(ch) ) {
- this.readNumber();
- canStartRegExp = false;
- } else if (this.isIdent(ch)) {
- this.readIdent();
- canStartRegExp = false;
- } else if (this.isWhitespace(ch)) {
- this.index++;
- } else {
- var ch2 = ch + this.peek();
- var fn = OPERATORS[ch];
- var fn2 = OPERATORS[ch2];
- if (fn2) {
- tokens.push({index:this.index, text:ch2, fn:fn2});
- this.index += 2;
- } else if (fn) {
- tokens.push({index:this.index, text:ch, fn:fn});
- this.index += 1;
+ }
+ return tokens;
+ },
+
+ isNumber: function(ch) {
+ return '0' <= ch && ch <= '9';
+ },
+
+ isWhitespace: function(ch) {
+ return ch == ' ' || ch == '\r' || ch == '\t' ||
+ ch == '\n' || ch == '\v';
+ },
+
+ isIdent: function(ch) {
+ return 'a' <= ch && ch <= 'z' ||
+ 'A' <= ch && ch <= 'Z' ||
+ '_' == ch || ch == '$';
+ },
+
+ readNumber: function() {
+ var number = "";
+ var start = this.index;
+ while (this.index < this.text.length) {
+ var ch = this.text.charAt(this.index);
+ if (ch == '.' || this.isNumber(ch)) {
+ number += ch;
} else {
- throw "Lexer Error: Unexpected next character [" +
- this.text.substring(this.index) +
- "] in expression '" + this.text +
- "' at column '" + (this.index+1) + "'.";
+ break;
}
- canStartRegExp = true;
+ this.index++;
}
- }
- return tokens;
-};
-
-Lexer.prototype.isNumber = function(ch) {
- return '0' <= ch && ch <= '9';
-};
-
-Lexer.prototype.isWhitespace = function(ch) {
- return ch == ' ' || ch == '\r' || ch == '\t' ||
- ch == '\n' || ch == '\v';
-};
-
-Lexer.prototype.isIdent = function(ch) {
- return 'a' <= ch && ch <= 'z' ||
- 'A' <= ch && ch <= 'Z' ||
- '_' == ch || ch == '$';
-};
-
-Lexer.prototype.readNumber = function() {
- var number = "";
- var start = this.index;
- while (this.index < this.text.length) {
- var ch = this.text.charAt(this.index);
- if (ch == '.' || this.isNumber(ch)) {
- number += ch;
- } else {
- break;
+ number = 1 * number;
+ this.tokens.push({index:start, text:number,
+ fn:function(){return number;}});
+ },
+
+ readIdent: function() {
+ var ident = "";
+ var start = this.index;
+ while (this.index < this.text.length) {
+ var ch = this.text.charAt(this.index);
+ if (ch == '.' || this.isIdent(ch) || this.isNumber(ch)) {
+ ident += ch;
+ } else {
+ break;
+ }
+ this.index++;
}
- this.index++;
- }
- number = 1 * number;
- this.tokens.push({index:start, text:number,
- fn:function(){return number;}});
-};
-
-Lexer.prototype.readIdent = function() {
- var ident = "";
- var start = this.index;
- while (this.index < this.text.length) {
- var ch = this.text.charAt(this.index);
- if (ch == '.' || this.isIdent(ch) || this.isNumber(ch)) {
- ident += ch;
- } else {
- break;
+ var fn = Lexer.OPERATORS[ident];
+ if (!fn) {
+ fn = function(self){
+ return self.scope.get(ident);
+ };
+ fn.isAssignable = ident;
}
+ this.tokens.push({index:start, text:ident, fn:fn});
+ },
+
+ readString: function(quote) {
+ var start = this.index;
+ var dateParseLength = this.dateParseLength;
this.index++;
- }
- var fn = Lexer.OPERATORS[ident];
- if (!fn) {
- fn = function(self){
- return self.scope.get(ident);
- };
- fn.isAssignable = ident;
- }
- this.tokens.push({index:start, text:ident, fn:fn});
-};
-Lexer.ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
-Lexer.prototype.readString = function(quote) {
- var start = this.index;
- var dateParseLength = this.dateParseLength;
- this.index++;
- var string = "";
- var escape = false;
- while (this.index < this.text.length) {
- var ch = this.text.charAt(this.index);
- if (escape) {
- if (ch == 'u') {
- var hex = this.text.substring(this.index + 1, this.index + 5);
- this.index += 4;
- string += String.fromCharCode(parseInt(hex, 16));
- } else {
- var rep = Lexer.ESCAPE[ch];
- if (rep) {
- string += rep;
+ var string = "";
+ var escape = false;
+ while (this.index < this.text.length) {
+ var ch = this.text.charAt(this.index);
+ if (escape) {
+ if (ch == 'u') {
+ var hex = this.text.substring(this.index + 1, this.index + 5);
+ this.index += 4;
+ string += String.fromCharCode(parseInt(hex, 16));
} else {
- string += ch;
+ var rep = Lexer.ESCAPE[ch];
+ if (rep) {
+ string += rep;
+ } else {
+ string += ch;
+ }
}
+ escape = false;
+ } else if (ch == '\\') {
+ escape = true;
+ } else if (ch == quote) {
+ this.index++;
+ this.tokens.push({index:start, text:string,
+ fn:function(){
+ return (string.length == dateParseLength) ?
+ angular['String']['toDate'](string) : string;
+ }});
+ return;
+ } else {
+ string += ch;
}
- escape = false;
- } else if (ch == '\\') {
- escape = true;
- } else if (ch == quote) {
this.index++;
- this.tokens.push({index:start, text:string,
- fn:function(){
- return (string.length == dateParseLength) ?
- angular['String']['toDate'](string) : string;
- }});
- return;
- } else {
- string += ch;
}
+ throw "Lexer Error: Unterminated quote [" +
+ this.text.substring(start) + "] starting at column '" +
+ (start+1) + "' in expression '" + this.text + "'.";
+ },
+
+ readRegexp: function(quote) {
+ var start = this.index;
this.index++;
- }
- throw "Lexer Error: Unterminated quote [" +
- this.text.substring(start) + "] starting at column '" +
- (start+1) + "' in expression '" + this.text + "'.";
-};
-
-Lexer.prototype.readRegexp = function(quote) {
- var start = this.index;
- this.index++;
- var regexp = "";
- var escape = false;
- while (this.index < this.text.length) {
- var ch = this.text.charAt(this.index);
- if (escape) {
- regexp += ch;
- escape = false;
- } else if (ch === '\\') {
- regexp += ch;
- escape = true;
- } else if (ch === '/') {
- this.index++;
- var flags = "";
- if (this.isIdent(this.text.charAt(this.index))) {
- this.readIdent();
- flags = this.tokens.pop().text;
+ var regexp = "";
+ var escape = false;
+ while (this.index < this.text.length) {
+ var ch = this.text.charAt(this.index);
+ if (escape) {
+ regexp += ch;
+ escape = false;
+ } else if (ch === '\\') {
+ regexp += ch;
+ escape = true;
+ } else if (ch === '/') {
+ this.index++;
+ var flags = "";
+ if (this.isIdent(this.text.charAt(this.index))) {
+ this.readIdent();
+ flags = this.tokens.pop().text;
+ }
+ var compiledRegexp = new RegExp(regexp, flags);
+ this.tokens.push({index:start, text:regexp, flags:flags,
+ fn:function(){return compiledRegexp;}});
+ return;
+ } else {
+ regexp += ch;
}
- var compiledRegexp = new RegExp(regexp, flags);
- this.tokens.push({index:start, text:regexp, flags:flags,
- fn:function(){return compiledRegexp;}});
- return;
- } else {
- regexp += ch;
+ this.index++;
}
- this.index++;
+ throw "Lexer Error: Unterminated RegExp [" +
+ this.text.substring(start) + "] starting at column '" +
+ (start+1) + "' in expression '" + this.text + "'.";
}
- throw "Lexer Error: Unterminated RegExp [" +
- this.text.substring(start) + "] starting at column '" +
- (start+1) + "' in expression '" + this.text + "'.";
};
+/////////////////////////////////////////
-Parser = function(text, parseStrings){
+function Parser(text, parseStrings){
this.text = text;
this.tokens = new Lexer(text, parseStrings).parse();
this.index = 0;
@@ -243,499 +247,501 @@ Parser.ZERO = function(){
return 0;
};
-Parser.prototype.error = function(msg, token) {
- throw "Token '" + token.text +
- "' is " + msg + " at column='" +
- (token.index + 1) + "' of expression '" +
- this.text + "' starting at '" + this.text.substring(token.index) + "'.";
-};
-
-Parser.prototype.peekToken = function() {
- if (this.tokens.length === 0)
- throw "Unexpected end of expression: " + this.text;
- return this.tokens[0];
-};
-
-Parser.prototype.peek = function(e1, e2, e3, e4) {
- var tokens = this.tokens;
- 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)) {
+Parser.prototype = {
+ error: function(msg, token) {
+ throw "Token '" + token.text +
+ "' is " + msg + " at column='" +
+ (token.index + 1) + "' of expression '" +
+ this.text + "' starting at '" + this.text.substring(token.index) + "'.";
+ },
+
+ peekToken: function() {
+ if (this.tokens.length === 0)
+ throw "Unexpected end of expression: " + this.text;
+ return this.tokens[0];
+ },
+
+ peek: function(e1, e2, e3, e4) {
+ var tokens = this.tokens;
+ 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;
+ },
+
+ expect: function(e1, e2, e3, e4){
+ var token = this.peek(e1, e2, e3, e4);
+ if (token) {
+ this.tokens.shift();
+ this.currentToken = token;
return token;
}
- }
- return false;
-};
-
-Parser.prototype.expect = function(e1, e2, e3, e4){
- var token = this.peek(e1, e2, e3, e4);
- if (token) {
- this.tokens.shift();
- this.currentToken = token;
- return token;
- }
- return false;
-};
-
-Parser.prototype.consume = function(e1){
- if (!this.expect(e1)) {
- var token = this.peek();
- throw "Expecting '" + e1 + "' at column '" +
- (token.index+1) + "' in '" +
- this.text + "' got '" +
- this.text.substring(token.index) + "'.";
- }
-};
-
-Parser.prototype._unary = function(fn, parse) {
- var right = parse.apply(this);
- return function(self) {
- return fn(self, right(self));
- };
-};
-
-Parser.prototype._binary = function(left, fn, parse) {
- var right = parse.apply(this);
- return function(self) {
- return fn(self, left(self), right(self));
- };
-};
-
-Parser.prototype.hasTokens = function () {
- return this.tokens.length > 0;
-};
-
-Parser.prototype.assertAllConsumed = function(){
- if (this.tokens.length !== 0) {
- throw "Did not understand '" + this.text.substring(this.tokens[0].index) +
- "' while evaluating '" + this.text + "'.";
- }
-};
-
-Parser.prototype.statements = function(){
- var statements = [];
- while(true) {
- if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
- statements.push(this.filterChain());
- if (!this.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;
- };
+ return false;
+ },
+
+ consume: function(e1){
+ if (!this.expect(e1)) {
+ var token = this.peek();
+ throw "Expecting '" + e1 + "' at column '" +
+ (token.index+1) + "' in '" +
+ this.text + "' got '" +
+ this.text.substring(token.index) + "'.";
}
- }
-};
-
-Parser.prototype.filterChain = function(){
- var left = this.expression();
- var token;
- while(true) {
- if ((token = this.expect('|'))) {
- left = this._binary(left, token.fn, this.filter);
- } else {
- return left;
+ },
+
+ _unary: function(fn, parse) {
+ var right = parse.apply(this);
+ return function(self) {
+ return fn(self, right(self));
+ };
+ },
+
+ _binary: function(left, fn, parse) {
+ var right = parse.apply(this);
+ return function(self) {
+ return fn(self, left(self), right(self));
+ };
+ },
+
+ hasTokens: function () {
+ return this.tokens.length > 0;
+ },
+
+ assertAllConsumed: function(){
+ if (this.tokens.length !== 0) {
+ throw "Did not understand '" + this.text.substring(this.tokens[0].index) +
+ "' while evaluating '" + this.text + "'.";
}
- }
-};
-
-Parser.prototype.filter = function(){
- return this._pipeFunction(angular['filter']);
-};
-
-Parser.prototype.validator = function(){
- return this._pipeFunction(angular['validator']);
-};
-
-Parser.prototype._pipeFunction = function(fnScope){
- var fn = this.functionIdent(fnScope);
- var argsFn = [];
- var token;
- while(true) {
- if ((token = this.expect(':'))) {
- argsFn.push(this.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;
- };
+ },
+
+ statements: function(){
+ var statements = [];
+ while(true) {
+ if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
+ statements.push(this.filterChain());
+ if (!this.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;
+ };
+ }
}
- }
-};
-
-Parser.prototype.expression = function(){
- return this.throwStmt();
-};
-
-Parser.prototype.throwStmt = function(){
- if (this.expect('throw')) {
- var throwExp = this.assignment();
- return function (self) {
- throw throwExp(self);
- };
- } else {
- return this.assignment();
- }
-};
-
-Parser.prototype.assignment = function(){
- var left = this.logicalOR();
- var token;
- if (token = this.expect('=')) {
- if (!left.isAssignable) {
- throw "Left hand side '" +
- this.text.substring(0, token.index) + "' of assignment '" +
- this.text.substring(token.index) + "' is not assignable.";
- }
- var ident = function(){return left.isAssignable;};
- return this._binary(ident, token.fn, this.logicalOR);
- } else {
- return left;
- }
-};
-
-Parser.prototype.logicalOR = function(){
- var left = this.logicalAND();
- var token;
- while(true) {
- if ((token = this.expect('||'))) {
- left = this._binary(left, token.fn, this.logicalAND);
+ },
+
+ filterChain: function(){
+ var left = this.expression();
+ var token;
+ while(true) {
+ if ((token = this.expect('|'))) {
+ left = this._binary(left, token.fn, this.filter);
+ } else {
+ return left;
+ }
+ }
+ },
+
+ filter: function(){
+ return this._pipeFunction(angular['filter']);
+ },
+
+ validator: function(){
+ return this._pipeFunction(angular['validator']);
+ },
+
+ _pipeFunction: function(fnScope){
+ var fn = this.functionIdent(fnScope);
+ var argsFn = [];
+ var token;
+ while(true) {
+ if ((token = this.expect(':'))) {
+ argsFn.push(this.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;
+ };
+ }
+ }
+ },
+
+ expression: function(){
+ return this.throwStmt();
+ },
+
+ throwStmt: function(){
+ if (this.expect('throw')) {
+ var throwExp = this.assignment();
+ return function (self) {
+ throw throwExp(self);
+ };
} else {
- return left;
+ return this.assignment();
}
- }
-};
-
-Parser.prototype.logicalAND = function(){
- var left = this.negated();
- var token;
- while(true) {
- if ((token = this.expect('&&'))) {
- left = this._binary(left, token.fn, this.negated);
+ },
+
+ assignment: function(){
+ var left = this.logicalOR();
+ var token;
+ if (token = this.expect('=')) {
+ if (!left.isAssignable) {
+ throw "Left hand side '" +
+ this.text.substring(0, token.index) + "' of assignment '" +
+ this.text.substring(token.index) + "' is not assignable.";
+ }
+ var ident = function(){return left.isAssignable;};
+ return this._binary(ident, token.fn, this.logicalOR);
} else {
- return left;
+ return left;
}
- }
-};
-
-Parser.prototype.negated = function(){
- var token;
- if (token = this.expect('!')) {
- return this._unary(token.fn, this.equality);
- } else {
- return this.equality();
- }
-};
-
-Parser.prototype.equality = function(){
- var left = this.relational();
- var token;
- while(true) {
- if ((token = this.expect('==','!='))) {
- left = this._binary(left, token.fn, this.relational);
+ },
+
+ logicalOR: function(){
+ var left = this.logicalAND();
+ var token;
+ while(true) {
+ if ((token = this.expect('||'))) {
+ left = this._binary(left, token.fn, this.logicalAND);
+ } else {
+ return left;
+ }
+ }
+ },
+
+ logicalAND: function(){
+ var left = this.negated();
+ var token;
+ while(true) {
+ if ((token = this.expect('&&'))) {
+ left = this._binary(left, token.fn, this.negated);
+ } else {
+ return left;
+ }
+ }
+ },
+
+ negated: function(){
+ var token;
+ if (token = this.expect('!')) {
+ return this._unary(token.fn, this.equality);
} else {
- return left;
+ return this.equality();
}
- }
-};
-
-Parser.prototype.relational = function(){
- var left = this.additive();
- var token;
- while(true) {
- if ((token = this.expect('<', '>', '<=', '>='))) {
- left = this._binary(left, token.fn, this.additive);
+ },
+
+ equality: function(){
+ var left = this.relational();
+ var token;
+ while(true) {
+ if ((token = this.expect('==','!='))) {
+ left = this._binary(left, token.fn, this.relational);
+ } else {
+ return left;
+ }
+ }
+ },
+
+ relational: function(){
+ var left = this.additive();
+ var token;
+ while(true) {
+ if ((token = this.expect('<', '>', '<=', '>='))) {
+ left = this._binary(left, token.fn, this.additive);
+ } else {
+ return left;
+ }
+ }
+ },
+
+ additive: function(){
+ var left = this.multiplicative();
+ var token;
+ while(token = this.expect('+','-')) {
+ left = this._binary(left, token.fn, this.multiplicative);
+ }
+ return left;
+ },
+
+ multiplicative: function(){
+ var left = this.unary();
+ var token;
+ while(token = this.expect('*','/','%')) {
+ left = this._binary(left, token.fn, this.unary);
+ }
+ return left;
+ },
+
+ unary: function(){
+ var token;
+ if (this.expect('+')) {
+ return this.primary();
+ } else if (token = this.expect('-')) {
+ return this._binary(Parser.ZERO, token.fn, this.multiplicative);
} else {
- return left;
+ return this.primary();
}
- }
-};
-
-Parser.prototype.additive = function(){
- var left = this.multiplicative();
- var token;
- while(token = this.expect('+','-')) {
- left = this._binary(left, token.fn, this.multiplicative);
- }
- return left;
-};
-
-Parser.prototype.multiplicative = function(){
- var left = this.unary();
- var token;
- while(token = this.expect('*','/','%')) {
- left = this._binary(left, token.fn, this.unary);
- }
- return left;
-};
-
-Parser.prototype.unary = function(){
- var token;
- if (this.expect('+')) {
- return this.primary();
- } else if (token = this.expect('-')) {
- return this._binary(Parser.ZERO, token.fn, this.multiplicative);
- } else {
- return this.primary();
- }
-};
-
-Parser.prototype.functionIdent = function(fnScope) {
- var token = this.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 '" + this.text + "' is not defined.";
- }
- return instance;
-};
-
-Parser.prototype.primary = function() {
- var primary;
- if (this.expect('(')) {
- var expression = this.filterChain();
- this.consume(')');
- primary = expression;
- } else if (this.expect('[')) {
- primary = this.arrayDeclaration();
- } else if (this.expect('{')) {
- primary = this.object();
- } else if (this.expect('{:')) {
- primary = this.closure(false);
- } else if (this.expect('{(')) {
- primary = this.closure(true);
- } else {
+ },
+
+ functionIdent: function(fnScope) {
var token = this.expect();
- primary = token.fn;
- if (!primary) {
- this.error("not a primary expression", token);
+ 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];
}
- }
- var next;
- while (next = this.expect('(', '[', '.')) {
- if (next.text === '(') {
- primary = this.functionCall(primary);
- } else if (next.text === '[') {
- primary = this.objectIndex(primary);
- } else if (next.text === '.') {
- primary = this.fieldAccess(primary);
+ if (typeof instance != 'function') {
+ throw "Function '" + token.text + "' at column '" +
+ (token.index+1) + "' in '" + this.text + "' is not defined.";
+ }
+ return instance;
+ },
+
+ primary: function() {
+ var primary;
+ if (this.expect('(')) {
+ var expression = this.filterChain();
+ this.consume(')');
+ primary = expression;
+ } else if (this.expect('[')) {
+ primary = this.arrayDeclaration();
+ } else if (this.expect('{')) {
+ primary = this.object();
+ } else if (this.expect('{:')) {
+ primary = this.closure(false);
+ } else if (this.expect('{(')) {
+ primary = this.closure(true);
} else {
- throw "IMPOSSIBLE";
+ var token = this.expect();
+ primary = token.fn;
+ if (!primary) {
+ this.error("not a primary expression", token);
+ }
}
- }
- return primary;
-};
-
-Parser.prototype.closure = function(hasArgs) {
- var args = [];
- if (hasArgs) {
- if (!this.expect(')')) {
- args.push(this.expect().text);
- while(this.expect(',')) {
+ var next;
+ while (next = this.expect('(', '[', '.')) {
+ if (next.text === '(') {
+ primary = this.functionCall(primary);
+ } else if (next.text === '[') {
+ primary = this.objectIndex(primary);
+ } else if (next.text === '.') {
+ primary = this.fieldAccess(primary);
+ } else {
+ throw "IMPOSSIBLE";
+ }
+ }
+ return primary;
+ },
+
+ closure: function(hasArgs) {
+ var args = [];
+ if (hasArgs) {
+ if (!this.expect(')')) {
args.push(this.expect().text);
+ while(this.expect(',')) {
+ args.push(this.expect().text);
+ }
+ this.consume(')');
}
- this.consume(')');
+ this.consume(":");
}
- this.consume(":");
- }
- var statements = this.statements();
- this.consume("}");
- return function(self){
- return function($){
- var scope = new Scope(self.scope.state);
- scope.set('$', $);
- for ( var i = 0; i < args.length; i++) {
- scope.set(args[i], arguments[i]);
+ var statements = this.statements();
+ this.consume("}");
+ return function(self){
+ return function($){
+ var scope = new Scope(self.scope.state);
+ scope.set('$', $);
+ for ( var i = 0; i < args.length; i++) {
+ scope.set(args[i], arguments[i]);
+ }
+ return statements({scope:scope});
+ };
+ };
+ },
+
+ fieldAccess: function(object) {
+ var field = this.expect().text;
+ var fn = function (self){
+ return Scope.getter(object(self), field);
+ };
+ fn.isAssignable = field;
+ return fn;
+ },
+
+ objectIndex: function(obj) {
+ var indexFn = this.expression();
+ this.consume(']');
+ if (this.expect('=')) {
+ var rhs = this.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;
+ };
+ }
+ },
+
+ functionCall: function(fn) {
+ var argsFn = [];
+ if (this.peekToken().text != ')') {
+ do {
+ argsFn.push(this.expression());
+ } while (this.expect(','));
+ }
+ this.consume(')');
+ return function (self){
+ var args = [];
+ for ( var i = 0; i < argsFn.length; i++) {
+ args.push(argsFn[i](self));
+ }
+ var fnPtr = fn(self);
+ if (typeof fnPtr === 'function') {
+ return fnPtr.apply(self, args);
+ } else {
+ throw "Expression '" + fn.isAssignable + "' is not a function.";
}
- return statements({scope:scope});
};
- };
-};
-
-Parser.prototype.fieldAccess = function(object) {
- var field = this.expect().text;
- var fn = function (self){
- return Scope.getter(object(self), field);
- };
- fn.isAssignable = field;
- return fn;
-};
-
-Parser.prototype.objectIndex = function(obj) {
- var indexFn = this.expression();
- this.consume(']');
- if (this.expect('=')) {
- var rhs = this.expression();
+ },
+
+ // This is used with json array declaration
+ arrayDeclaration: function () {
+ var elementFns = [];
+ if (this.peekToken().text != ']') {
+ do {
+ elementFns.push(this.expression());
+ } while (this.expect(','));
+ }
+ this.consume(']');
return function (self){
- return obj(self)[indexFn(self)] = rhs(self);
+ var array = [];
+ for ( var i = 0; i < elementFns.length; i++) {
+ array.push(elementFns[i](self));
+ }
+ return array;
};
- } else {
+ },
+
+ object: function () {
+ var keyValues = [];
+ if (this.peekToken().text != '}') {
+ do {
+ var key = this.expect().text;
+ this.consume(":");
+ var value = this.expression();
+ keyValues.push({key:key, value:value});
+ } while (this.expect(','));
+ }
+ this.consume('}');
return function (self){
- var o = obj(self);
- var i = indexFn(self);
- return (o) ? o[i] : undefined;
+ 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;
};
- }
-};
-
-Parser.prototype.functionCall = function(fn) {
- var argsFn = [];
- if (this.peekToken().text != ')') {
- do {
- argsFn.push(this.expression());
- } while (this.expect(','));
- }
- this.consume(')');
- return function (self){
- var args = [];
- for ( var i = 0; i < argsFn.length; i++) {
- args.push(argsFn[i](self));
+ },
+
+ entityDeclaration: function () {
+ var decl = [];
+ while(this.hasTokens()) {
+ decl.push(this.entityDecl());
+ if (!this.expect(';')) {
+ this.assertAllConsumed();
+ }
}
- var fnPtr = fn(self);
- if (typeof fnPtr === 'function') {
- return fnPtr.apply(self, args);
- } else {
- throw "Expression '" + fn.isAssignable + "' is not a function.";
+ return function (self){
+ var code = "";
+ for ( var i = 0; i < decl.length; i++) {
+ code += decl[i](self);
+ }
+ return code;
+ };
+ },
+
+ entityDecl: function () {
+ var entity = this.expect().text;
+ var instance;
+ var defaults;
+ if (this.expect('=')) {
+ instance = entity;
+ entity = this.expect().text;
}
- };
-};
-
-// This is used with json array declaration
-Parser.prototype.arrayDeclaration = function () {
- var elementFns = [];
- if (this.peekToken().text != ']') {
- do {
- elementFns.push(this.expression());
- } while (this.expect(','));
- }
- this.consume(']');
- return function (self){
- var array = [];
- for ( var i = 0; i < elementFns.length; i++) {
- array.push(elementFns[i](self));
- }
- return array;
- };
-};
-
-Parser.prototype.object = function () {
- var keyValues = [];
- if (this.peekToken().text != '}') {
- do {
- var key = this.expect().text;
- this.consume(":");
- var value = this.expression();
- keyValues.push({key:key, value:value});
- } while (this.expect(','));
- }
- this.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;
- };
-};
-
-Parser.prototype.entityDeclaration = function () {
- var decl = [];
- while(this.hasTokens()) {
- decl.push(this.entityDecl());
- if (!this.expect(';')) {
- this.assertAllConsumed();
+ if (this.expect(':')) {
+ defaults = this.primary()(null);
}
- }
- return function (self){
- var code = "";
- for ( var i = 0; i < decl.length; i++) {
- code += decl[i](self);
+ return function(self) {
+ var datastore = self.scope.get('$datastore');
+ var Entity = datastore.entity(entity, defaults);
+ self.scope.set(entity, Entity);
+ if (instance) {
+ var document = Entity();
+ document.$$anchor = instance;
+ self.scope.set(instance, document);
+ return "$anchor." + instance + ":{" +
+ instance + "=" + entity + ".load($anchor." + instance + ");" +
+ instance + ".$$anchor=" + angular['String']['quote'](instance) + ";" +
+ "};";
+ } else {
+ return "";
+ }
+ };
+ },
+
+ watch: function () {
+ var decl = [];
+ while(this.hasTokens()) {
+ decl.push(this.watchDecl());
+ if (!this.expect(';')) {
+ this.assertAllConsumed();
+ }
}
- return code;
- };
-};
-
-Parser.prototype.entityDecl = function () {
- var entity = this.expect().text;
- var instance;
- var defaults;
- if (this.expect('=')) {
- instance = entity;
- entity = this.expect().text;
- }
- if (this.expect(':')) {
- defaults = this.primary()(null);
- }
- return function(self) {
- var datastore = self.scope.get('$datastore');
- var Entity = datastore.entity(entity, defaults);
- self.scope.set(entity, Entity);
- if (instance) {
- var document = Entity();
- document.$$anchor = instance;
- self.scope.set(instance, document);
- return "$anchor." + instance + ":{" +
- instance + "=" + entity + ".load($anchor." + instance + ");" +
- instance + ".$$anchor=" + angular['String']['quote'](instance) + ";" +
- "};";
+ this.assertAllConsumed();
+ return function (self){
+ for ( var i = 0; i < decl.length; i++) {
+ var d = decl[i](self);
+ self.addListener(d.name, d.fn);
+ }
+ };
+ },
+
+ watchDecl: function () {
+ var anchorName = this.expect().text;
+ this.consume(":");
+ var expression;
+ if (this.peekToken().text == '{') {
+ this.consume("{");
+ expression = this.statements();
+ this.consume("}");
} else {
- return "";
- }
- };
-};
-
-Parser.prototype.watch = function () {
- var decl = [];
- while(this.hasTokens()) {
- decl.push(this.watchDecl());
- if (!this.expect(';')) {
- this.assertAllConsumed();
- }
- }
- this.assertAllConsumed();
- return function (self){
- for ( var i = 0; i < decl.length; i++) {
- var d = decl[i](self);
- self.addListener(d.name, d.fn);
+ expression = this.expression();
}
- };
-};
-
-Parser.prototype.watchDecl = function () {
- var anchorName = this.expect().text;
- this.consume(":");
- var expression;
- if (this.peekToken().text == '{') {
- this.consume("{");
- expression = this.statements();
- this.consume("}");
- } else {
- expression = this.expression();
+ return function(self) {
+ return {name:anchorName, fn:expression};
+ };
}
- return function(self) {
- return {name:anchorName, fn:expression};
- };
};