aboutsummaryrefslogtreecommitdiffstats
path: root/src/Parser.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/Parser.js')
-rw-r--r--src/Parser.js345
1 files changed, 125 insertions, 220 deletions
diff --git a/src/Parser.js b/src/Parser.js
index 5eb75713..eacbf117 100644
--- a/src/Parser.js
+++ b/src/Parser.js
@@ -1,16 +1,8 @@
-function Lexer(text, parsStrings){
- this.text = text;
- // UTC dates have 20 characters, we send them through parser
- this.dateParseLength = parsStrings ? 20 : -1;
- this.tokens = [];
- this.index = 0;
-}
-
-Lexer.OPERATORS = {
- 'null':function(self){return null;},
+OPERATORS = {
+ 'null':function(self){return _null;},
'true':function(self){return true;},
'false':function(self){return false;},
- 'undefined':noop,
+ $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;},
@@ -31,149 +23,138 @@ 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);
+ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
+
+function lex(text, parseStrings){
+ var dateParseLength = parseStrings ? 20 : -1,
+ tokens = [],
+ index = 0,
+ canStartRegExp = true;
+
+ while (index < text.length) {
+ var ch = text.charAt(index);
+ if (ch == '"' || ch == "'") {
+ readString(ch);
+ canStartRegExp = true;
+ } else if (ch == '(' || ch == '[') {
+ tokens.push({index:index, text:ch});
+ index++;
+ } else if (ch == '{' ) {
+ var peekCh = peek();
+ if (peekCh == ':' || peekCh == '(') {
+ tokens.push({index:index, text:ch + peekCh});
+ index++;
+ } else {
+ tokens.push({index:index, text:ch});
+ }
+ index++;
+ canStartRegExp = true;
+ } else if (ch == ')' || ch == ']' || ch == '}' ) {
+ tokens.push({index:index, text:ch});
+ index++;
+ canStartRegExp = false;
+ } else if ( ch == ':' || ch == '.' || ch == ',' || ch == ';') {
+ tokens.push({index:index, text:ch});
+ index++;
+ canStartRegExp = true;
+ } else if ( canStartRegExp && ch == '/' ) {
+ readRegexp();
+ canStartRegExp = false;
+ } else if ( isNumber(ch) ) {
+ readNumber();
+ canStartRegExp = false;
+ } else if (isIdent(ch)) {
+ readIdent();
+ canStartRegExp = false;
+ } else if (isWhitespace(ch)) {
+ index++;
} 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 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++;
+ 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});
+ index += 1;
} 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;
+ throw "Lexer Error: Unexpected next character [" +
+ text.substring(index) +
+ "] in expression '" + text +
+ "' at column '" + (index+1) + "'.";
}
+ canStartRegExp = true;
}
- return tokens;
- },
+ }
+ return tokens;
- isNumber: function(ch) {
+ function peek() {
+ return index + 1 < text.length ? text.charAt(index + 1) : false;
+ }
+ function isNumber(ch) {
return '0' <= ch && ch <= '9';
- },
-
- isWhitespace: function(ch) {
+ }
+ function isWhitespace(ch) {
return ch == ' ' || ch == '\r' || ch == '\t' ||
ch == '\n' || ch == '\v';
- },
-
- isIdent: function(ch) {
+ }
+ function isIdent(ch) {
return 'a' <= ch && ch <= 'z' ||
'A' <= ch && ch <= 'Z' ||
'_' == ch || ch == '$';
- },
-
- readNumber: function() {
+ }
+ function readNumber() {
var number = "";
- var start = this.index;
- while (this.index < this.text.length) {
- var ch = this.text.charAt(this.index);
- if (ch == '.' || this.isNumber(ch)) {
+ var start = index;
+ while (index < text.length) {
+ var ch = text.charAt(index);
+ if (ch == '.' || isNumber(ch)) {
number += ch;
} else {
break;
}
- this.index++;
+ index++;
}
number = 1 * number;
- this.tokens.push({index:start, text:number,
+ tokens.push({index:start, text:number,
fn:function(){return number;}});
- },
-
- readIdent: function() {
+ }
+ function readIdent() {
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)) {
+ var start = index;
+ while (index < text.length) {
+ var ch = text.charAt(index);
+ if (ch == '.' || isIdent(ch) || isNumber(ch)) {
ident += ch;
} else {
break;
}
- this.index++;
+ index++;
}
- var fn = Lexer.OPERATORS[ident];
+ var fn = OPERATORS[ident];
if (!fn) {
fn = getterFn(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++;
+ tokens.push({index:start, text:ident, fn:fn});
+ }
+ function readString(quote) {
+ var start = index;
+ index++;
var string = "";
var rawString = quote;
var escape = false;
- while (this.index < this.text.length) {
- var ch = this.text.charAt(this.index);
+ while (index < text.length) {
+ var ch = text.charAt(index);
rawString += ch;
if (escape) {
if (ch == 'u') {
- var hex = this.text.substring(this.index + 1, this.index + 5);
- this.index += 4;
+ var hex = text.substring(index + 1, index + 5);
+ index += 4;
string += String.fromCharCode(parseInt(hex, 16));
} else {
- var rep = Lexer.ESCAPE[ch];
+ var rep = ESCAPE[ch];
if (rep) {
string += rep;
} else {
@@ -184,8 +165,8 @@ Lexer.prototype = {
} else if (ch == '\\') {
escape = true;
} else if (ch == quote) {
- this.index++;
- this.tokens.push({index:start, text:rawString, string:string,
+ index++;
+ tokens.push({index:start, text:rawString, string:string,
fn:function(){
return (string.length == dateParseLength) ?
angular['String']['toDate'](string) : string;
@@ -194,20 +175,19 @@ Lexer.prototype = {
} else {
string += ch;
}
- this.index++;
+ index++;
}
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++;
+ text.substring(start) + "] starting at column '" +
+ (start+1) + "' in expression '" + text + "'.";
+ }
+ function readRegexp(quote) {
+ var start = index;
+ index++;
var regexp = "";
var escape = false;
- while (this.index < this.text.length) {
- var ch = this.text.charAt(this.index);
+ while (index < text.length) {
+ var ch = text.charAt(index);
if (escape) {
regexp += ch;
escape = false;
@@ -215,36 +195,36 @@ Lexer.prototype = {
regexp += ch;
escape = true;
} else if (ch === '/') {
- this.index++;
+ index++;
var flags = "";
- if (this.isIdent(this.text.charAt(this.index))) {
- this.readIdent();
- flags = this.tokens.pop().text;
+ if (isIdent(text.charAt(index))) {
+ readIdent();
+ flags = tokens.pop().text;
}
var compiledRegexp = new RegExp(regexp, flags);
- this.tokens.push({index:start, text:regexp, flags:flags,
+ tokens.push({index:start, text:regexp, flags:flags,
fn:function(){return compiledRegexp;}});
return;
} else {
regexp += ch;
}
- this.index++;
+ index++;
}
throw "Lexer Error: Unterminated RegExp [" +
- this.text.substring(start) + "] starting at column '" +
- (start+1) + "' in expression '" + this.text + "'.";
+ text.substring(start) + "] starting at column '" +
+ (start+1) + "' in expression '" + text + "'.";
}
-};
+}
/////////////////////////////////////////
function Parser(text, parseStrings){
this.text = text;
- this.tokens = new Lexer(text, parseStrings).parse();
+ this.tokens = lex(text, parseStrings);
this.index = 0;
}
-Parser.ZERO = function(){
+ZERO = function(){
return 0;
};
@@ -472,7 +452,7 @@ Parser.prototype = {
if (this.expect('+')) {
return this.primary();
} else if (token = this.expect('-')) {
- return this._binary(Parser.ZERO, token.fn, this.unary());
+ return this._binary(ZERO, token.fn, this.unary());
} else if (token = this.expect('!')) {
return this._unary(token.fn, this.unary());
} else {
@@ -490,7 +470,7 @@ Parser.prototype = {
if (instance)
instance = instance[key];
}
- if (typeof instance != 'function') {
+ if (typeof instance != $function) {
throw "Function '" + token.text + "' at column '" +
(token.index+1) + "' in '" + this.text + "' is not defined.";
}
@@ -507,10 +487,6 @@ Parser.prototype = {
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 {
var token = this.expect();
primary = token.fn;
@@ -533,32 +509,6 @@ Parser.prototype = {
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(":");
- }
- var statements = this.statements();
- this.consume("}");
- return function(self) {
- return function($){
- var scope = createScope(self);
- scope['$'] = $;
- for ( var i = 0; i < args.length; i++) {
- setter(scope, args[i], arguments[i]);
- }
- return statements(scope);
- };
- };
- },
-
fieldAccess: function(object) {
var field = this.expect().text;
var getter = getterFn(field);
@@ -581,7 +531,7 @@ Parser.prototype = {
return function (self){
var o = obj(self);
var i = indexFn(self);
- return (o) ? o[i] : undefined;
+ return (o) ? o[i] : _undefined;
};
}
},
@@ -601,8 +551,8 @@ Parser.prototype = {
}
var fnPtr = fn(self) || noop;
// IE stupidity!
- return fnPtr.apply ?
- fnPtr.apply(self, args) :
+ return fnPtr.apply ?
+ fnPtr.apply(self, args) :
fnPtr(args[0], args[1], args[2], args[3], args[4]);
};
},
@@ -648,51 +598,6 @@ Parser.prototype = {
};
},
- entityDeclaration: function () {
- var decl = [];
- while(this.hasTokens()) {
- decl.push(this.entityDecl());
- if (!this.expect(';')) {
- this.assertAllConsumed();
- }
- }
- 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;
- }
- if (this.expect(':')) {
- defaults = this.primary()(null);
- }
- return function(self) {
- var Entity = self.datastore.entity(entity, defaults);
- setter(self, entity, Entity);
- if (instance) {
- var document = Entity();
- document['$$anchor'] = instance;
- setter(self, 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()) {