Rename ol.expression to ol.expr

This commit is contained in:
Tim Schaub
2013-06-21 17:29:16 -06:00
parent 9928730bd3
commit 2577d3f7d6
28 changed files with 1550 additions and 1548 deletions

View File

@@ -0,0 +1,679 @@
goog.provide('ol.test.expression');
describe('ol.expr.parse()', function() {
it('parses a subset of ECMAScript 5.1 expressions', function() {
var expr = ol.expr.parse('foo');
expect(expr).to.be.a(ol.expr.Expression);
});
describe('11.1 - primary expressions', function() {
// http://www.ecma-international.org/ecma-262/5.1/#sec-11.1
it('parses identifier expressions', function() {
var expr = ol.expr.parse('foo');
expect(expr).to.be.a(ol.expr.Identifier);
expect(expr.evaluate({foo: 'bar'})).to.be('bar');
});
it('consumes whitespace as expected', function() {
var expr = ol.expr.parse(' foo ');
expect(expr).to.be.a(ol.expr.Identifier);
expect(expr.evaluate({foo: 'bar'})).to.be('bar');
});
it('throws on invalid identifier expressions', function() {
expect(function() {
ol.expr.parse('3foo');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('f');
expect(token.index).to.be(1);
});
});
it('parses string literal expressions', function() {
var expr = ol.expr.parse('"foo"');
expect(expr).to.be.a(ol.expr.Literal);
expect(expr.evaluate()).to.be('foo');
});
it('throws on unterminated string', function() {
expect(function() {
ol.expr.parse('"foo');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.type).to.be(ol.expr.TokenType.EOF);
expect(token.index).to.be(4);
});
});
it('parses numeric literal expressions', function() {
var expr = ol.expr.parse('.42e+2');
expect(expr).to.be.a(ol.expr.Literal);
expect(expr.evaluate()).to.be(42);
});
it('throws on invalid number', function() {
expect(function() {
ol.expr.parse('.42eX');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('X');
expect(token.index).to.be(4);
});
});
it('parses boolean literal expressions', function() {
var expr = ol.expr.parse('false');
expect(expr).to.be.a(ol.expr.Literal);
expect(expr.evaluate()).to.be(false);
});
it('parses null literal expressions', function() {
var expr = ol.expr.parse('null');
expect(expr).to.be.a(ol.expr.Literal);
expect(expr.evaluate()).to.be(null);
});
});
describe('11.2 - left-hand-side expressions', function() {
// http://www.ecma-international.org/ecma-262/5.1/#sec-11.2
it('parses member expressions with dot notation', function() {
var expr = ol.expr.parse('foo.bar.baz');
expect(expr).to.be.a(ol.expr.Member);
var scope = {foo: {bar: {baz: 42}}};
expect(expr.evaluate(scope)).to.be(42);
});
it('consumes whitespace as expected', function() {
var expr = ol.expr.parse(' foo . bar . baz ');
expect(expr).to.be.a(ol.expr.Member);
var scope = {foo: {bar: {baz: 42}}};
expect(expr.evaluate(scope)).to.be(42);
});
it('throws on invalid member expression', function() {
expect(function() {
ol.expr.parse('foo.4bar');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('b');
expect(token.index).to.be(5);
});
});
it('parses call expressions with literal arguments', function() {
var expr = ol.expr.parse('foo(42, "bar")');
expect(expr).to.be.a(ol.expr.Call);
var scope = {
foo: function(num, str) {
expect(num).to.be(42);
expect(str).to.be('bar');
return str + num;
}
};
expect(expr.evaluate(scope)).to.be('bar42');
});
it('throws on calls with unterminated arguments', function() {
expect(function() {
ol.expr.parse('foo(42,)');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be(')');
expect(token.index).to.be(7);
});
});
});
describe('11.3 - postfix expressions', function() {
// http://www.ecma-international.org/ecma-262/5.1/#sec-11.3
it('not supported');
});
describe('11.4 - unary operators', function() {
// http://www.ecma-international.org/ecma-262/5.1/#sec-11.4
it('parses logical not operator', function() {
var expr = ol.expr.parse('!foo');
expect(expr).to.be.a(ol.expr.Not);
expect(expr.evaluate({foo: true})).to.be(false);
expect(expr.evaluate({foo: false})).to.be(true);
expect(expr.evaluate({foo: ''})).to.be(true);
expect(expr.evaluate({foo: 'foo'})).to.be(false);
});
it('consumes whitespace as expected', function() {
var expr = ol.expr.parse(' ! foo');
expect(expr).to.be.a(ol.expr.Not);
expect(expr.evaluate({foo: true})).to.be(false);
expect(expr.evaluate({foo: false})).to.be(true);
});
});
describe('11.5 - multiplicitave operators', function() {
// http://www.ecma-international.org/ecma-262/5.1/#sec-11.5
it('parses * operator', function() {
var expr = ol.expr.parse('foo*bar');
expect(expr).to.be.a(ol.expr.Math);
expect(expr.evaluate({foo: 10, bar: 20})).to.be(200);
});
it('consumes whitespace as expected with *', function() {
var expr = ol.expr.parse(' foo * bar ');
expect(expr).to.be.a(ol.expr.Math);
expect(expr.evaluate({foo: 15, bar: 2})).to.be(30);
});
it('parses / operator', function() {
var expr = ol.expr.parse('foo/12');
expect(expr).to.be.a(ol.expr.Math);
expect(expr.evaluate({foo: 10})).to.be(10 / 12);
});
it('consumes whitespace as expected with /', function() {
var expr = ol.expr.parse(' 4 / bar ');
expect(expr).to.be.a(ol.expr.Math);
expect(expr.evaluate({bar: 3})).to.be(4 / 3);
});
it('parses % operator', function() {
var expr = ol.expr.parse('12%foo');
expect(expr).to.be.a(ol.expr.Math);
expect(expr.evaluate({foo: 10})).to.be(2);
});
it('consumes whitespace as expected with %', function() {
var expr = ol.expr.parse(' 4 %bar ');
expect(expr).to.be.a(ol.expr.Math);
expect(expr.evaluate({bar: 3})).to.be(1);
});
});
describe('11.6 - additive operators', function() {
// http://www.ecma-international.org/ecma-262/5.1/#sec-11.6
it('parses + operator', function() {
var expr = ol.expr.parse('foo+bar');
expect(expr).to.be.a(ol.expr.Math);
expect(expr.evaluate({foo: 10, bar: 20})).to.be(30);
});
it('consumes whitespace as expected with +', function() {
var expr = ol.expr.parse(' foo +10 ');
expect(expr).to.be.a(ol.expr.Math);
expect(expr.evaluate({foo: 15})).to.be(25);
});
it('parses - operator', function() {
var expr = ol.expr.parse('foo-bar');
expect(expr).to.be.a(ol.expr.Math);
expect(expr.evaluate({foo: 10, bar: 20})).to.be(-10);
});
it('consumes whitespace as expected with -', function() {
var expr = ol.expr.parse(' foo- 10 ');
expect(expr).to.be.a(ol.expr.Math);
expect(expr.evaluate({foo: 15})).to.be(5);
});
});
describe('11.7 - bitwise shift operators', function() {
// http://www.ecma-international.org/ecma-262/5.1/#sec-11.7
it('not supported');
});
describe('11.8 - relational operators', function() {
// http://www.ecma-international.org/ecma-262/5.1/#sec-11.8
it('parses < operator', function() {
var expr = ol.expr.parse('foo<bar');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 10, bar: 20})).to.be(true);
expect(expr.evaluate({foo: 100, bar: 20})).to.be(false);
});
it('consumes whitespace as expected with <', function() {
var expr = ol.expr.parse(' foo <10 ');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 15})).to.be(false);
expect(expr.evaluate({foo: 5})).to.be(true);
});
it('parses > operator', function() {
var expr = ol.expr.parse('foo>bar');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 10, bar: 20})).to.be(false);
expect(expr.evaluate({foo: 100, bar: 20})).to.be(true);
});
it('consumes whitespace as expected with >', function() {
var expr = ol.expr.parse(' foo> 10 ');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 15})).to.be(true);
expect(expr.evaluate({foo: 5})).to.be(false);
});
it('parses <= operator', function() {
var expr = ol.expr.parse('foo<=bar');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 10, bar: 20})).to.be(true);
expect(expr.evaluate({foo: 100, bar: 20})).to.be(false);
expect(expr.evaluate({foo: 20, bar: 20})).to.be(true);
});
it('consumes whitespace as expected with <=', function() {
var expr = ol.expr.parse(' foo<= 10 ');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 15})).to.be(false);
expect(expr.evaluate({foo: 5})).to.be(true);
expect(expr.evaluate({foo: 10})).to.be(true);
});
it('throws for invalid spacing with <=', function() {
expect(function() {
ol.expr.parse(' foo< = 10 ');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('=');
expect(token.index).to.be(6);
});
});
it('parses >= operator', function() {
var expr = ol.expr.parse('foo>=bar');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 10, bar: 20})).to.be(false);
expect(expr.evaluate({foo: 100, bar: 20})).to.be(true);
expect(expr.evaluate({foo: 20, bar: 20})).to.be(true);
});
it('consumes whitespace as expected with >=', function() {
var expr = ol.expr.parse(' foo >=10 ');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 15})).to.be(true);
expect(expr.evaluate({foo: 5})).to.be(false);
expect(expr.evaluate({foo: 10})).to.be(true);
});
it('throws for invalid spacing with >=', function() {
expect(function() {
ol.expr.parse(' 10 > =foo ');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('=');
expect(token.index).to.be(6);
});
});
});
describe('11.9 - equality operators', function() {
// http://www.ecma-international.org/ecma-262/5.1/#sec-11.9
it('parses == operator', function() {
var expr = ol.expr.parse('foo==42');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 42})).to.be(true);
expect(expr.evaluate({foo: 41})).to.be(false);
expect(expr.evaluate({foo: '42'})).to.be(true);
});
it('consumes whitespace as expected with ==', function() {
var expr = ol.expr.parse(' 42 ==foo ');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 42})).to.be(true);
expect(expr.evaluate({foo: 41})).to.be(false);
expect(expr.evaluate({foo: '42'})).to.be(true);
});
it('throws for invalid spacing with ==', function() {
expect(function() {
ol.expr.parse(' 10 = =foo ');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('=');
expect(token.index).to.be(4);
});
});
it('parses != operator', function() {
var expr = ol.expr.parse('foo!=42');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 42})).to.be(false);
expect(expr.evaluate({foo: 41})).to.be(true);
expect(expr.evaluate({foo: '42'})).to.be(false);
});
it('consumes whitespace as expected with !=', function() {
var expr = ol.expr.parse(' 42 !=foo ');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 42})).to.be(false);
expect(expr.evaluate({foo: 41})).to.be(true);
expect(expr.evaluate({foo: '42'})).to.be(false);
});
it('throws for invalid spacing with !=', function() {
expect(function() {
ol.expr.parse(' 10! =foo ');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('!');
expect(token.index).to.be(3);
});
});
it('parses === operator', function() {
var expr = ol.expr.parse('42===foo');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 42})).to.be(true);
expect(expr.evaluate({foo: 41})).to.be(false);
expect(expr.evaluate({foo: '42'})).to.be(false);
});
it('consumes whitespace as expected with ===', function() {
var expr = ol.expr.parse(' foo ===42 ');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 42})).to.be(true);
expect(expr.evaluate({foo: 41})).to.be(false);
expect(expr.evaluate({foo: '42'})).to.be(false);
});
it('throws for invalid spacing with ===', function() {
expect(function() {
ol.expr.parse(' 10 = == foo ');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('=');
expect(token.index).to.be(4);
});
});
it('parses !== operator', function() {
var expr = ol.expr.parse('foo!==42');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 42})).to.be(false);
expect(expr.evaluate({foo: 41})).to.be(true);
expect(expr.evaluate({foo: '42'})).to.be(true);
});
it('consumes whitespace as expected with !==', function() {
var expr = ol.expr.parse(' 42 !== foo ');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate({foo: 42})).to.be(false);
expect(expr.evaluate({foo: 41})).to.be(true);
expect(expr.evaluate({foo: '42'})).to.be(true);
});
it('throws for invalid spacing with !==', function() {
expect(function() {
ol.expr.parse(' 10 != = foo ');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('=');
expect(token.index).to.be(7);
});
});
});
describe('11.10 - binary bitwise operators', function() {
// http://www.ecma-international.org/ecma-262/5.1/#sec-11.10
it('not supported');
});
describe('11.11 - binary logical operators', function() {
// http://www.ecma-international.org/ecma-262/5.1/#sec-11.11
it('parses && operator', function() {
var expr = ol.expr.parse('foo&&bar');
expect(expr).to.be.a(ol.expr.Logical);
expect(expr.evaluate({foo: true, bar: true})).to.be(true);
expect(expr.evaluate({foo: true, bar: false})).to.be(false);
expect(expr.evaluate({foo: false, bar: true})).to.be(false);
expect(expr.evaluate({foo: false, bar: false})).to.be(false);
});
it('consumes space as expected with &&', function() {
var expr = ol.expr.parse(' foo && bar ');
expect(expr).to.be.a(ol.expr.Logical);
expect(expr.evaluate({foo: true, bar: true})).to.be(true);
expect(expr.evaluate({foo: true, bar: false})).to.be(false);
expect(expr.evaluate({foo: false, bar: true})).to.be(false);
expect(expr.evaluate({foo: false, bar: false})).to.be(false);
});
it('throws for invalid spacing with &&', function() {
expect(function() {
ol.expr.parse('true & & false');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('&');
expect(token.index).to.be(5);
});
});
it('parses || operator', function() {
var expr = ol.expr.parse('foo||bar');
expect(expr).to.be.a(ol.expr.Logical);
expect(expr.evaluate({foo: true, bar: true})).to.be(true);
expect(expr.evaluate({foo: true, bar: false})).to.be(true);
expect(expr.evaluate({foo: false, bar: true})).to.be(true);
expect(expr.evaluate({foo: false, bar: false})).to.be(false);
});
it('consumes space as expected with ||', function() {
var expr = ol.expr.parse(' foo || bar ');
expect(expr).to.be.a(ol.expr.Logical);
expect(expr.evaluate({foo: true, bar: true})).to.be(true);
expect(expr.evaluate({foo: true, bar: false})).to.be(true);
expect(expr.evaluate({foo: false, bar: true})).to.be(true);
expect(expr.evaluate({foo: false, bar: false})).to.be(false);
});
it('throws for invalid spacing with ||', function() {
expect(function() {
ol.expr.parse('true | | false');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('|');
expect(token.index).to.be(5);
});
});
});
describe('11.12 - conditional operator', function() {
// http://www.ecma-international.org/ecma-262/5.1/#sec-11.12
it('not supported');
});
describe('11.13 - assignment operators', function() {
// http://www.ecma-international.org/ecma-262/5.1/#sec-11.13
it('not supported');
});
describe('11.14 - comma operator', function() {
// http://www.ecma-international.org/ecma-262/5.1/#sec-11.14
it('not supported');
});
});
describe('ol.expr.lib', function() {
var parse = ol.expr.parse;
var evaluate = ol.expr.evaluateFeature;
describe('extent()', function() {
var nw = new ol.Feature({
geom: new ol.geom.Polygon([[
[-180, 90], [0, 90], [0, 0], [-180, 0], [-180, 90]
]])
});
var se = new ol.Feature({
geom: new ol.geom.Polygon([[
[180, -90], [0, -90], [0, 0], [180, 0], [180, -90]
]])
});
var north = parse('extent(-100, 100, 40, 60)');
var south = parse('extent(-100, 100, -60, -40)');
var east = parse('extent(80, 100, -50, 50)');
var west = parse('extent(-100, -80, -50, 50)');
it('evaluates to true for features within given extent', function() {
expect(evaluate(north, nw), true);
expect(evaluate(south, nw), false);
expect(evaluate(east, nw), false);
expect(evaluate(west, nw), true);
expect(evaluate(north, se), false);
expect(evaluate(south, se), true);
expect(evaluate(east, se), true);
expect(evaluate(west, se), false);
});
});
describe('geometryType()', function() {
var point = new ol.Feature({
geom: new ol.geom.Point([0, 0])
});
var line = new ol.Feature({
geom: new ol.geom.LineString([[180, -90], [-180, 90]])
});
var poly = new ol.Feature({
geom: new ol.geom.Polygon([[
[180, -90], [0, -90], [0, 0], [180, 0], [180, -90]
]])
});
var isPoint = parse('geometryType("point")');
var isLine = parse('geometryType("linestring")');
var isPoly = parse('geometryType("polygon")');
var pointOrPoly = parse('geometryType("point") || geometryType("polygon")');
it('distinguishes point features', function() {
expect(evaluate(isPoint, point), true);
expect(evaluate(isPoint, line), false);
expect(evaluate(isPoint, poly), false);
});
it('distinguishes line features', function() {
expect(evaluate(isLine, point), false);
expect(evaluate(isLine, line), true);
expect(evaluate(isLine, poly), false);
});
it('distinguishes polygon features', function() {
expect(evaluate(isPoly, point), false);
expect(evaluate(isPoly, line), false);
expect(evaluate(isPoly, poly), true);
});
it('can be composed in a logical expression', function() {
expect(evaluate(pointOrPoly, point), true);
expect(evaluate(pointOrPoly, line), false);
expect(evaluate(pointOrPoly, poly), true);
});
});
});
describe('ol.expr.register()', function() {
var spy;
beforeEach(function() {
spy = sinon.spy();
});
it('registers custom functions in ol.expr.lib', function() {
ol.expr.register('someFunc', spy);
expect(ol.expr.lib.someFunc).to.be(spy);
});
it('allows custom functions to be called', function() {
ol.expr.register('myFunc', spy);
var expr = ol.expr.parse('myFunc(42)');
expr.evaluate(null, ol.expr.lib);
expect(spy.calledOnce);
expect(spy.calledWithExactly(42));
});
it('allows custom functions to be called with identifiers', function() {
ol.expr.register('myFunc', spy);
var expr = ol.expr.parse('myFunc(foo, 42)');
expr.evaluate({foo: 'bar'}, ol.expr.lib);
expect(spy.calledOnce);
expect(spy.calledWithExactly('bar', 42));
});
it('allows custom functions to be called with custom this obj', function() {
ol.expr.register('myFunc', spy);
var expr = ol.expr.parse('myFunc(foo, 42)');
var that = {};
expr.evaluate({foo: 'bar'}, ol.expr.lib, that);
expect(spy.calledOnce);
expect(spy.calledWithExactly('bar', 42));
expect(spy.calledOn(that));
});
it('allows overriding existing ol.expr.lib functions', function() {
var orig = ol.expr.lib.extent;
expect(orig).not.to.be(spy);
ol.expr.register('extent', spy);
expect(ol.expr.lib.extent).to.be(spy);
ol.expr.lib.extent = orig;
});
});
goog.require('ol.Feature');
goog.require('ol.expr');
goog.require('ol.expr.Call');
goog.require('ol.expr.Comparison');
goog.require('ol.expr.Expression');
goog.require('ol.expr.Identifier');
goog.require('ol.expr.Literal');
goog.require('ol.expr.Logical');
goog.require('ol.expr.Math');
goog.require('ol.expr.Member');
goog.require('ol.expr.Not');
goog.require('ol.expr.TokenType');
goog.require('ol.expr.UnexpectedToken');
goog.require('ol.geom.LineString');
goog.require('ol.geom.Point');
goog.require('ol.geom.Polygon');

