aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/parser.js137
-rw-r--r--test/ParserSpec.js22
2 files changed, 103 insertions, 56 deletions
diff --git a/src/parser.js b/src/parser.js
index 47b23e7e..a9b66c52 100644
--- a/src/parser.js
+++ b/src/parser.js
@@ -32,7 +32,7 @@ function lex(text, parseStringsForObjects){
index = 0,
json = [],
ch,
- lastCh = ':';
+ lastCh = ':'; // can start regexp
while (index < text.length) {
ch = text.charAt(index);
@@ -76,9 +76,6 @@ function lex(text, parseStringsForObjects){
lastCh = ch;
}
return tokens;
-
-
- //////////////////////////////////////////////
function is(chars) {
return chars.indexOf(ch) != -1;
@@ -103,6 +100,10 @@ function lex(text, parseStringsForObjects){
'A' <= ch && ch <= 'Z' ||
'_' == ch || ch == '$';
}
+ function isExpOperator(ch) {
+ return ch == '-' || ch == '+' || isNumber(ch);
+ }
+
function throwError(error, start, end) {
end = end || index;
throw Error("Lexer Error: " + error + " at column" +
@@ -111,61 +112,103 @@ function lex(text, parseStringsForObjects){
" " + end) +
" in expression [" + text + "].");
}
-
- function consume(regexp, processToken, errorMsg) {
- var match = text.substr(index).match(regexp);
- var token = {index: index};
- var start = index;
- if (!match) throwError(errorMsg);
- index += match[0].length;
- processToken(token, token.text = match[0], start);
- tokens.push(token);
- }
function readNumber() {
- consume(/^(\d+)?(\.\d+)?([eE][+-]?\d+)?/, function(token, number){
- token.text = number = 1 * number;
- token.json = true;
- token.fn = valueFn(number);
- }, "Not a valid number");
+ var number = "";
+ var start = index;
+ while (index < text.length) {
+ var ch = lowercase(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') {
+ throwError('Invalid exponent');
+ } else {
+ break;
+ }
+ }
+ index++;
+ }
+ number = 1 * number;
+ tokens.push({index:start, text:number, json:true,
+ fn:function(){return number;}});
}
-
function readIdent() {
- consume(/^[\w_\$][\w_\$\d]*(\.[\w_\$][\w_\$\d]*)*/, function(token, ident){
- fn = OPERATORS[ident];
- if (!fn) {
- fn = getterFn(ident);
- fn.isAssignable = ident;
+ var ident = "";
+ var start = index;
+ var fn;
+ while (index < text.length) {
+ var ch = text.charAt(index);
+ if (ch == '.' || isIdent(ch) || isNumber(ch)) {
+ ident += ch;
+ } else {
+ break;
}
- token.fn = OPERATORS[ident]||extend(getterFn(ident), {
+ index++;
+ }
+ fn = OPERATORS[ident];
+ tokens.push({
+ index:start,
+ text:ident,
+ json: fn,
+ fn:fn||extend(getterFn(ident), {
assign:function(self, value){
return setter(self, ident, value);
}
- });
- token.json = OPERATORS[ident];
+ })
});
}
function readString(quote) {
- consume(/^(('(\\'|[^'])*')|("(\\"|[^"])*"))/, function(token, rawString, start){
- var hasError;
- var string = token.string = rawString.substr(1, rawString.length - 2).
- replace(/(\\u(.?.?.?.?))|(\\(.))/g,
- function(match, wholeUnicode, unicode, wholeEscape, escape){
- if (unicode && !unicode.match(/[\da-fA-F]{4}/))
- hasError = hasError || bind(null, throwError, "Invalid unicode escape [\\u" + unicode + "]", start);
- return unicode ?
- String.fromCharCode(parseInt(unicode, 16)) :
- ESCAPE[escape] || escape;
- });
- (hasError||noop)();
- token.json = true;
- token.fn = function(){
- return (string.length == dateParseLength) ?
- angular['String']['toDate'](string) :
- string;
- };
- }, "Unterminated string");
+ 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))
+ throwError( "Invalid unicode escape [\\u" + hex + "]");
+ 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++;
+ }
+ throwError("Unterminated quote", start);
}
}
diff --git a/test/ParserSpec.js b/test/ParserSpec.js
index 71208783..c237aa40 100644
--- a/test/ParserSpec.js
+++ b/test/ParserSpec.js
@@ -82,15 +82,9 @@ describe('parser', function() {
expect(tokens.length).toEqual(1);
expect(tokens[0].string).toEqual('\u00a0');
});
-
- it('should error when non terminated string', function(){
- expect(function(){
- lex('ignore "text');
- }).toThrow(new Error('Lexer Error: Unterminated string at column 7 in expression [ignore "text].'));
- });
it('should ignore whitespace', function() {
- var tokens = lex("a \t \n \r \u00A0 b");
+ var tokens = lex("a \t \n \r b");
expect(tokens[0].text).toEqual('a');
expect(tokens[1].text).toEqual('b');
});
@@ -136,6 +130,16 @@ describe('parser', function() {
expect(tokens[0].text).toEqual(0.5E+10);
});
+ it('should throws exception for invalid exponent', function() {
+ expect(function() {
+ lex("0.5E-");
+ }).toThrow(new Error('Lexer Error: Invalid exponent at column 4 in expression [0.5E-].'));
+
+ expect(function() {
+ lex("0.5E-A");
+ }).toThrow(new Error('Lexer Error: Invalid exponent at column 4 in expression [0.5E-A].'));
+ });
+
it('should tokenize number starting with a dot', function() {
var tokens = lex(".5");
expect(tokens[0].text).toEqual(0.5);
@@ -143,8 +147,8 @@ describe('parser', function() {
it('should throw error on invalid unicode', function() {
expect(function() {
- lex("'\\u1xbla'");
- }).toThrow(new Error("Lexer Error: Invalid unicode escape [\\u1xbl] at columns 0-9 ['\\u1xbla'] in expression ['\\u1xbla']."));
+ lex("'\\u1''bla'");
+ }).toThrow(new Error("Lexer Error: Invalid unicode escape [\\u1''b] at column 2 in expression ['\\u1''bla']."));
});
});