expression-eval
Advanced tools
Comparing version 1.0.1 to 1.1.0
118
index.js
var jsep = require('jsep'); | ||
/** | ||
* Evaluation code from JSEP project, under MIT License. | ||
* Copyright (c) 2013 Stephen Oney, http://jsep.from.so/ | ||
*/ | ||
var binops = { | ||
'||': function (a, b) { return a || b; }, | ||
'&&': function (a, b) { return a && b; }, | ||
'|': function (a, b) { return a | b; }, | ||
'^': function (a, b) { return a ^ b; }, | ||
'&': function (a, b) { return a & b; }, | ||
'==': function (a, b) { return a == b; }, // jshint ignore:line | ||
'!=': function (a, b) { return a != b; }, // jshint ignore:line | ||
'||': function (a, b) { return a || b; }, | ||
'&&': function (a, b) { return a && b; }, | ||
'|': function (a, b) { return a | b; }, | ||
'^': function (a, b) { return a ^ b; }, | ||
'&': function (a, b) { return a & b; }, | ||
'==': function (a, b) { return a == b; }, // jshint ignore:line | ||
'!=': function (a, b) { return a != b; }, // jshint ignore:line | ||
'===': function (a, b) { return a === b; }, | ||
'!==': function (a, b) { return a !== b; }, | ||
'<': function (a, b) { return a < b; }, | ||
'>': function (a, b) { return a > b; }, | ||
'<=': function (a, b) { return a <= b; }, | ||
'>=': function (a, b) { return a >= b; }, | ||
'<<': function (a, b) { return a << b; }, | ||
'>>': function (a, b) { return a >> b; }, | ||
'<': function (a, b) { return a < b; }, | ||
'>': function (a, b) { return a > b; }, | ||
'<=': function (a, b) { return a <= b; }, | ||
'>=': function (a, b) { return a >= b; }, | ||
'<<': function (a, b) { return a << b; }, | ||
'>>': function (a, b) { return a >> b; }, | ||
'>>>': function (a, b) { return a >>> b; }, | ||
'+': function (a, b) { return a + b; }, | ||
'-': function (a, b) { return a - b; }, | ||
'*': function (a, b) { return a * b; }, | ||
'/': function (a, b) { return a / b; }, | ||
'%': function (a, b) { return a % b; } | ||
'+': function (a, b) { return a + b; }, | ||
'-': function (a, b) { return a - b; }, | ||
'*': function (a, b) { return a * b; }, | ||
'/': function (a, b) { return a / b; }, | ||
'%': function (a, b) { return a % b; } | ||
}; | ||
var unops = { | ||
'-' : function(a) { return -a; }, | ||
'+' : function(a) { return a; }, | ||
'~' : function(a) { return ~a; }, | ||
'!' : function(a) { return !a; }, | ||
'-' : function (a) { return -a; }, | ||
'+' : function (a) { return a; }, | ||
'~' : function (a) { return ~a; }, | ||
'!' : function (a) { return !a; }, | ||
}; | ||
function evaluate (node, context) { | ||
if(node.type === 'BinaryExpression') { | ||
return binops[node.operator](evaluate(node.left, context), evaluate(node.right, context)); | ||
} else if(node.type === 'UnaryExpression') { | ||
return unops[node.operator](evaluate(node.argument, context)); | ||
} else if (node.type === 'MemberExpression') { | ||
if (node.computed) { | ||
return evaluate(node.object, context)[evaluate(node.property, context)]; | ||
} else { | ||
return evaluate(node.object, context)[node.property.name]; | ||
} | ||
} else if (node.type === 'Identifier') { | ||
return context[node.name]; | ||
} else if(node.type === 'Literal') { | ||
return node.value; | ||
function evaluate_array( list, context ) { | ||
return list.map( function(v,i,a) { return evaluate( v, context ); } ); | ||
} | ||
function evaluate ( node, context ) { | ||
switch ( node.type ) { | ||
case 'ArrayExpression': | ||
return evaluate_array( node.elements, context ); | ||
case 'BinaryExpression': | ||
return binops[ node.operator ]( evaluate( node.left, context ), evaluate( node.right, context ) ); | ||
case 'CallExpression': | ||
let fn = evaluate( node.callee, context ); | ||
if ( typeof( fn ) === "function" ) { | ||
return fn.apply( null, evaluate_array( node.arguments, context ) ); | ||
} else { | ||
return undefined; | ||
} | ||
case 'ConditionalExpression': | ||
if ( evaluate( node.test, context ) ) { | ||
return evaluate( node.consequent, context ); | ||
} else { | ||
return evaluate( node.alternate, context ); | ||
} | ||
case 'Identifier': | ||
return context[ node.name ]; | ||
case 'Literal': | ||
return node.value; | ||
case 'LogicalExpression': | ||
return binops[ node.operator ]( evaluate( node.left, context ), evaluate( node.right, context ) ); | ||
case 'MemberExpression': | ||
if ( node.computed ) { | ||
return evaluate(node.object, context)[evaluate(node.property, context)]; | ||
} else { | ||
return evaluate(node.object, context)[node.property.name]; | ||
} | ||
case 'ThisExpression': | ||
return this; | ||
case 'UnaryExpression': | ||
return unops[ node.operator ]( evaluate( node.argument, context ) ); | ||
default: | ||
return undefined; | ||
} | ||
} | ||
@@ -51,0 +95,0 @@ |
{ | ||
"name": "expression-eval", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"description": "JavaScript expression parsing and evaluation, safely.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -55,2 +55,2 @@ # expression-eval | ||
Not yet licensed. | ||
MIT License. |
109
test.js
@@ -5,25 +5,114 @@ const expr = require('./'); | ||
const fixtures = [ | ||
{expr: '1 + 2 + 3', expected: 6}, | ||
// array expression | ||
{expr: '([1,2,3])[0]', expected: 1 }, | ||
{expr: '(["one","two","three"])[1]', expected: "two" }, | ||
{expr: '([true,false,true])[2]', expected: true }, | ||
{expr: '([1,true,"three"]).length', expected: 3 }, | ||
{expr: 'isArray([1,2,3])', expected: true }, | ||
{expr: 'list[3]', expected: 4 }, | ||
{expr: 'numMap[1 + two]', expected: 'three'}, | ||
// binary expression | ||
{expr: '1+2', expected: 3}, | ||
{expr: '2-1', expected: 1}, | ||
{expr: '2*2', expected: 4}, | ||
{expr: '6/3', expected: 2}, | ||
{expr: '5|3', expected: 7}, | ||
{expr: '5&3', expected: 1}, | ||
{expr: '5^3', expected: 6}, | ||
{expr: '4<<2', expected: 16}, | ||
{expr: '256>>4', expected: 16}, | ||
{expr: '-14>>>2', expected: 1073741820}, | ||
{expr: '10%6', expected: 4}, | ||
{expr: '"a"+"b"', expected: "ab"}, | ||
{expr: 'one + three', expected: 4}, | ||
{expr: 'foo.bar + 2', expected: 'baz2'}, | ||
{expr: 'foo.bar + 2', expected: 'baz2'}, | ||
{expr: 'numMap[1 + two]', expected: 'three'}, | ||
{expr: '"run" + "ning"', expected: 'running'} | ||
// call expression | ||
{expr: 'func(5)', expected: 6}, | ||
{expr: 'func(1+2)', expected: 4}, | ||
// conditional expression | ||
{expr: '(true ? "true" : "false")', expected: "true" }, | ||
{expr: '( ( bool || false ) ? "true" : "false")', expected: "true" }, | ||
{expr: '( true ? ( 123*456 ) : "false")', expected: 123*456 }, | ||
{expr: '( false ? "true" : one + two )', expected: 3 }, | ||
// identifier | ||
{expr: 'string', expected: "string" }, | ||
{expr: 'number', expected: 123 }, | ||
{expr: 'bool', expected: true }, | ||
// literal | ||
{expr: '"foo"', expected: "foo" }, // string literal | ||
{expr: "'foo'", expected: "foo" }, // string literal | ||
{expr: '123', expected: 123 }, // numeric literal | ||
{expr: 'true', expected: true }, // boolean literal | ||
// logical expression | ||
{expr: 'true || false', expected: true }, | ||
{expr: 'true && false', expected: false }, | ||
{expr: '1 == "1"', expected: true }, | ||
{expr: '2 != "2"', expected: false }, | ||
{expr: '1.234 === 1.234', expected: true }, | ||
{expr: '123 !== "123"', expected: true }, | ||
{expr: '1 < 2', expected: true }, | ||
{expr: '1 > 2', expected: false }, | ||
{expr: '2 <= 2', expected: true }, | ||
{expr: '1 >= 2', expected: false }, | ||
// member expression | ||
{expr: 'foo.bar', expected: 'baz' }, | ||
{expr: 'foo["bar"]', expected: 'baz' }, | ||
{expr: 'foo[foo.bar]', expected: 'wow' }, | ||
// unary expression | ||
{expr: '-one', expected: -1 }, | ||
{expr: '+two', expected: 2 }, | ||
{expr: '!false', expected: true }, | ||
{expr: '!!true', expected: true }, | ||
{expr: '~15', expected: -16 }, | ||
]; | ||
const context = { | ||
string: "string", | ||
number: 123, | ||
bool: true, | ||
one: 1, | ||
two: 2, | ||
three: 3, | ||
foo: {bar: 'baz'}, | ||
numMap: {10: 'ten', 3: 'three'} | ||
foo: {bar: 'baz', baz: "wow"}, | ||
numMap: {10: 'ten', 3: 'three'}, | ||
list: [1,2,3,4,5], | ||
func: function(x) { return x + 1; }, | ||
isArray: Array.isArray, | ||
}; | ||
var tests = 0; | ||
var passed = 0; | ||
fixtures.forEach((o) => { | ||
tests++; | ||
var val = expr.compile(o.expr)(context); | ||
assert.equal( | ||
expr.compile(o.expr)(context), | ||
val, | ||
o.expected, | ||
'Failed: ' + o.expr + ' === ' + o.expected); | ||
'Failed: ' + o.expr + ' ('+val+') === ' + o.expected); | ||
passed++; | ||
}); | ||
console.log('%s/%s tests passed.', fixtures.length, fixtures.length); | ||
// test 'this' | ||
(function testThis() { | ||
tests++; | ||
this.foo = 'bar'; | ||
var ast = expr.parse( 'this.foo' ); | ||
var val = expr.eval( ast, { 'baz': 'blah' } ); | ||
assert.equal( val, 'bar' ); | ||
passed++; | ||
})(); | ||
console.log('%s/%s tests passed.', passed, tests); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
8979
5
182