View File

@@ -0,0 +1,638 @@
goog.provide('ol.test.expression.Expression');
describe('ol.expr.Call', function() {
describe('constructor', function() {
it('creates a new expression', function() {
var expr = new ol.expr.Call(
new ol.expr.Identifier('sqrt'),
[new ol.expr.Literal(42)]);
expect(expr).to.be.a(ol.expr.Expression);
expect(expr).to.be.a(ol.expr.Call);
});
});
describe('#evaluate()', function() {
var fns = {
sqrt: function(value) {
return Math.sqrt(value);
},
strConcat: function() {
return Array.prototype.join.call(arguments, '');
},
discouraged: function() {
return this.message;
}
};
it('calls method on scope with literal args', function() {
var expr = new ol.expr.Call(
new ol.expr.Identifier('sqrt'),
[new ol.expr.Literal(42)]);
expect(expr.evaluate(fns)).to.be(Math.sqrt(42));
});
it('accepts a separate scope for functions', function() {
var expr = new ol.expr.Call(
new ol.expr.Identifier('sqrt'),
[new ol.expr.Identifier('foo')]);
expect(expr.evaluate({foo: 42}, fns)).to.be(Math.sqrt(42));
});
it('accepts multiple expression arguments', function() {
var expr = new ol.expr.Call(
new ol.expr.Identifier('strConcat'),
[
new ol.expr.Identifier('foo'),
new ol.expr.Literal(' comes after '),
new ol.expr.Math(
ol.expr.MathOp.SUBTRACT,
new ol.expr.Identifier('foo'),
new ol.expr.Literal(1))
]);
expect(expr.evaluate({foo: 42}, fns)).to.be('42 comes after 41');
});
it('accepts optional this arg', function() {
var expr = new ol.expr.Call(
new ol.expr.Identifier('discouraged'), []);
var thisArg = {
message: 'avoid this'
};
expect(expr.evaluate(fns, null, thisArg)).to.be('avoid this');
});
});
var callee = new ol.expr.Identifier('sqrt');
var args = [new ol.expr.Literal(42)];
var expr = new ol.expr.Call(callee, args);
describe('#getArgs()', function() {
it('gets the callee expression', function() {
expect(expr.getArgs()).to.be(args);
});
});
describe('#getCallee()', function() {
it('gets the callee expression', function() {
expect(expr.getCallee()).to.be(callee);
});
});
});
describe('ol.expr.Comparison', function() {
describe('constructor', function() {
it('creates a new expression', function() {
var expr = new ol.expr.Comparison(
ol.expr.ComparisonOp.EQ,
new ol.expr.Identifier('foo'),
new ol.expr.Literal(42));
expect(expr).to.be.a(ol.expr.Expression);
expect(expr).to.be.a(ol.expr.Comparison);
});
});
describe('#evaluate()', function() {
it('compares with ==', function() {
var expr = new ol.expr.Comparison(
ol.expr.ComparisonOp.EQ,
new ol.expr.Identifier('foo'),
new ol.expr.Literal(42));
expect(expr.evaluate({foo: 42})).to.be(true);
expect(expr.evaluate({foo: '42'})).to.be(true);
expect(expr.evaluate({foo: true})).to.be(false);
expect(expr.evaluate({bar: true})).to.be(false);
});
it('compares with !=', function() {
var expr = new ol.expr.Comparison(
ol.expr.ComparisonOp.NEQ,
new ol.expr.Identifier('foo'),
new ol.expr.Literal(42));
expect(expr.evaluate({foo: 42})).to.be(false);
expect(expr.evaluate({foo: '42'})).to.be(false);
expect(expr.evaluate({foo: true})).to.be(true);
expect(expr.evaluate({bar: true})).to.be(true);
});
it('compares with ===', function() {
var expr = new ol.expr.Comparison(
ol.expr.ComparisonOp.STRICT_EQ,
new ol.expr.Identifier('foo'),
new ol.expr.Literal(42));
expect(expr.evaluate({foo: 42})).to.be(true);
expect(expr.evaluate({foo: '42'})).to.be(false);
expect(expr.evaluate({foo: true})).to.be(false);
expect(expr.evaluate({bar: true})).to.be(false);
});
it('compares with !==', function() {
var expr = new ol.expr.Comparison(
ol.expr.ComparisonOp.STRICT_NEQ,
new ol.expr.Identifier('foo'),
new ol.expr.Literal(42));
expect(expr.evaluate({foo: 42})).to.be(false);
expect(expr.evaluate({foo: '42'})).to.be(true);
expect(expr.evaluate({foo: true})).to.be(true);
expect(expr.evaluate({bar: true})).to.be(true);
});
it('compares with >', function() {
var expr = new ol.expr.Comparison(
ol.expr.ComparisonOp.GT,
new ol.expr.Identifier('foo'),
new ol.expr.Literal(42));
expect(expr.evaluate({foo: 42})).to.be(false);
expect(expr.evaluate({foo: 41})).to.be(false);
expect(expr.evaluate({foo: 43})).to.be(true);
});
it('compares with <', function() {
var expr = new ol.expr.Comparison(
ol.expr.ComparisonOp.LT,
new ol.expr.Identifier('foo'),
new ol.expr.Literal(42));
expect(expr.evaluate({foo: 42})).to.be(false);
expect(expr.evaluate({foo: 41})).to.be(true);
expect(expr.evaluate({foo: 43})).to.be(false);
});
it('compares with >=', function() {
var expr = new ol.expr.Comparison(
ol.expr.ComparisonOp.GTE,
new ol.expr.Identifier('foo'),
new ol.expr.Literal(42));
expect(expr.evaluate({foo: 42})).to.be(true);
expect(expr.evaluate({foo: 41})).to.be(false);
expect(expr.evaluate({foo: 43})).to.be(true);
});
it('compares with <=', function() {
var expr = new ol.expr.Comparison(
ol.expr.ComparisonOp.LTE,
new ol.expr.Identifier('foo'),
new ol.expr.Literal(42));
expect(expr.evaluate({foo: 42})).to.be(true);
expect(expr.evaluate({foo: 41})).to.be(true);
expect(expr.evaluate({foo: 43})).to.be(false);
});
});
describe('#isValidOp()', function() {
it('determines if a string is a valid operator', function() {
expect(ol.expr.Comparison.isValidOp('<')).to.be(true);
expect(ol.expr.Comparison.isValidOp('<')).to.be(true);
expect(ol.expr.Comparison.isValidOp('<=')).to.be(true);
expect(ol.expr.Comparison.isValidOp('<=')).to.be(true);
expect(ol.expr.Comparison.isValidOp('==')).to.be(true);
expect(ol.expr.Comparison.isValidOp('!=')).to.be(true);
expect(ol.expr.Comparison.isValidOp('===')).to.be(true);
expect(ol.expr.Comparison.isValidOp('!==')).to.be(true);
expect(ol.expr.Comparison.isValidOp('')).to.be(false);
expect(ol.expr.Comparison.isValidOp('+')).to.be(false);
expect(ol.expr.Comparison.isValidOp('-')).to.be(false);
expect(ol.expr.Comparison.isValidOp('&&')).to.be(false);
});
});
var op = ol.expr.ComparisonOp.LTE;
var left = new ol.expr.Identifier('foo');
var right = new ol.expr.Literal(42);
var expr = new ol.expr.Comparison(op, left, right);
describe('#getOperator()', function() {
it('gets the operator', function() {
expect(expr.getOperator()).to.be(op);
});
});
describe('#getLeft()', function() {
it('gets the left expression', function() {
expect(expr.getLeft()).to.be(left);
});
});
describe('#getRight()', function() {
it('gets the right expression', function() {
expect(expr.getRight()).to.be(right);
});
});
});
describe('ol.expr.Identifier', function() {
describe('constructor', function() {
it('creates a new expression', function() {
var expr = new ol.expr.Identifier('foo');
expect(expr).to.be.a(ol.expr.Expression);
expect(expr).to.be.a(ol.expr.Identifier);
});
});
describe('#evaluate()', function() {
it('returns a number from the scope', function() {
var expr = new ol.expr.Identifier('foo');
expect(expr.evaluate({foo: 42})).to.be(42);
});
it('returns a string from the scope', function() {
var expr = new ol.expr.Identifier('foo');
expect(expr.evaluate({foo: 'chicken'})).to.be('chicken');
});
it('returns a boolean from the scope', function() {
var expr = new ol.expr.Identifier('bar');
expect(expr.evaluate({bar: false})).to.be(false);
expect(expr.evaluate({bar: true})).to.be(true);
});
it('returns a null from the scope', function() {
var expr = new ol.expr.Identifier('nada');
expect(expr.evaluate({nada: null})).to.be(null);
});
it('works for unicode identifiers', function() {
var expr = new ol.expr.Identifier('\u03c0');
expect(expr.evaluate({'\u03c0': Math.PI})).to.be(Math.PI);
});
});
describe('#getName()', function() {
var expr = new ol.expr.Identifier('asdf');
expect(expr.getName()).to.be('asdf');
});
});
describe('ol.expr.Literal', function() {
describe('constructor', function() {
it('creates a new expression', function() {
var expr = new ol.expr.Literal(true);
expect(expr).to.be.a(ol.expr.Expression);
expect(expr).to.be.a(ol.expr.Literal);
});
});
describe('#evaluate()', function() {
it('works for numeric literal', function() {
var expr = new ol.expr.Literal(42e-11);
expect(expr.evaluate()).to.be(4.2e-10);
});
it('works for string literal', function() {
var expr = new ol.expr.Literal('asdf');
expect(expr.evaluate()).to.be('asdf');
});
it('works for boolean literal', function() {
var expr = new ol.expr.Literal(true);
expect(expr.evaluate()).to.be(true);
});
it('works for null literal', function() {
var expr = new ol.expr.Literal(null);
expect(expr.evaluate()).to.be(null);
});
});
describe('#getValue()', function() {
var expr = new ol.expr.Literal('asdf');
expect(expr.getValue()).to.be('asdf');
});
});
describe('ol.expr.Logical', function() {
describe('constructor', function() {
it('creates a new expression', function() {
var expr = new ol.expr.Logical(
ol.expr.LogicalOp.OR,
new ol.expr.Identifier('foo'),
new ol.expr.Identifier('bar'));
expect(expr).to.be.a(ol.expr.Expression);
expect(expr).to.be.a(ol.expr.Logical);
});
});
describe('#evaluate()', function() {
it('applies || to resolved identifiers', function() {
var expr = new ol.expr.Logical(
ol.expr.LogicalOp.OR,
new ol.expr.Identifier('foo'),
new ol.expr.Identifier('bar'));
expect(expr.evaluate({foo: true, bar: true})).to.be(true);
expect(expr.evaluate({foo: true, bar: false})).to.be(true);
expect(expr.evaluate({foo: false, bar: true})).to.be(true);
expect(expr.evaluate({foo: false, bar: false})).to.be(false);
});
it('applies && to resolved identifiers', function() {
var expr = new ol.expr.Logical(
ol.expr.LogicalOp.AND,
new ol.expr.Identifier('foo'),
new ol.expr.Identifier('bar'));
expect(expr.evaluate({foo: true, bar: true})).to.be(true);
expect(expr.evaluate({foo: true, bar: false})).to.be(false);
expect(expr.evaluate({foo: false, bar: true})).to.be(false);
expect(expr.evaluate({foo: false, bar: false})).to.be(false);
});
});
describe('#isValidOp()', function() {
it('determines if a string is a valid operator', function() {
expect(ol.expr.Logical.isValidOp('||')).to.be(true);
expect(ol.expr.Logical.isValidOp('&&')).to.be(true);
expect(ol.expr.Logical.isValidOp('')).to.be(false);
expect(ol.expr.Logical.isValidOp('+')).to.be(false);
expect(ol.expr.Logical.isValidOp('<')).to.be(false);
expect(ol.expr.Logical.isValidOp('|')).to.be(false);
});
});
var op = ol.expr.LogicalOp.AND;
var left = new ol.expr.Identifier('foo');
var right = new ol.expr.Literal(false);
var expr = new ol.expr.Logical(op, left, right);
describe('#getOperator()', function() {
it('gets the operator', function() {
expect(expr.getOperator()).to.be(op);
});
});
describe('#getLeft()', function() {
it('gets the left expression', function() {
expect(expr.getLeft()).to.be(left);
});
});
describe('#getRight()', function() {
it('gets the right expression', function() {
expect(expr.getRight()).to.be(right);
});
});
});
describe('ol.expr.Math', function() {
describe('constructor', function() {
it('creates a new expression', function() {
var expr = new ol.expr.Math(
ol.expr.MathOp.ADD,
new ol.expr.Literal(40),
new ol.expr.Literal(2));
expect(expr).to.be.a(ol.expr.Expression);
expect(expr).to.be.a(ol.expr.Math);
});
});
describe('#evaluate()', function() {
it('does + with numeric literal', function() {
var expr = new ol.expr.Math(
ol.expr.MathOp.ADD,
new ol.expr.Literal(40),
new ol.expr.Literal(2));
expect(expr.evaluate()).to.be(42);
});
it('does + with string literal (note: subject to change)', function() {
var expr = new ol.expr.Math(
ol.expr.MathOp.ADD,
new ol.expr.Literal('foo'),
new ol.expr.Literal('bar'));
expect(expr.evaluate()).to.be('foobar');
});
it('does + with identifiers', function() {
var expr = new ol.expr.Math(
ol.expr.MathOp.ADD,
new ol.expr.Identifier('foo'),
new ol.expr.Identifier('bar'));
expect(expr.evaluate({foo: 40, bar: 2})).to.be(42);
});
it('does - with identifiers', function() {
var expr = new ol.expr.Math(
ol.expr.MathOp.SUBTRACT,
new ol.expr.Identifier('foo'),
new ol.expr.Literal(2));
expect(expr.evaluate({foo: 40})).to.be(38);
});
it('casts to number with - (note: this may throw later)', function() {
var expr = new ol.expr.Math(
ol.expr.MathOp.SUBTRACT,
new ol.expr.Identifier('foo'),
new ol.expr.Literal(2));
expect(expr.evaluate({foo: '40'})).to.be(38);
});
it('does * with identifiers', function() {
var expr = new ol.expr.Math(
ol.expr.MathOp.MULTIPLY,
new ol.expr.Literal(2),
new ol.expr.Identifier('foo'));
expect(expr.evaluate({foo: 21})).to.be(42);
});
it('casts to number with * (note: this may throw later)', function() {
var expr = new ol.expr.Math(
ol.expr.MathOp.MULTIPLY,
new ol.expr.Identifier('foo'),
new ol.expr.Literal(2));
expect(expr.evaluate({foo: '21'})).to.be(42);
});
it('does % with identifiers', function() {
var expr = new ol.expr.Math(
ol.expr.MathOp.MOD,
new ol.expr.Literal(97),
new ol.expr.Identifier('foo'));
expect(expr.evaluate({foo: 55})).to.be(42);
});
it('casts to number with % (note: this may throw later)', function() {
var expr = new ol.expr.Math(
ol.expr.MathOp.MOD,
new ol.expr.Identifier('foo'),
new ol.expr.Literal(100));
expect(expr.evaluate({foo: '150'})).to.be(50);
});
});
describe('#isValidOp()', function() {
it('determines if a string is a valid operator', function() {
expect(ol.expr.Math.isValidOp('+')).to.be(true);
expect(ol.expr.Math.isValidOp('-')).to.be(true);
expect(ol.expr.Math.isValidOp('*')).to.be(true);
expect(ol.expr.Math.isValidOp('/')).to.be(true);
expect(ol.expr.Math.isValidOp('%')).to.be(true);
expect(ol.expr.Math.isValidOp('')).to.be(false);
expect(ol.expr.Math.isValidOp('|')).to.be(false);
expect(ol.expr.Math.isValidOp('&')).to.be(false);
expect(ol.expr.Math.isValidOp('<')).to.be(false);
expect(ol.expr.Math.isValidOp('||')).to.be(false);
expect(ol.expr.Math.isValidOp('.')).to.be(false);
});
});
var op = ol.expr.MathOp.MOD;
var left = new ol.expr.Identifier('foo');
var right = new ol.expr.Literal(20);
var expr = new ol.expr.Math(op, left, right);
describe('#getOperator()', function() {
it('gets the operator', function() {
expect(expr.getOperator()).to.be(op);
});
});
describe('#getLeft()', function() {
it('gets the left expression', function() {
expect(expr.getLeft()).to.be(left);
});
});
describe('#getRight()', function() {
it('gets the right expression', function() {
expect(expr.getRight()).to.be(right);
});
});
});
describe('ol.expr.Member', function() {
describe('constructor', function() {
it('creates a new expression', function() {
var expr = new ol.expr.Member(
new ol.expr.Identifier('foo'),
new ol.expr.Identifier('bar'));
expect(expr).to.be.a(ol.expr.Expression);
expect(expr).to.be.a(ol.expr.Member);
});
});
describe('#evaluate()', function() {
it('accesses an object property', function() {
var expr = new ol.expr.Member(
new ol.expr.Identifier('foo'),
new ol.expr.Identifier('bar'));
var scope = {foo: {bar: 42}};
expect(expr.evaluate(scope)).to.be(42);
});
});
var object = new ol.expr.Identifier('foo');
var property = new ol.expr.Identifier('bar');
var expr = new ol.expr.Member(object, property);
describe('#getObject()', function() {
expect(expr.getObject()).to.be(object);
});
describe('#getProperty()', function() {
expect(expr.getProperty()).to.be(property);
});
});
describe('ol.expr.Not', function() {
describe('constructor', function() {
it('creates a new expression', function() {
var expr = new ol.expr.Not(
new ol.expr.Literal(true));
expect(expr).to.be.a(ol.expr.Expression);
expect(expr).to.be.a(ol.expr.Not);
});
});
describe('#evaluate()', function() {
it('returns the logical complement', function() {
var expr = new ol.expr.Not(new ol.expr.Literal(true));
expect(expr.evaluate()).to.be(false);
expr = new ol.expr.Not(new ol.expr.Literal(false));
expect(expr.evaluate()).to.be(true);
});
it('negates a truthy string', function() {
var expr = new ol.expr.Not(new ol.expr.Literal('asdf'));
expect(expr.evaluate()).to.be(false);
});
it('negates a falsy string', function() {
var expr = new ol.expr.Not(new ol.expr.Literal(''));
expect(expr.evaluate()).to.be(true);
});
it('negates a truthy number', function() {
var expr = new ol.expr.Not(new ol.expr.Literal(42));
expect(expr.evaluate()).to.be(false);
});
it('negates a falsy number', function() {
var expr = new ol.expr.Not(new ol.expr.Literal(NaN));
expect(expr.evaluate()).to.be(true);
});
});
describe('#getArgument()', function() {
var argument = new ol.expr.Literal(true);
var expr = new ol.expr.Not(argument);
expect(expr.getArgument()).to.be(argument);
});
});
goog.require('ol.expr.Call');
goog.require('ol.expr.Comparison');
goog.require('ol.expr.ComparisonOp');
goog.require('ol.expr.Expression');
goog.require('ol.expr.Identifier');
goog.require('ol.expr.Literal');
goog.require('ol.expr.Logical');
goog.require('ol.expr.LogicalOp');
goog.require('ol.expr.Math');
goog.require('ol.expr.MathOp');
goog.require('ol.expr.Member');
goog.require('ol.expr.Not');

