A compact library to execute small scripts in a secure way and avoiding an eval expression. See also Never use eval()! or this Stackoverflow link.
npm
$> npm install @nytramr/executor
yarn
$> yarn add @nytramr/executor
const engine = new Engine();
compile(_textGraph_);
name | description |
---|---|
textGraph |
The textGraph is a graph like string that represents the code to be "compiled" into an executer. |
The executor function
const engine = new Engine();
const executor = engine.compile('GT(PP(first), PP(second))');
executor({ first: 10, second: 5 }); // returns true
executor({ first: -1, second: -12 }); // returns true
executor({ first: 'b', second: 'a' }); // returns true
executor({ first: 'a', second: 'A' }); // returns true
executor({ first: 10, second: 10 }); // returns false
executor({ first: -10, second: 10 }); // returns false
executor({ first: 'a', second: 'b' }); // returns false
It allows the introduction of new operators into the compiler parser, in order to extend the functionality to meet special needs.
define(_operator_, _executer_);
name | description |
---|---|
operator |
The operator is a string to be used by the compiler to identify the new functionality. |
executer |
The executer is a special function to be used in order to compile the operator. The function must return another function that will receive the context as a parameter |
undefined
const engine = new Engine();
// let's define an IF operator, that depending on the boolean result of pred, will execute and return the execution of trueResult or falseResult
engine.define('IF', (pred, trueResult, falseResult) => (context) =>
pred(context) ? trueResult(context) : falseResult(context),
);
// let's create an operator that prints in the console the value got by `valueGetter`
engine.define('CL', (valueGetter) => (context) => console.log(valueGetter(context)));
var executer = engine.compile('IF(PP(value), CL(CT("true")), CL(CT("false")))');
executer({ value: true }); // prints "true"
executer({ value: 'hello' }); // prints "true"
executer({ value: 0 }); // prints "false"
executer({}); // prints "false"
Please consider be exception free, if your new component can throw and exception, try to avoid it as much as possible.
Name | Implementation | Use |
---|---|---|
conditional (if-else) |
const engine = new Engine();
engine.define('IF', (pred, trueResult, falseResult) => (context) =>
pred(context) ? trueResult(context) : falseResult(context),
); or const engine = new Engine();
engine.define('IF', (pred, trueResult, falseResult) => (context) =>
(pred(context) && trueResult(context)) || falseResult(context),
); |
var executer = engine.compile('IF(PP(value), CT("true")), CL(CT("false"))'); |
console log |
const engine = new Engine();
engine.define('CL', (valueGetter) => (context) => console.log(valueGetter(context))); |
var executer = engine.compile('CL(PP(value))'); |
join |
const engine = new Engine();
engine.define('JN', (arrayGetter, string) => (context) => {
const array = arrayGetter(context);
if (Array.isArray(array)) return array.join(string(context));
return ''; // you may choice to return undefined instead.
}); |
var executer = engine.compile('JN(PP("myArray"), CT(","))'); |
find |
const engine = new Engine();
engine.define('FD', (arrayGetter, string) => (context) => {
const array = arrayGetter(context);
if (Array.isArray(array)) return array.find((element) => predicate(context, subContext, element));
return undefined;
}); |
var executer = engine.compile('FN(PP("singers"), EQ(SL(PP("name")), CT("John")))'); |
filter |
const engine = new Engine();
engine.define('FT', (arrayGetter, string) => (context) => {
const array = arrayGetter(context);
if (Array.isArray(array)) return array.filter((element) => predicate(context, subContext, element));
return []; // you may choice to return undefined instead.
}); |
var executer = engine.compile('FT(PP("singers"), EQ(SL(PP("band")), CT("The Beatles")))'); |
It will return the part of the context object according to the given path
. If at any point of the path
a value cannot be resolved, it returns undefined
.
Overall |
The |
Special Chars |
There is also the possibility to use a path-like string between quotes to access to a property which contains non allowed chars like |
Dynamic Access |
The use of squarebrackets allows using literals or other values of the same context as part of the |
Simplest use |
It should return the value of the property |
const engine = new Engine();
const executer = engine.compile('PP(name)');
executor({ name: 'John' }); // returns "John"
executor({ name: 'Paul' }); // returns "Paul" |
Navigate in the object |
It should return the value of the property |
const engine = new Engine();
const executer = engine.compile('PP(user.name)');
executor({ user: { name: 'John' } }); // returns "John"
executor({ user: { name: 'Paul' } }); // returns "Paul" |
Index in array |
It should return the value of second position in the array |
const engine = new Engine();
const executer = engine.compile('PP(1)');
executor(['cero', 'uno', 'dos']); // returns "uno"
executor([20, 30, 40]); // returns 30 |
Use a property as a key |
It should return the value of the property specified by the property |
const executor = engine.compile('PP([key])');
executor({ value: 'name', key: 'value' }); // returns "name"
executor({ 'another.value': 'another name', key: 'another.value' }); // returns "another name"
executor({ value: 'name', keyNotFound: 'value' }); // returns undefined
executor({}); // returns undefined or const executor = engine.compile('PP(PP(key))');
executor({ value: 'name', key: 'value' }); // returns "name"
executor({ 'another.value': 'another name', key: 'another.value' }); // returns "another name"
executor({ value: 'name', keyNotFound: 'value' }); // returns undefined
executor({}); // returns undefined |
Use a property path as a key |
It should return the value of the property specified by the property |
const executor = engine.compile('PP([key.sub-key])');
executor({ value: 'name', key: { 'sub-key': 'value' } }); // returns "name"
executor({ 'another value': 'another name', key: { 'sub-key': 'another value' } }); // returns "another name"
executor({ value: 'name', keyNotFound: { 'sub-key': 'value' } }); // returns undefined
executor({ value: 'name', key: { 'sub-keyNotFound': 'value' } }); // returns undefined
executor({}); // returns undefined or const executor2 = engine.compile('PP(PP(key.sub-key))');
executor2({ value: 'name', key: { 'sub-key': 'value' } }); // returns "name"
executor2({ 'another value': 'another name', key: { 'sub-key': 'another value' } }); // returns "another name"
executor2({ value: 'name', keyNotFound: { 'sub-key': 'value' } }); // returns undefined
executor2({ value: 'name', key: { 'sub-keyNotFound': 'value' } }); // returns undefined
executor2({}); // returns undefined |
It will return true
or false
depending of the and evaluation of condition1
and condition2
.
The execution is lazy, therefore in case the first condition returns falsy value, the second condition is not evaluated.
const engine = new Engine();
const executer = engine.compile('AN(PP(first), PP(second))');
executor({ first: true, second: true }); // returns true
executor({ first: true, second: 10 }); // returns 10
executor({ first: true, second: 'true' }); // returns "true"
executor({ first: false, second: true }); // returns false
executor({ first: false, second: false }); // returns false
executor({ first: true, second: false }); // returns false
executor({ first: 0, second: false }); // returns 0
executor({ first: null, second: false }); // returns null
executor({ first: '', second: false }); // returns ''
executor({ second: true }); // returns Undefined
It will return an executor that always returns value
. Is the way we can define literals.
const engine = new Engine();
const executer = engine.compile('CT("someText")');
executer({}); // returns "someText"
executor({ first: 10, second: '10' }); // returns "someText"
It will return true
when both values are equals, returns false
otherwise.
const engine = new Engine();
const executer = engine.compile('EQ(PP(first), PP(second))');
executor({ first: 10, second: 10 }); // returns true
executor({ first: '10', second: '10' }); // returns true
executor({ first: true, second: true }); // returns true
executor({}); // returns true
executor({ first: 10, second: '10' }); // returns false
executor({ first: false, second: true }); // returns false
executor({ first: false, second: undefined }); // returns false
executor({ first: false, second: 0 }); // returns false
It will return true
when the first value is greater or equals than the second value, returns false
otherwise.
const engine = new Engine();
const executer = engine.compile('GE(PP(first), PP(second))');
executor({ first: 10, second: 5 }); // returns true
executor({ first: -1, second: -12 }); // returns true
executor({ first: 'b', second: 'a' }); // returns true
executor({ first: 'a', second: 'A' }); // returns true
executor({ first: 10, second: 10 }); // returns false
executor({ first: -10, second: 10 }); // returns false
executor({ first: 'a', second: 'b' }); // returns false
executor({}); // returns false
It will return true
when the first value is greater than the second value, returns false
otherwise.
const engine = new Engine();
const executer = engine.compile('GT(PP(first), PP(second))');
executor({ first: 10, second: 5 }); // returns true
executor({ first: -1, second: -12 }); // returns true
executor({ first: 'b', second: 'a' }); // returns true
executor({ first: 'a', second: 'A' }); // returns true
executor({ first: 10, second: 10 }); // returns false
executor({ first: -10, second: 10 }); // returns false
executor({ first: 'a', second: 'b' }); // returns false
executor({}); // returns false
It will return true
when the first value is less or equals than the second value, returns false
otherwise.
const engine = new Engine();
const executer = engine.compile('LE(PP(first), PP(second))');
executor({ first: 10, second: 10 }); // returns true
executor({ first: 5, second: 10 }); // returns true
executor({ first: -1, second: 0 }); // returns true
executor({ first: 'a', second: 'b' }); // returns true
executor({ first: 'A', second: 'a' }); // returns true
executor({ first: 10, second: -10 }); // returns false
executor({ first: 'b', second: 'a' }); // returns false
executor({}); // returns false
It will return true
when the first value is less than the second value, returns false
otherwise.
const engine = new Engine();
const executer = engine.compile('LT(PP(first), PP(second))');
executor({ first: 5, second: 6 }); // returns true
executor({ first: -2, second: 0 }); // returns true
executor({ first: 'a', second: 'b' }); // returns true
executor({ first: 'A', second: 'a' }); // returns true
executor({ first: 10, second: 10 }); // returns false
executor({ first: 10, second: -10 }); // returns false
executor({ first: 'b', second: 'a' }); // returns false
executor({}); // returns false
It will return true
when both values are different, returns false
otherwise.
const engine = new Engine();
const executer = engine.compile('EQ(PP(first), PP(second))');
executor({ first: 10, second: 10 }); // returns false
executor({ first: '10', second: '10' }); // returns false
executor({ first: true, second: true }); // returns false
executor({}); // returns false
executor({ first: 10, second: '10' }); // returns true
executor({ first: false, second: true }); // returns true
executor({ first: false, second: undefined }); // returns true
executor({ first: false, second: 0 }); // returns true
It will return true
when the condition is false
or false
when the condition is true
.
const engine = new Engine();
const executer = engine.compile('NT(PP(first))');
executor({ first: false }); // returns true
executor({ first: 0 }); // returns true
executor({ first: null }); // returns true
executor({}); // returns true
executor({ first: true }); // returns false
executor({ first: 1 }); // returns false
executor({ first: {} }); // returns false
It will return true
or false
depending of the or evaluation of condition1
and condition2
.
The execution is lazy, therefore in case the first condition returns a truly value, the second condition is not evaluated.
const engine = new Engine();
const executer = engine.compile('OR(PP(first), PP(second))');
executor({ first: true, second: true }); // returns true
executor({ first: true, second: false }); // returns true
executor({ first: false, second: true }); // returns true
executor({ first: false, second: false }); // returns false
executor({ first: 'true', second: true }); // returns "true"
executor({ first: 10, second: false }); // returns 10
executor({ first: 0, second: false }); // returns 0
executor({ first: false, second: null }); // returns null
executor({ first: false, second: '' }); // returns ''
executor({ second: true }); // returns true
In order to checkout project and build and run tests locally you need to meet the following requirements:
- Node.js version >= 13.14.0, you can get the latest version of Node.js from http://nodejs.org/,
- git, you can get git from http://git-scm.com/,
- yarn, you can install yarn by running the following command:
npm install yarn -g
$> yarn install
$> yarn build
$> yarn test