Call expressions
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
goog.provide('ol.expression.Call');
|
||||
goog.provide('ol.expression.Comparison');
|
||||
goog.provide('ol.expression.ComparisonOp');
|
||||
goog.provide('ol.expression.Expression');
|
||||
@@ -29,11 +30,62 @@ ol.expression.Expression = function() {};
|
||||
*
|
||||
* @param {Object} scope Evaluation scope. All properties of this object
|
||||
* will be available as variables when evaluating the expression.
|
||||
* @param {Object=} opt_fns Optional scope for looking up functions. If not
|
||||
* provided, functions will be looked in the evaluation scope.
|
||||
* @param {Object=} opt_this Object to use as this when evaluating call
|
||||
* expressions. If not provided, `this` will resolve to a new object.
|
||||
* @return {*} Result of the expression.
|
||||
*/
|
||||
ol.expression.Expression.prototype.evaluate = goog.abstractMethod;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A call expression (e.g. `foo(bar)`).
|
||||
*
|
||||
* @constructor
|
||||
* @extends {ol.expression.Expression}
|
||||
* @param {expr} expr An expression that resolves to a function.
|
||||
* @param {Array.<ol.expression.Expression>} args Arguments.
|
||||
*/
|
||||
ol.expression.Call = function(expr, args) {
|
||||
|
||||
/**
|
||||
* @type {expr}
|
||||
* @private
|
||||
*/
|
||||
this.expr_ = expr;
|
||||
|
||||
/**
|
||||
* @type {Array.<ol.expression.Expression>}
|
||||
* @private
|
||||
*/
|
||||
this.args_ = args;
|
||||
|
||||
};
|
||||
goog.inherits(ol.expression.Call, ol.expression.Expression);
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.expression.Call.prototype.evaluate = function(scope, opt_fns, opt_this) {
|
||||
var fnScope = goog.isDefAndNotNull(opt_fns) ? opt_fns : scope;
|
||||
var fn = this.expr_.evaluate(fnScope);
|
||||
if (!fn || !goog.isFunction(fn)) {
|
||||
throw new Error('No function in provided scope: ' + this.name_);
|
||||
}
|
||||
var thisArg = goog.isDef(opt_this) ? opt_this : {};
|
||||
|
||||
var len = this.args_.length;
|
||||
var values = new Array(len);
|
||||
for (var i = 0; i < len; ++i) {
|
||||
values[i] = this.args_[i].evaluate(scope, opt_fns, opt_this);
|
||||
}
|
||||
return fn.apply(thisArg, values);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
@@ -86,10 +138,11 @@ goog.inherits(ol.expression.Comparison, ol.expression.Expression);
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.expression.Comparison.prototype.evaluate = function(scope) {
|
||||
ol.expression.Comparison.prototype.evaluate = function(scope, opt_this,
|
||||
opt_fns) {
|
||||
var result;
|
||||
var rightVal = this.right_.evaluate(scope);
|
||||
var leftVal = this.left_.evaluate(scope);
|
||||
var rightVal = this.right_.evaluate(scope, opt_fns, opt_this);
|
||||
var leftVal = this.left_.evaluate(scope, opt_fns, opt_this);
|
||||
|
||||
switch (this.operator_) {
|
||||
case ol.expression.ComparisonOp.EQ:
|
||||
@@ -174,7 +227,7 @@ goog.inherits(ol.expression.Literal, ol.expression.Expression);
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.expression.Literal.prototype.evaluate = function(scope) {
|
||||
ol.expression.Literal.prototype.evaluate = function() {
|
||||
return this.value_;
|
||||
};
|
||||
|
||||
@@ -225,10 +278,10 @@ goog.inherits(ol.expression.Logical, ol.expression.Expression);
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.expression.Logical.prototype.evaluate = function(scope) {
|
||||
ol.expression.Logical.prototype.evaluate = function(scope, opt_fns, opt_this) {
|
||||
var result;
|
||||
var rightVal = this.right_.evaluate(scope);
|
||||
var leftVal = this.left_.evaluate(scope);
|
||||
var rightVal = this.right_.evaluate(scope, opt_fns, opt_this);
|
||||
var leftVal = this.left_.evaluate(scope, opt_fns, opt_this);
|
||||
|
||||
if (this.operator_ === ol.expression.LogicalOp.AND) {
|
||||
result = leftVal && rightVal;
|
||||
@@ -290,10 +343,10 @@ goog.inherits(ol.expression.Math, ol.expression.Expression);
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.expression.Math.prototype.evaluate = function(scope) {
|
||||
ol.expression.Math.prototype.evaluate = function(scope, opt_fns, opt_this) {
|
||||
var result;
|
||||
var rightVal = this.right_.evaluate(scope);
|
||||
var leftVal = this.left_.evaluate(scope);
|
||||
var rightVal = this.right_.evaluate(scope, opt_fns, opt_this);
|
||||
var leftVal = this.left_.evaluate(scope, opt_fns, opt_this);
|
||||
/**
|
||||
* TODO: throw if rightVal, leftVal not numbers - this would require the use
|
||||
* of a concat function for strings but it would let us serialize these as
|
||||
@@ -346,7 +399,6 @@ goog.inherits(ol.expression.Not, ol.expression.Expression);
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.expression.Not.prototype.evaluate = function(scope) {
|
||||
return !this.expr_.evaluate(scope);
|
||||
ol.expression.Not.prototype.evaluate = function(scope, opt_fns, opt_this) {
|
||||
return !this.expr_.evaluate(scope, opt_fns, opt_this);
|
||||
};
|
||||
|
||||
|
||||
@@ -761,7 +761,6 @@ ol.expression.Lexer.prototype.scanStringLiteral_ = function(quote) {
|
||||
quote === ol.expression.Char.DOUBLE_QUOTE,
|
||||
'Strings must start with a quote: ' + String.fromCharCode(quote));
|
||||
|
||||
var start = this.index_;
|
||||
this.increment_(1);
|
||||
|
||||
var str = '';
|
||||
|
||||
@@ -17,6 +17,7 @@ goog.provide('ol.expression.Parser');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
|
||||
goog.require('ol.expression.Call');
|
||||
goog.require('ol.expression.Comparison');
|
||||
goog.require('ol.expression.ComparisonOp');
|
||||
goog.require('ol.expression.Expression');
|
||||
@@ -148,10 +149,11 @@ ol.expression.Parser.prototype.createBinaryExpression_ = function(operator,
|
||||
*
|
||||
* @param {ol.expression.Identifier} expr Identifier expression for function.
|
||||
* @param {Array.<ol.expression.Expression>} args Arguments array.
|
||||
* @return {ol.expression.Call} Call expression.
|
||||
* @private
|
||||
*/
|
||||
ol.expression.Parser.prototype.createCallExpression_ = function(expr, args) {
|
||||
throw new Error('Not implemented');
|
||||
return new ol.expressions.Call(expr, args);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,75 @@
|
||||
goog.provide('ol.test.expression.Expression');
|
||||
|
||||
|
||||
describe('ol.expression.Call', function() {
|
||||
|
||||
describe('constructor', function() {
|
||||
it('creates a new expression', function() {
|
||||
var expr = new ol.expression.Call(
|
||||
new ol.expression.Identifier('sqrt'),
|
||||
[new ol.expression.Literal(42)]);
|
||||
expect(expr).to.be.a(ol.expression.Expression);
|
||||
expect(expr).to.be.a(ol.expression.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.expression.Call(
|
||||
new ol.expression.Identifier('sqrt'),
|
||||
[new ol.expression.Literal(42)]);
|
||||
expect(expr.evaluate(fns)).to.be(Math.sqrt(42));
|
||||
});
|
||||
|
||||
it('accepts a separate scope for functions', function() {
|
||||
var expr = new ol.expression.Call(
|
||||
new ol.expression.Identifier('sqrt'),
|
||||
[new ol.expression.Identifier('foo')]);
|
||||
expect(expr.evaluate({foo: 42}, fns)).to.be(Math.sqrt(42));
|
||||
});
|
||||
|
||||
it('accepts multiple expression arguments', function() {
|
||||
var expr = new ol.expression.Call(
|
||||
new ol.expression.Identifier('strConcat'),
|
||||
[
|
||||
new ol.expression.Identifier('foo'),
|
||||
new ol.expression.Literal(' comes after '),
|
||||
new ol.expression.Math(
|
||||
ol.expression.MathOp.SUBTRACT,
|
||||
new ol.expression.Identifier('foo'),
|
||||
new ol.expression.Literal(1))
|
||||
]);
|
||||
expect(expr.evaluate({foo: 42}, fns)).to.be('42 comes after 41');
|
||||
});
|
||||
|
||||
it('accepts optional this arg', function() {
|
||||
var expr = new ol.expression.Call(
|
||||
new ol.expression.Identifier('discouraged'), []);
|
||||
|
||||
var thisArg = {
|
||||
message: 'avoid this'
|
||||
};
|
||||
|
||||
expect(expr.evaluate(fns, null, thisArg)).to.be('avoid this');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('ol.expression.Comparison', function() {
|
||||
|
||||
describe('constructor', function() {
|
||||
@@ -367,6 +436,7 @@ describe('ol.expression.Not', function() {
|
||||
});
|
||||
|
||||
|
||||
goog.require('ol.expression.Call');
|
||||
goog.require('ol.expression.Comparison');
|
||||
goog.require('ol.expression.ComparisonOp');
|
||||
goog.require('ol.expression.Expression');
|
||||
|
||||
Reference in New Issue
Block a user