View File

@@ -0,0 +1,509 @@
goog.provide('ol.test.expression.Lexer');
describe('ol.expr.Lexer', function() {
describe('constructor', function() {
it('creates a new lexer', function() {
var lexer = new ol.expr.Lexer('foo');
expect(lexer).to.be.a(ol.expr.Lexer);
});
});
describe('#next()', function() {
it('returns one token at a time', function() {
var source = 'foo === "bar"';
var lexer = new ol.expr.Lexer(source);
// scan first token
var token = lexer.next();
expect(token.type).to.be(ol.expr.TokenType.IDENTIFIER);
expect(token.value).to.be('foo');
// scan second token
token = lexer.next();
expect(token.type).to.be(ol.expr.TokenType.PUNCTUATOR);
expect(token.value).to.be('===');
// scan third token
token = lexer.next();
expect(token.type).to.be(ol.expr.TokenType.STRING_LITERAL);
expect(token.value).to.be('bar');
// scan again
token = lexer.next();
expect(token.type).to.be(ol.expr.TokenType.EOF);
// and again
token = lexer.next();
expect(token.type).to.be(ol.expr.TokenType.EOF);
});
});
describe('#peek()', function() {
var lexer;
beforeEach(function() {
lexer = new ol.expr.Lexer('foo > 42 && bar == "chicken"');
});
it('looks ahead without consuming token', function() {
var token = lexer.peek();
expect(token.type).to.be(ol.expr.TokenType.IDENTIFIER);
expect(token.value).to.be('foo');
token = lexer.next();
expect(token.type).to.be(ol.expr.TokenType.IDENTIFIER);
expect(token.value).to.be('foo');
});
it('works after a couple scans', function() {
var token = lexer.next();
expect(token.type).to.be(ol.expr.TokenType.IDENTIFIER);
expect(token.value).to.be('foo');
token = lexer.next();
expect(token.type).to.be(ol.expr.TokenType.PUNCTUATOR);
expect(token.value).to.be('>');
token = lexer.peek();
expect(token.type).to.be(ol.expr.TokenType.NUMERIC_LITERAL);
expect(token.value).to.be(42);
token = lexer.next();
expect(token.type).to.be(ol.expr.TokenType.NUMERIC_LITERAL);
expect(token.value).to.be(42);
});
it('returns the same thing when called multiple times', function() {
var token = lexer.peek();
expect(token.type).to.be(ol.expr.TokenType.IDENTIFIER);
expect(token.value).to.be('foo');
for (var i = 0; i < 10; ++i) {
token = lexer.peek();
expect(token.type).to.be(ol.expr.TokenType.IDENTIFIER);
expect(token.value).to.be('foo');
}
});
});
describe('#scanIdentifier_()', function() {
function scan(source, part) {
var lexer = new ol.expr.Lexer(source);
var token = lexer.scanIdentifier_(lexer.getCurrentCharCode_());
if (!part) {
expect(token.index).to.be(0);
expect(lexer.peek().type).to.be(ol.expr.TokenType.EOF);
}
return token;
}
it('works for short identifiers', function() {
var token = scan('a');
expect(token.value).to.be('a');
expect(token.type).to.be(ol.expr.TokenType.IDENTIFIER);
});
it('works for longer identifiers', function() {
var token = scan('foo');
expect(token.value).to.be('foo');
expect(token.type).to.be(ol.expr.TokenType.IDENTIFIER);
});
it('works for $ anywhere', function() {
var token = scan('$foo$bar$');
expect(token.value).to.be('$foo$bar$');
expect(token.type).to.be(ol.expr.TokenType.IDENTIFIER);
});
it('works for _ anywhere', function() {
var token = scan('_foo_bar_');
expect(token.value).to.be('_foo_bar_');
expect(token.type).to.be(ol.expr.TokenType.IDENTIFIER);
});
it('works for keywords', function() {
var token = scan('delete');
expect(token.value).to.be('delete');
expect(token.type).to.be(ol.expr.TokenType.KEYWORD);
});
it('works for null', function() {
var token = scan('null');
expect(token.value).to.be('null');
expect(token.type).to.be(ol.expr.TokenType.NULL_LITERAL);
});
it('works for boolean true', function() {
var token = scan('true');
expect(token.value).to.be('true');
expect(token.type).to.be(ol.expr.TokenType.BOOLEAN_LITERAL);
});
it('works for boolean false', function() {
var token = scan('false');
expect(token.value).to.be('false');
expect(token.type).to.be(ol.expr.TokenType.BOOLEAN_LITERAL);
});
it('works with unicode escape sequences', function() {
var token = scan('\u006f\u006c\u0033');
expect(token.value).to.be('ol3');
expect(token.type).to.be(ol.expr.TokenType.IDENTIFIER);
});
it('works with hex escape sequences', function() {
var token = scan('\x6f\x6c\x33');
expect(token.value).to.be('ol3');
expect(token.type).to.be(ol.expr.TokenType.IDENTIFIER);
});
it('throws for identifiers starting with a number', function() {
expect(function() {
scan('4foo');
}).throwException();
});
it('throws for identifiers starting with a punctuation char', function() {
expect(function() {
scan('!foo');
}).throwException();
});
it('only scans valid identifier part', function() {
var token = scan('foo>bar', true);
expect(token.value).to.be('foo');
expect(token.type).to.be(ol.expr.TokenType.IDENTIFIER);
});
});
describe('#scanNumericLiteral_()', function() {
function scan(source) {
var lexer = new ol.expr.Lexer(source);
var token = lexer.scanNumericLiteral_(lexer.getCurrentCharCode_());
expect(token.index).to.be(0);
expect(lexer.peek().type).to.be(ol.expr.TokenType.EOF);
return token;
}
it('works for integers', function() {
var token = scan('123');
expect(token.value).to.be(123);
expect(token.type).to.be(ol.expr.TokenType.NUMERIC_LITERAL);
});
it('throws for bogus integer', function() {
expect(function() {
scan('123z');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('z');
expect(token.index).to.be(3);
});
});
it('works for float', function() {
var token = scan('123.456');
expect(token.value).to.be(123.456);
expect(token.type).to.be(ol.expr.TokenType.NUMERIC_LITERAL);
});
it('throws for bogus float', function() {
expect(function() {
scan('123.4x4');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('x');
expect(token.index).to.be(5);
});
});
it('works with exponent', function() {
var token = scan('1.234e5');
expect(token.value).to.be(1.234e5);
expect(token.type).to.be(ol.expr.TokenType.NUMERIC_LITERAL);
});
it('works with explicit positive exponent', function() {
var token = scan('1.234e+5');
expect(token.value).to.be(1.234e5);
expect(token.type).to.be(ol.expr.TokenType.NUMERIC_LITERAL);
});
it('works with negative exponent', function() {
var token = scan('1.234e-5');
expect(token.value).to.be(1.234e-5);
expect(token.type).to.be(ol.expr.TokenType.NUMERIC_LITERAL);
});
it('throws for bogus float', function() {
expect(function() {
scan('1.234eo4');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('o');
expect(token.index).to.be(6);
});
});
it('works with octals', function() {
var token = scan('02322');
expect(token.value).to.be(1234);
expect(token.type).to.be(ol.expr.TokenType.NUMERIC_LITERAL);
});
it('throws for bogus octal', function() {
// note that this is more strict than most es5 engines
expect(function() {
scan('02392');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('9');
expect(token.index).to.be(3);
});
});
it('works with hex', function() {
var token = scan('0x4d2');
expect(token.value).to.be(1234);
expect(token.type).to.be(ol.expr.TokenType.NUMERIC_LITERAL);
});
it('throws for bogus hex', function() {
// note that this is more strict than most es5 engines
expect(function() {
scan('0x4G');
}).throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.value).to.be('G');
expect(token.index).to.be(3);
});
});
});
describe('#scanPunctuator_()', function() {
function scan(source) {
var lexer = new ol.expr.Lexer(source);
var token = lexer.scanPunctuator_(lexer.getCurrentCharCode_());
expect(token.index).to.be(0);
expect(lexer.peek().type).to.be(ol.expr.TokenType.EOF);
return token;
}
it('works for dot', function() {
var token = scan('.');
expect(token.value).to.be('.');
expect(token.type).to.be(ol.expr.TokenType.PUNCTUATOR);
});
it('works for bang', function() {
var token = scan('!');
expect(token.value).to.be('!');
expect(token.type).to.be(ol.expr.TokenType.PUNCTUATOR);
});
it('works for double equal', function() {
var token = scan('==');
expect(token.value).to.be('==');
expect(token.type).to.be(ol.expr.TokenType.PUNCTUATOR);
});
it('works for triple equal', function() {
var token = scan('===');
expect(token.value).to.be('===');
expect(token.type).to.be(ol.expr.TokenType.PUNCTUATOR);
});
it('works for not double equal', function() {
var token = scan('!=');
expect(token.value).to.be('!=');
expect(token.type).to.be(ol.expr.TokenType.PUNCTUATOR);
});
it('works for not triple equal', function() {
var token = scan('!==');
expect(token.value).to.be('!==');
expect(token.type).to.be(ol.expr.TokenType.PUNCTUATOR);
});
it('works for logical or', function() {
var token = scan('||');
expect(token.value).to.be('||');
expect(token.type).to.be(ol.expr.TokenType.PUNCTUATOR);
});
it('works for logical and', function() {
var token = scan('&&');
expect(token.value).to.be('&&');
expect(token.type).to.be(ol.expr.TokenType.PUNCTUATOR);
});
it('works for plus', function() {
var token = scan('+');
expect(token.value).to.be('+');
expect(token.type).to.be(ol.expr.TokenType.PUNCTUATOR);
});
it('works for minus', function() {
var token = scan('-');
expect(token.value).to.be('-');
expect(token.type).to.be(ol.expr.TokenType.PUNCTUATOR);
});
it('works for star', function() {
var token = scan('*');
expect(token.value).to.be('*');
expect(token.type).to.be(ol.expr.TokenType.PUNCTUATOR);
});
it('works for slash', function() {
var token = scan('/');
expect(token.value).to.be('/');
expect(token.type).to.be(ol.expr.TokenType.PUNCTUATOR);
});
it('works for percent', function() {
var token = scan('%');
expect(token.value).to.be('%');
expect(token.type).to.be(ol.expr.TokenType.PUNCTUATOR);
});
});
describe('#scanStringLiteral_()', function() {
function scan(source) {
var lexer = new ol.expr.Lexer(source);
var token = lexer.scanStringLiteral_(lexer.getCurrentCharCode_());
expect(token.index).to.be(0);
expect(lexer.peek().type).to.be(ol.expr.TokenType.EOF);
return token;
}
it('parses double quoted string', function() {
var token = scan('"my string"');
expect(token.value).to.be('my string');
expect(token.type).to.be(ol.expr.TokenType.STRING_LITERAL);
});
it('parses double quoted string with internal single quotes', function() {
var token = scan('"my \'quoted\' string"');
expect(token.value).to.be('my \'quoted\' string');
expect(token.type).to.be(ol.expr.TokenType.STRING_LITERAL);
});
it('parses double quoted string with escaped double quotes', function() {
var token = scan('"my \\"quoted\\" string"');
expect(token.value).to.be('my "quoted" string');
expect(token.type).to.be(ol.expr.TokenType.STRING_LITERAL);
});
it('parses double quoted string with escaped backslash', function() {
var token = scan('"my \\\ string"');
expect(token.value).to.be('my \ string');
expect(token.type).to.be(ol.expr.TokenType.STRING_LITERAL);
});
it('parses double quoted string with unicode escape sequences', function() {
var token = scan('"\u006f\u006c\u0033"');
expect(token.value).to.be('ol3');
expect(token.type).to.be(ol.expr.TokenType.STRING_LITERAL);
});
it('parses double quoted string with hex escape sequences', function() {
var token = scan('"\x6f\x6c\x33"');
expect(token.value).to.be('ol3');
expect(token.type).to.be(ol.expr.TokenType.STRING_LITERAL);
});
it('parses double quoted string with tab', function() {
var token = scan('"a\ttab"');
expect(token.value).to.be('a\ttab');
expect(token.type).to.be(ol.expr.TokenType.STRING_LITERAL);
});
it('throws on unterminated double quote', function() {
expect(function() {
scan('"never \'ending\' string');
}).to.throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.type).to.be(ol.expr.TokenType.EOF);
expect(token.index).to.be(22);
});
});
it('parses single quoted string', function() {
var token = scan('\'my string\'');
expect(token.value).to.be('my string');
expect(token.type).to.be(ol.expr.TokenType.STRING_LITERAL);
});
it('parses single quoted string with internal double quotes', function() {
var token = scan('\'my "quoted" string\'');
expect(token.value).to.be('my "quoted" string');
expect(token.type).to.be(ol.expr.TokenType.STRING_LITERAL);
});
it('parses single quoted string with escaped single quotes', function() {
var token = scan('\'my \\\'quoted\\\' string\'');
expect(token.value).to.be('my \'quoted\' string');
expect(token.type).to.be(ol.expr.TokenType.STRING_LITERAL);
});
it('parses single quoted string with escaped backslash', function() {
var token = scan('\'my \\\ string\'');
expect(token.value).to.be('my \ string');
expect(token.type).to.be(ol.expr.TokenType.STRING_LITERAL);
});
it('parses single quoted string with unicode escape sequences', function() {
var token = scan('\'\u006f\u006c\u0033\'');
expect(token.value).to.be('ol3');
expect(token.type).to.be(ol.expr.TokenType.STRING_LITERAL);
});
it('parses single quoted string with hex escape sequences', function() {
var token = scan('\'\x6f\x6c\x33\'');
expect(token.value).to.be('ol3');
expect(token.type).to.be(ol.expr.TokenType.STRING_LITERAL);
});
it('parses single quoted string with tab', function() {
var token = scan('\'a\ttab\'');
expect(token.value).to.be('a\ttab');
expect(token.type).to.be(ol.expr.TokenType.STRING_LITERAL);
});
it('throws on unterminated single quote', function() {
expect(function() {
scan('\'never "ending" string');
}).to.throwException(function(err) {
expect(err).to.be.an(ol.expr.UnexpectedToken);
var token = err.token;
expect(token.type).to.be(ol.expr.TokenType.EOF);
expect(token.index).to.be(22);
});
});
});
});
goog.require('ol.expr.Lexer');
goog.require('ol.expr.TokenType');
goog.require('ol.expr.UnexpectedToken');

