646 lines
14 KiB
JavaScript
646 lines
14 KiB
JavaScript
goog.provide('ol.expression.Call');
|
|
goog.provide('ol.expression.Comparison');
|
|
goog.provide('ol.expression.ComparisonOp');
|
|
goog.provide('ol.expression.Expression');
|
|
goog.provide('ol.expression.Identifier');
|
|
goog.provide('ol.expression.Literal');
|
|
goog.provide('ol.expression.Logical');
|
|
goog.provide('ol.expression.LogicalOp');
|
|
goog.provide('ol.expression.Math');
|
|
goog.provide('ol.expression.MathOp');
|
|
goog.provide('ol.expression.Member');
|
|
goog.provide('ol.expression.Not');
|
|
|
|
|
|
|
|
/**
|
|
* Base class for all expressions. Instances of ol.expression.Expression
|
|
* correspond to a limited set of ECMAScript 5.1 expressions.
|
|
* http://www.ecma-international.org/ecma-262/5.1/#sec-11
|
|
*
|
|
* This base class should not be constructed directly. Instead, use one of
|
|
* the subclass constructors.
|
|
*
|
|
* @constructor
|
|
*/
|
|
ol.expression.Expression = function() {};
|
|
|
|
|
|
/**
|
|
* Evaluate the expression and return the result.
|
|
*
|
|
* @param {Object=} opt_scope Evaluation scope. All properties of this object
|
|
* will be available as variables when evaluating the expression. If not
|
|
* provided, `null` will be used.
|
|
* @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 {ol.expression.Expression} callee An expression that resolves to a
|
|
* function.
|
|
* @param {Array.<ol.expression.Expression>} args Arguments.
|
|
*/
|
|
ol.expression.Call = function(callee, args) {
|
|
|
|
/**
|
|
* @type {ol.expression.Expression}
|
|
* @private
|
|
*/
|
|
this.callee_ = callee;
|
|
|
|
/**
|
|
* @type {Array.<ol.expression.Expression>}
|
|
* @private
|
|
*/
|
|
this.args_ = args;
|
|
|
|
};
|
|
goog.inherits(ol.expression.Call, ol.expression.Expression);
|
|
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
ol.expression.Call.prototype.evaluate = function(opt_scope, opt_fns, opt_this) {
|
|
var fnScope = goog.isDefAndNotNull(opt_fns) ? opt_fns : opt_scope;
|
|
var fn = this.callee_.evaluate(fnScope);
|
|
if (!fn || !goog.isFunction(fn)) {
|
|
throw new Error('Expected function but found ' + fn);
|
|
}
|
|
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(opt_scope, opt_fns, opt_this);
|
|
}
|
|
return fn.apply(thisArg, values);
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the argument list.
|
|
* @return {Array.<ol.expression.Expression>} The argument.
|
|
*/
|
|
ol.expression.Call.prototype.getArgs = function() {
|
|
return this.args_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the callee expression.
|
|
* @return {ol.expression.Expression} The callee expression.
|
|
*/
|
|
ol.expression.Call.prototype.getCallee = function() {
|
|
return this.callee_;
|
|
};
|
|
|
|
|
|
/**
|
|
* @enum {string}
|
|
*/
|
|
ol.expression.ComparisonOp = {
|
|
EQ: '==',
|
|
NEQ: '!=',
|
|
STRICT_EQ: '===',
|
|
STRICT_NEQ: '!==',
|
|
GT: '>',
|
|
LT: '<',
|
|
GTE: '>=',
|
|
LTE: '<='
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* A comparison expression (e.g. `foo >= 42`, `bar != "chicken"`).
|
|
*
|
|
* @constructor
|
|
* @extends {ol.expression.Expression}
|
|
* @param {ol.expression.ComparisonOp} operator Comparison operator.
|
|
* @param {ol.expression.Expression} left Left expression.
|
|
* @param {ol.expression.Expression} right Right expression.
|
|
*/
|
|
ol.expression.Comparison = function(operator, left, right) {
|
|
|
|
/**
|
|
* @type {ol.expression.ComparisonOp}
|
|
* @private
|
|
*/
|
|
this.operator_ = operator;
|
|
|
|
/**
|
|
* @type {ol.expression.Expression}
|
|
* @private
|
|
*/
|
|
this.left_ = left;
|
|
|
|
/**
|
|
* @type {ol.expression.Expression}
|
|
* @private
|
|
*/
|
|
this.right_ = right;
|
|
|
|
};
|
|
goog.inherits(ol.expression.Comparison, ol.expression.Expression);
|
|
|
|
|
|
/**
|
|
* Determine if a given string is a valid comparison operator.
|
|
* @param {string} candidate Operator to test.
|
|
* @return {boolean} The operator is valid.
|
|
*/
|
|
ol.expression.Comparison.isValidOp = (function() {
|
|
var valid = {};
|
|
for (var key in ol.expression.ComparisonOp) {
|
|
valid[ol.expression.ComparisonOp[key]] = true;
|
|
}
|
|
return function isValidOp(candidate) {
|
|
return !!valid[candidate];
|
|
};
|
|
}());
|
|
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
ol.expression.Comparison.prototype.evaluate = function(opt_scope, opt_fns,
|
|
opt_this) {
|
|
var result;
|
|
var rightVal = this.right_.evaluate(opt_scope, opt_fns, opt_this);
|
|
var leftVal = this.left_.evaluate(opt_scope, opt_fns, opt_this);
|
|
|
|
switch (this.operator_) {
|
|
case ol.expression.ComparisonOp.EQ:
|
|
result = leftVal == rightVal;
|
|
break;
|
|
case ol.expression.ComparisonOp.NEQ:
|
|
result = leftVal != rightVal;
|
|
break;
|
|
case ol.expression.ComparisonOp.STRICT_EQ:
|
|
result = leftVal === rightVal;
|
|
break;
|
|
case ol.expression.ComparisonOp.STRICT_NEQ:
|
|
result = leftVal !== rightVal;
|
|
break;
|
|
case ol.expression.ComparisonOp.GT:
|
|
result = leftVal > rightVal;
|
|
break;
|
|
case ol.expression.ComparisonOp.LT:
|
|
result = leftVal < rightVal;
|
|
break;
|
|
case ol.expression.ComparisonOp.GTE:
|
|
result = leftVal >= rightVal;
|
|
break;
|
|
case ol.expression.ComparisonOp.LTE:
|
|
result = leftVal <= rightVal;
|
|
break;
|
|
default:
|
|
throw new Error('Unsupported comparison operator: ' + this.operator_);
|
|
}
|
|
return result;
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the comparison operator.
|
|
* @return {string} The comparison operator.
|
|
*/
|
|
ol.expression.Comparison.prototype.getOperator = function() {
|
|
return this.operator_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the left expression.
|
|
* @return {ol.expression.Expression} The left expression.
|
|
*/
|
|
ol.expression.Comparison.prototype.getLeft = function() {
|
|
return this.left_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the right expression.
|
|
* @return {ol.expression.Expression} The right expression.
|
|
*/
|
|
ol.expression.Comparison.prototype.getRight = function() {
|
|
return this.right_;
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* An identifier expression (e.g. `foo`).
|
|
*
|
|
* @constructor
|
|
* @extends {ol.expression.Expression}
|
|
* @param {string} name An identifier name.
|
|
*/
|
|
ol.expression.Identifier = function(name) {
|
|
|
|
/**
|
|
* @type {string}
|
|
* @private
|
|
*/
|
|
this.name_ = name;
|
|
|
|
};
|
|
goog.inherits(ol.expression.Identifier, ol.expression.Expression);
|
|
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
ol.expression.Identifier.prototype.evaluate = function(opt_scope) {
|
|
if (!goog.isDefAndNotNull(opt_scope)) {
|
|
throw new Error('Attempt to evaluate identifier with no scope');
|
|
}
|
|
return opt_scope[this.name_];
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the identifier name.
|
|
* @return {string} The identifier name.
|
|
*/
|
|
ol.expression.Identifier.prototype.getName = function() {
|
|
return this.name_;
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* A literal expression (e.g. `"chicken"`, `42`, `true`, `null`).
|
|
*
|
|
* @constructor
|
|
* @extends {ol.expression.Expression}
|
|
* @param {string|number|boolean|null} value A literal value.
|
|
*/
|
|
ol.expression.Literal = function(value) {
|
|
|
|
/**
|
|
* @type {string|number|boolean|null}
|
|
* @private
|
|
*/
|
|
this.value_ = value;
|
|
|
|
};
|
|
goog.inherits(ol.expression.Literal, ol.expression.Expression);
|
|
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
ol.expression.Literal.prototype.evaluate = function() {
|
|
return this.value_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the literal value.
|
|
* @return {string|number|boolean|null} The literal value.
|
|
*/
|
|
ol.expression.Literal.prototype.getValue = function() {
|
|
return this.value_;
|
|
};
|
|
|
|
|
|
/**
|
|
* @enum {string}
|
|
*/
|
|
ol.expression.LogicalOp = {
|
|
AND: '&&',
|
|
OR: '||'
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* A binary logical expression (e.g. `foo && bar`, `bar || "chicken"`).
|
|
*
|
|
* @constructor
|
|
* @extends {ol.expression.Expression}
|
|
* @param {ol.expression.LogicalOp} operator Logical operator.
|
|
* @param {ol.expression.Expression} left Left expression.
|
|
* @param {ol.expression.Expression} right Right expression.
|
|
*/
|
|
ol.expression.Logical = function(operator, left, right) {
|
|
|
|
/**
|
|
* @type {ol.expression.LogicalOp}
|
|
* @private
|
|
*/
|
|
this.operator_ = operator;
|
|
|
|
/**
|
|
* @type {ol.expression.Expression}
|
|
* @private
|
|
*/
|
|
this.left_ = left;
|
|
|
|
/**
|
|
* @type {ol.expression.Expression}
|
|
* @private
|
|
*/
|
|
this.right_ = right;
|
|
|
|
};
|
|
goog.inherits(ol.expression.Logical, ol.expression.Expression);
|
|
|
|
|
|
/**
|
|
* Determine if a given string is a valid logical operator.
|
|
* @param {string} candidate Operator to test.
|
|
* @return {boolean} The operator is valid.
|
|
*/
|
|
ol.expression.Logical.isValidOp = (function() {
|
|
var valid = {};
|
|
for (var key in ol.expression.LogicalOp) {
|
|
valid[ol.expression.LogicalOp[key]] = true;
|
|
}
|
|
return function isValidOp(candidate) {
|
|
return !!valid[candidate];
|
|
};
|
|
}());
|
|
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
ol.expression.Logical.prototype.evaluate = function(opt_scope, opt_fns,
|
|
opt_this) {
|
|
var result;
|
|
var rightVal = this.right_.evaluate(opt_scope, opt_fns, opt_this);
|
|
var leftVal = this.left_.evaluate(opt_scope, opt_fns, opt_this);
|
|
|
|
if (this.operator_ === ol.expression.LogicalOp.AND) {
|
|
result = leftVal && rightVal;
|
|
} else if (this.operator_ === ol.expression.LogicalOp.OR) {
|
|
result = leftVal || rightVal;
|
|
} else {
|
|
throw new Error('Unsupported logical operator: ' + this.operator_);
|
|
}
|
|
return result;
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the logical operator.
|
|
* @return {string} The logical operator.
|
|
*/
|
|
ol.expression.Logical.prototype.getOperator = function() {
|
|
return this.operator_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the left expression.
|
|
* @return {ol.expression.Expression} The left expression.
|
|
*/
|
|
ol.expression.Logical.prototype.getLeft = function() {
|
|
return this.left_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the right expression.
|
|
* @return {ol.expression.Expression} The right expression.
|
|
*/
|
|
ol.expression.Logical.prototype.getRight = function() {
|
|
return this.right_;
|
|
};
|
|
|
|
|
|
/**
|
|
* @enum {string}
|
|
*/
|
|
ol.expression.MathOp = {
|
|
ADD: '+',
|
|
SUBTRACT: '-',
|
|
MULTIPLY: '*',
|
|
DIVIDE: '/',
|
|
MOD: '%'
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* A math expression (e.g. `foo + 42`, `bar % 10`).
|
|
*
|
|
* @constructor
|
|
* @extends {ol.expression.Expression}
|
|
* @param {ol.expression.MathOp} operator Math operator.
|
|
* @param {ol.expression.Expression} left Left expression.
|
|
* @param {ol.expression.Expression} right Right expression.
|
|
*/
|
|
ol.expression.Math = function(operator, left, right) {
|
|
|
|
/**
|
|
* @type {ol.expression.MathOp}
|
|
* @private
|
|
*/
|
|
this.operator_ = operator;
|
|
|
|
/**
|
|
* @type {ol.expression.Expression}
|
|
* @private
|
|
*/
|
|
this.left_ = left;
|
|
|
|
/**
|
|
* @type {ol.expression.Expression}
|
|
* @private
|
|
*/
|
|
this.right_ = right;
|
|
|
|
};
|
|
goog.inherits(ol.expression.Math, ol.expression.Expression);
|
|
|
|
|
|
/**
|
|
* Determine if a given string is a valid math operator.
|
|
* @param {string} candidate Operator to test.
|
|
* @return {boolean} The operator is valid.
|
|
*/
|
|
ol.expression.Math.isValidOp = (function() {
|
|
var valid = {};
|
|
for (var key in ol.expression.MathOp) {
|
|
valid[ol.expression.MathOp[key]] = true;
|
|
}
|
|
return function isValidOp(candidate) {
|
|
return !!valid[candidate];
|
|
};
|
|
}());
|
|
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
ol.expression.Math.prototype.evaluate = function(opt_scope, opt_fns, opt_this) {
|
|
var result;
|
|
var rightVal = this.right_.evaluate(opt_scope, opt_fns, opt_this);
|
|
var leftVal = this.left_.evaluate(opt_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
|
|
* math functions where available elsewhere
|
|
*/
|
|
|
|
switch (this.operator_) {
|
|
case ol.expression.MathOp.ADD:
|
|
result = leftVal + rightVal;
|
|
break;
|
|
case ol.expression.MathOp.SUBTRACT:
|
|
result = Number(leftVal) - Number(rightVal);
|
|
break;
|
|
case ol.expression.MathOp.MULTIPLY:
|
|
result = Number(leftVal) * Number(rightVal);
|
|
break;
|
|
case ol.expression.MathOp.DIVIDE:
|
|
result = Number(leftVal) / Number(rightVal);
|
|
break;
|
|
case ol.expression.MathOp.MOD:
|
|
result = Number(leftVal) % Number(rightVal);
|
|
break;
|
|
default:
|
|
throw new Error('Unsupported math operator: ' + this.operator_);
|
|
}
|
|
return result;
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the math operator.
|
|
* @return {string} The math operator.
|
|
*/
|
|
ol.expression.Math.prototype.getOperator = function() {
|
|
return this.operator_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the left expression.
|
|
* @return {ol.expression.Expression} The left expression.
|
|
*/
|
|
ol.expression.Math.prototype.getLeft = function() {
|
|
return this.left_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the right expression.
|
|
* @return {ol.expression.Expression} The right expression.
|
|
*/
|
|
ol.expression.Math.prototype.getRight = function() {
|
|
return this.right_;
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* A member expression (e.g. `foo.bar`).
|
|
*
|
|
* @constructor
|
|
* @extends {ol.expression.Expression}
|
|
* @param {ol.expression.Expression} object An expression that resolves to an
|
|
* object.
|
|
* @param {ol.expression.Identifier} property Identifier with name of property.
|
|
*/
|
|
ol.expression.Member = function(object, property) {
|
|
|
|
/**
|
|
* @type {ol.expression.Expression}
|
|
* @private
|
|
*/
|
|
this.object_ = object;
|
|
|
|
/**
|
|
* @type {ol.expression.Identifier}
|
|
* @private
|
|
*/
|
|
this.property_ = property;
|
|
|
|
};
|
|
goog.inherits(ol.expression.Member, ol.expression.Expression);
|
|
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
ol.expression.Member.prototype.evaluate = function(opt_scope, opt_fns,
|
|
opt_this) {
|
|
var obj = this.object_.evaluate(opt_scope, opt_fns, opt_this);
|
|
if (!goog.isObject(obj)) {
|
|
throw new Error('Expected member expression to evaluate to an object ' +
|
|
'but got ' + obj);
|
|
}
|
|
return this.property_.evaluate(/** @type {Object} */ (obj));
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the object expression.
|
|
* @return {ol.expression.Expression} The object.
|
|
*/
|
|
ol.expression.Member.prototype.getObject = function() {
|
|
return this.object_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the property expression.
|
|
* @return {ol.expression.Identifier} The property.
|
|
*/
|
|
ol.expression.Member.prototype.getProperty = function() {
|
|
return this.property_;
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* A logical not expression (e.g. `!foo`).
|
|
*
|
|
* @constructor
|
|
* @extends {ol.expression.Expression}
|
|
* @param {ol.expression.Expression} argument Expression to negate.
|
|
*/
|
|
ol.expression.Not = function(argument) {
|
|
|
|
/**
|
|
* @type {ol.expression.Expression}
|
|
* @private
|
|
*/
|
|
this.argument_ = argument;
|
|
|
|
};
|
|
goog.inherits(ol.expression.Not, ol.expression.Expression);
|
|
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
ol.expression.Not.prototype.evaluate = function(opt_scope, opt_fns, opt_this) {
|
|
return !this.argument_.evaluate(opt_scope, opt_fns, opt_this);
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the argument (the negated expression).
|
|
* @return {ol.expression.Expression} The argument.
|
|
*/
|
|
ol.expression.Not.prototype.getArgument = function() {
|
|
return this.argument_;
|
|
};
|