diff options
| -rw-r--r-- | src/ng/parse.js | 32 | ||||
| -rw-r--r-- | test/ng/parseSpec.js | 54 | 
2 files changed, 83 insertions, 3 deletions
diff --git a/src/ng/parse.js b/src/ng/parse.js index 26c41a90..4616d15d 100644 --- a/src/ng/parse.js +++ b/src/ng/parse.js @@ -58,7 +58,7 @@ function lex(text, csp){           (token=tokens[tokens.length-1])) {          token.json = token.text.indexOf('.') == -1;        } -    } else if (is('(){}[].,;:')) { +    } else if (is('(){}[].,;:?')) {        tokens.push({          index:index,          text:ch, @@ -359,6 +359,14 @@ function parser(text, json, $filter, csp){      });    } +  function ternaryFn(left, middle, right){ +    return extend(function(self, locals){ +      return left(self, locals) ? middle(self, locals) : right(self, locals); +    }, { +      constant: left.constant && middle.constant && right.constant +    }); +  } +      function binaryFn(left, fn, right) {      return extend(function(self, locals) {        return fn(self, locals, left, right); @@ -429,7 +437,7 @@ function parser(text, json, $filter, csp){    }    function _assignment() { -    var left = logicalOR(); +    var left = ternary();      var right;      var token;      if ((token = expect('='))) { @@ -437,7 +445,7 @@ function parser(text, json, $filter, csp){          throwError("implies assignment but [" +            text.substring(0, token.index) + "] can not be assigned to", token);        } -      right = logicalOR(); +      right = ternary();        return function(scope, locals){          return left.assign(scope, right(scope, locals), locals);        }; @@ -446,6 +454,24 @@ function parser(text, json, $filter, csp){      }    } +  function ternary() { +    var left = logicalOR(); +    var middle; +    var token; +    if((token = expect('?'))){ +      middle = ternary(); +      if((token = expect(':'))){ +        return ternaryFn(left, middle, ternary()); +      } +      else { +        throwError('expected :', token); +      } +    } +    else { +      return left; +    } +  } +      function logicalOR() {      var left = logicalAND();      var token; diff --git a/test/ng/parseSpec.js b/test/ng/parseSpec.js index 96b3d909..113ec051 100644 --- a/test/ng/parseSpec.js +++ b/test/ng/parseSpec.js @@ -103,6 +103,14 @@ describe('parser', function() {        expect(tokens[7].text).toEqual('===');        expect(tokens[8].text).toEqual('!==');      }); +     +    it('should tokenize logical and ternary', function() { +      var tokens = lex("&& || ? :"); +      expect(tokens[0].text).toEqual('&&'); +      expect(tokens[1].text).toEqual('||'); +      expect(tokens[2].text).toEqual('?'); +      expect(tokens[3].text).toEqual(':'); +    });      it('should tokenize statements', function() {        var tokens = lex("a;b;"); @@ -220,6 +228,52 @@ describe('parser', function() {          expect(scope.$eval("0||2")).toEqual(0||2);          expect(scope.$eval("0||1&&2")).toEqual(0||1&&2);        }); +       +      it('should parse ternary', function(){ +        var f = scope.f = function(){ return true; }; +        var g = scope.g = function(){ return false; }; +        var h = scope.h = function(){ return 'asd'; }; +        var i = scope.i = function(){ return 123; }; +        var id = scope.id = function(x){ return x; }; + +        // Simple. +        expect(scope.$eval('0?0:2')).toEqual(0?0:2); +        expect(scope.$eval('1?0:2')).toEqual(1?0:2); +         +        // Nested on the left. +        expect(scope.$eval('0?0?0:0:2')).toEqual(0?0?0:0:2); +        expect(scope.$eval('1?0?0:0:2')).toEqual(1?0?0:0:2); +        expect(scope.$eval('0?1?0:0:2')).toEqual(0?1?0:0:2); +        expect(scope.$eval('0?0?1:0:2')).toEqual(0?0?1:0:2); +        expect(scope.$eval('0?0?0:2:3')).toEqual(0?0?0:2:3); +        expect(scope.$eval('1?1?0:0:2')).toEqual(1?1?0:0:2); +        expect(scope.$eval('1?1?1:0:2')).toEqual(1?1?1:0:2); +        expect(scope.$eval('1?1?1:2:3')).toEqual(1?1?1:2:3); +        expect(scope.$eval('1?1?1:2:3')).toEqual(1?1?1:2:3); +         +        // Nested on the right. +        expect(scope.$eval('0?0:0?0:2')).toEqual(0?0:0?0:2); +        expect(scope.$eval('1?0:0?0:2')).toEqual(1?0:0?0:2); +        expect(scope.$eval('0?1:0?0:2')).toEqual(0?1:0?0:2); +        expect(scope.$eval('0?0:1?0:2')).toEqual(0?0:1?0:2); +        expect(scope.$eval('0?0:0?2:3')).toEqual(0?0:0?2:3); +        expect(scope.$eval('1?1:0?0:2')).toEqual(1?1:0?0:2); +        expect(scope.$eval('1?1:1?0:2')).toEqual(1?1:1?0:2); +        expect(scope.$eval('1?1:1?2:3')).toEqual(1?1:1?2:3); +        expect(scope.$eval('1?1:1?2:3')).toEqual(1?1:1?2:3); +         +        // Precedence with respect to logical operators. +        expect(scope.$eval('0&&1?0:1')).toEqual(0&&1?0:1); +        expect(scope.$eval('0&&1?0:1')).toEqual((0&&1)?0:1); +        expect(scope.$eval('1||0?0:0')).toEqual(1||0?0:0); +        expect(scope.$eval('1||0?0:0')).toEqual((1||0)?0:0); +         +        // Function calls. +        expect(scope.$eval('f() ? h() : i()')).toEqual(f() ? h() : i()); +        expect(scope.$eval('g() ? h() : i()')).toEqual(g() ? h() : i()); +        expect(scope.$eval('f() ? h() : i()')).toEqual(f() ? h() : i()); +        expect(scope.$eval('id(g() ? h() : i())')).toEqual(id(g() ? h() : i())); +      });        it('should parse string', function() {          expect(scope.$eval("'a' + 'b c'")).toEqual("ab c");  | 