View File

@@ -0,0 +1,275 @@
goog.provide('ol.test.expression.Parser');
describe('ol.expr.Parser', function() {
describe('constructor', function() {
it('creates a new expression parser', function() {
var parser = new ol.expr.Parser();
expect(parser).to.be.a(ol.expr.Parser);
});
});
describe('#parseArguments_()', function() {
function parse(source) {
var lexer = new ol.expr.Lexer(source);
var parser = new ol.expr.Parser();
var expr = parser.parseArguments_(lexer);
expect(lexer.peek().type).to.be(ol.expr.TokenType.EOF);
return expr;
}
it('parses comma separated expressions in parens', function() {
var args = parse('(1/3, "foo", true)');
expect(args).length(3);
expect(args[0]).to.be.a(ol.expr.Math);
expect(args[0].evaluate()).to.be(1 / 3);
expect(args[1]).to.be.a(ol.expr.Literal);
expect(args[1].evaluate()).to.be('foo');
expect(args[2]).to.be.a(ol.expr.Literal);
expect(args[2].evaluate()).to.be(true);
});
it('throws on invalid arg expression', function() {
expect(function() {
parse('(6e)');
}).throwException();
});
it('throws on unterminated args', function() {
expect(function() {
parse('("foo", 42, )');
}).throwException();
});
});
describe('#parseBinaryExpression_()', function() {
function parse(source) {
var lexer = new ol.expr.Lexer(source);
var parser = new ol.expr.Parser();
var expr = parser.parseBinaryExpression_(lexer);
expect(lexer.peek().type).to.be(ol.expr.TokenType.EOF);
return expr;
}
it('works with multiplicitave operators', function() {
var expr = parse('4 * 1e4');
expect(expr).to.be.a(ol.expr.Math);
expect(expr.evaluate()).to.be(40000);
expect(parse('10/3').evaluate()).to.be(10 / 3);
});
it('works with additive operators', function() {
var expr = parse('4 +1e4');
expect(expr).to.be.a(ol.expr.Math);
expect(expr.evaluate()).to.be(10004);
expect(parse('10-3').evaluate()).to.be(7);
});
it('works with relational operators', function() {
var expr = parse('4 < 1e4');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate()).to.be(true);
expect(parse('10<3').evaluate()).to.be(false);
expect(parse('10 <= "10"').evaluate()).to.be(true);
expect(parse('10 > "10"').evaluate()).to.be(false);
expect(parse('10 >= 9').evaluate()).to.be(true);
});
it('works with equality operators', function() {
var expr = parse('4 == 1e4');
expect(expr).to.be.a(ol.expr.Comparison);
expect(expr.evaluate()).to.be(false);
expect(parse('10!=3').evaluate()).to.be(true);
expect(parse('10 == "10"').evaluate()).to.be(true);
expect(parse('10 === "10"').evaluate()).to.be(false);
expect(parse('10 !== "10"').evaluate()).to.be(true);
});
it('works with binary logical operators', function() {
var expr = parse('true && false');
expect(expr).to.be.a(ol.expr.Logical);
expect(expr.evaluate()).to.be(false);
expect(parse('false||true').evaluate()).to.be(true);
expect(parse('false || false').evaluate()).to.be(false);
expect(parse('true &&true').evaluate()).to.be(true);
});
it('throws for invalid binary expression', function() {
expect(function() {
parse('4 * / 2');
}).throwException();
expect(function() {
parse('4 < / 2');
}).throwException();
expect(function() {
parse('4 * && 2');
}).throwException();
});
});
describe('#parseGroupExpression_()', function() {
function parse(source) {
var lexer = new ol.expr.Lexer(source);
var parser = new ol.expr.Parser();
var expr = parser.parseGroupExpression_(lexer);
expect(lexer.peek().type).to.be(ol.expr.TokenType.EOF);
return expr;
}
it('parses grouped expressions', function() {
var expr = parse('(3 * (foo + 2))');
expect(expr).to.be.a(ol.expr.Math);
expect(expr.evaluate({foo: 3})).to.be(15);
});
});
describe('#parseLeftHandSideExpression_()', function() {
function parse(source) {
var lexer = new ol.expr.Lexer(source);
var parser = new ol.expr.Parser();
var expr = parser.parseLeftHandSideExpression_(lexer);
expect(lexer.peek().type).to.be(ol.expr.TokenType.EOF);
return expr;
}
it('parses member expressions', function() {
var expr = parse('foo.bar.bam');
expect(expr).to.be.a(ol.expr.Member);
});
it('throws on invalid member expression', function() {
expect(function() {
parse('foo.4');
}).throwException();
});
it('parses call expressions', function() {
var expr = parse('foo(bar)');
expect(expr).to.be.a(ol.expr.Call);
var fns = {
foo: function(arg) {
expect(arguments).length(1);
expect(arg).to.be('chicken');
return 'got ' + arg;
}
};
var scope = {
bar: 'chicken'
};
expect(expr.evaluate(scope, fns)).to.be('got chicken');
});
it('throws on invalid call expression', function() {
expect(function() {
parse('foo(*)');
}).throwException();
});
});
describe('#parsePrimaryExpression_()', function() {
function parse(source) {
var lexer = new ol.expr.Lexer(source);
var parser = new ol.expr.Parser();
var expr = parser.parsePrimaryExpression_(lexer);
expect(lexer.peek().type).to.be(ol.expr.TokenType.EOF);
return expr;
}
it('parses string literal', function() {
var expr = parse('"foo"');
expect(expr).to.be.a(ol.expr.Literal);
expect(expr.evaluate()).to.be('foo');
});
it('parses numeric literal', function() {
var expr = parse('.42e2');
expect(expr).to.be.a(ol.expr.Literal);
expect(expr.evaluate()).to.be(42);
});
it('parses boolean literal', function() {
var expr = parse('.42e2');
expect(expr).to.be.a(ol.expr.Literal);
expect(expr.evaluate()).to.be(42);
});
it('parses null literal', function() {
var expr = parse('null');
expect(expr).to.be.a(ol.expr.Literal);
expect(expr.evaluate()).to.be(null);
});
});
describe('#parseUnaryExpression_()', function() {
function parse(source) {
var lexer = new ol.expr.Lexer(source);
var parser = new ol.expr.Parser();
var expr = parser.parseUnaryExpression_(lexer);
expect(lexer.peek().type).to.be(ol.expr.TokenType.EOF);
return expr;
}
it('parses logical not', function() {
var expr = parse('!foo');
expect(expr).to.be.a(ol.expr.Not);
expect(expr.evaluate({foo: true})).to.be(false);
});
it('works with string literal', function() {
var expr = parse('!"foo"');
expect(expr).to.be.a(ol.expr.Not);
expect(expr.evaluate()).to.be(false);
});
it('works with empty string', function() {
var expr = parse('!""');
expect(expr).to.be.a(ol.expr.Not);
expect(expr.evaluate()).to.be(true);
});
it('works with null', function() {
var expr = parse('!null');
expect(expr).to.be.a(ol.expr.Not);
expect(expr.evaluate()).to.be(true);
});
});
});
goog.require('ol.expr.Expression');
goog.require('ol.expr.Call');
goog.require('ol.expr.Comparison');
goog.require('ol.expr.Lexer');
goog.require('ol.expr.Literal');
goog.require('ol.expr.Logical');
goog.require('ol.expr.Math');
goog.require('ol.expr.Member');
goog.require('ol.expr.Not');
goog.require('ol.expr.Parser');
goog.require('ol.expr.TokenType');