The way to describe and build simple functions using JSON
I need to allow the end user to create some simple function using a JSON file (mostly math based functions), but without allowing them to access the global scope. This module allow them to declare and run functions, but in an enclosed environment provided by the developer.
Functions built with this module are very slow, despite the fact that we pre-compile and cache every expression and statement. Use it only when performance is not an issue.
<script src="https://cdn.jsdelivr.net/npm/build-function@latest/dist/umd/build-function.umd.js"></script>
for production...
<script src="https://cdn.jsdelivr.net/npm/build-function@latest/dist/umd/build-function.umd.min.js"></script>
<script src="https://unpkg.com/build-function@latest/dist/build-function.umd.js"></script>
for production...
<script src="https://unpkg.com/build-function@latest/dist/build-function.umd.min.js"></script>
Creates a function from options
using env
as outer environment
.
function build(
options: BuildFunctionOptions,
env?: Environment,
): Function;
interface BuildFunctionOptions {
name?: string;
params?: string | ParamDescriptor | Array<string | ParamDescriptor>;
body?: Step | Step[];
}
arguments
-
options
Function options.
-
name
(optional
)A
name
for thefunction
, if provided it will be registered to theenvironment
so you can call the function recursively. -
params
(optional
)see Function Expression for more information.
-
body
(optional
)see Function Expression for more information.
-
-
env
(optional
)Outer
environment
for the function. see environment section for more information.
Compiles an expression
or array of expressions
into a function
or array of functions
.
function compileExp(
expression: Expression,
cache: object,
safeGet?: boolean,
): (env: Environment) => any;
function compileExp(
expression: Array<Expression>,
cache: object,
safeGet?: boolean,
): Array<(env: Environment) => any>;
arguments
-
expression
Expression
orarray of Expressions
to be compiled. -
cache
Cache object.
-
safeGet
(optional
)Whether or not to return
undefined
ifid
not found on aget
expression. IfsafeGet
is falsyget
expression will throw ifid
not present in theenvironment
.
Compiles a step
or array of steps
into a function
.
function compileStep(
step: Step | Step[],
cache: object,
allowBreak?: boolean,
): (env: Environment) => StepResult;
arguments
-
step
Step
orarray of steps
to be compiled. -
cache
Cache object.
-
allowBreak
(optional
)Whether or not to allow
break
statements.
Creates a new environment
with parent
as parent environment.
function createEnv(
parent: Environment | null,
lib?: EnvironmentLib | null,
): Environment;
arguments
-
parent
Parent
environment
. -
lib
Variables to be added to the newly created
environment
.
Searches for an id
in an environment
.
function findInEnv(
env: Environment,
id: string,
topOnly?: boolean,
): EnvFound | void;
interface EnvFound {
env: Environment;
id: string;
}
arguments
-
env
The
environment
. -
id
Variable
id
to search for. -
topOnly
(optional
)Whether or not to search the top level
environment
only. Otherwise it will keep searching every parentenvironment
.
Sets a value into an environment
.
function setInEnv(
env: Environment,
id: string,
value: any,
): void;
arguments
-
env
The
environment
to set the variable. -
id
The variable
id
. -
value
The variable value.
It resolves to a literal expression.
syntax
interface LiteralExpression {
type: "literal";
value: any;
}
-
type
Always
"literal"
, it's what identifies aliteral
expression from other expressions and statements. -
value
Value to be used as literal when expression is evaluated. This value will be serialized using
JSON.stringify
and then reparsed usingJSON.parse
when expression is evaluated, it allows to resolve to a fresh object or array when expresion is evaluated.
example
{
"type": "literal",
"value": [1, 2]
}
... is equivalent to...
[1, 2]
Gets a value of the variable identified by the id
from the current virtual environment. If the variable if not found, it will throw, unless it is inside a typeof
trnsform operation.
syntax
interface GetExpression {
type: "get";
id: string;
}
-
type
Always
"get"
, it's what identifies aget
expression from other expressions and statements. -
id
String representing the
id
to be used when expression is resolved.If the
id
is not present in the current virtual environment, it will throw.
example
{
"type": "get",
"id": "current"
}
... is equivalent to...
current
It sets a value to the variable identified by the id
in the current virtual environment. If the variable has not been declared prevoiusly, it will throw. The expression will resolve to the value being set.
syntax
interface SetExpression {
type: "set";
id: string;
value: Expression;
}
-
type
Always
"set"
, it's what identifies aset
expression from other expressions and statements. -
id
String representing the
id
to be used when expression is resolved. -
value
Expression resolving to a value to be assigned to the corresponding
id
in the current virtual environment.
example
{
"type": "set",
"id": "a",
"value": {
"type": "set",
"id": "b",
"value": {
"type": "literal",
"value": true
}
}
}
... is equivalent to...
a = b = true
Note that set
expressions resolve to the value being set so they can be chained together.
It performs a transform operation to another expression, see transformations for supported operators and information.
syntax
interface TransformExpression {
type: "trans";
oper: TransformOperator;
exp: Expression;
}
-
type
Always
"trans"
, it's what identifies atrnsform
expression from other expressions and statements. -
oper
The operator to be used in the operation, see transformations for more information.
-
exp
Expression which result will be transformed.
example
{
"type": "trans",
"oper": "typeof",
"exp": {
"type": "get",
"id": "value"
}
}
... is equivalent to...
typeof value
It performs an operation between 2 or more operands, see operations for supported operators and information.
syntax
interface OperationExpression {
type: "oper";
oper: MultiTermOperator;
exp: Expression[];
}
-
type
Always
"oper"
, it's what identifies anoperation
expression from other expressions and statements. -
oper
The operator to be used in the operation, see operations for more information.
-
exp
Array of expressions to be used in the operation, if less than 2 operators provided, it will throw at compile time.
example
{
"type": "oper",
"oper": "*",
"exp": [
{
"type": "literal",
"value": "15"
},
{
"type": "oper",
"oper": "+",
"exp": [
{
"type": "get",
"id": "value"
},
{
"type": "literal",
"value": 2
},
{
"type": "literal",
"value": 5
}
]
}
]
}
... is equivalent to...
15 * (value + 2 + 5)
Every operation expression acts like its operands has been grouped inside parentheses, so the order of operations doesn't apply.
It resolves to a ternary operation expression.
syntax
interface TernaryExpression {
type: "ternary";
condition: Expression;
then: Expression;
otherwise: Expression;
}
-
type
Always
"ternary"
, it's what identifies aternary
expression from other expressions and statements. -
condition
Expression which result will be used as condition for the
ternary
expression. -
then
Expression which result will be used as resul for the
ternary
expression ifcondition
is truthy. -
otherwise
Expression which result will be used as resul for the
ternary
expression ifcondition
is falsy.
example
{
"type": "ternary",
"condition": {
"type": "get",
"id": "value",
"then": {
"type": "literal",
"value": "yes"
},
"otherwise": {
"type": "literal",
"value": "no"
}
}
}
... is equivalent to...
value ? "yes" : "no"
It represents a function expression.
syntax
interface FunctionExpression {
type: "func";
params?: string | ParamDescriptor | Array<string | ParamDescriptor>;
body?: Step | Step[];
}
interface ParamDescriptor {
id: string;
type: "param" | "rest";
}
-
type
Always
"func"
, it's what identifies afunction
expression from other expressions and statements. -
params
(optional
)String representing the the param
id
,object
representing paramid
andtype
, or anarray of them
representing multiple parameters. -
body
(optional
)A
step
orarray of steps
to be executed when the function is called. See function steps for more information.
example
{
"type": "func",
"params": "obj",
"body": {
"type": "return",
"value": {
"type": "trans",
"oper": "!",
"exp": {
"type": "get",
"id": "obj"
}
}
}
}
... is equivalent to...
function (obj) {
return !obj;
}
It represents a function call result expression.
syntax
interface FunctionCallExpression {
type: "call";
func: Expression;
args?: Expression | SpreadExpression | Array<Expression | SpreadExpression>;
}
-
type
Always
"call"
, it's what identifies afunction call
expression from other expressions and statements. -
func
Expression which resolves to a function to be called.
-
args
(optional
)Expression
,spread expression
orarray of them
to be used asarguments
to call the function.
example
{
"type": "call",
"func": {
"type": "get",
"id": "concat"
},
"args": [
{
"type": "literal",
"value": "Hello "
},
{
"type": "get",
"id": "name"
}
]
}
... is equivalent to...
concat("Hello ", name)
It spreads the values of an array for a function call. Spread expressions only work on function call expressions, it will throw if used somewhere else.
syntax
interface SpreadExpression {
type: "spread";
exp: Expression;
}
-
type
Always
"spread"
, it's what identifies aspread
expression from other expressions and statements. -
exp
Expression which resolves to an array to be spread.
example
{
"type": "call",
"func": {
"type": "get",
"id": "func"
},
"args": [
{
"type": "literal",
"value": 100
},
{
"type": "spread",
"exp": {
"type": "get",
"id": "others"
}
}
]
}
... is equivalent to...
func(100, ...others)
Declares variables into the current virtual environment.
syntax
interface LetStatement {
type: "let";
declare: string | DeclareWithValue | Array<string | DeclareWithValue>;
}
interface DeclareWithValue {
id: string;
value?: Expression;
}
-
type
Always
"let"
, it's what identifies alet
statement from other statements and expressions. -
declare
An
id
,id-value-pair
orarray of them
to be declared into the current virtual environment.
example
{
"type": "let",
"declare": [
"a",
{
"id": "b"
},
{
"id": "c",
"value": 10
}
]
}
... is equivalent to...
let a, b, c = 10;
Declares an if
statement.
syntax
interface IfStatement {
type: "if";
condition: Expression;
then?: Step | Step[];
otherwise?: Step | Step[];
}
-
type
Always
"if"
, it's what identifies anif
statement from other statements and expressions. -
condition
Expression which result will be used as condition for the
if
statement. -
then
(optional
)A
step
orarray of steps
to be executed ifcondition
resolves to a truthy value. See function steps for more information. -
otherwise
(optional
)A
step
orarray of steps
to be executed ifcondition
resolves to a falsy value. See function steps for more information.
example
{
"type": "if",
"condition": {
"type": "get",
"id": "test"
},
"then": {
"type": "call",
"func": {
"type": "get",
"id": "func1"
}
},
"otherwise": {
"type": "call",
"func": {
"type": "get",
"id": "func2"
}
}
}
... is equivalent to...
if (test) {
func1();
} else {
func2();
}
Declares a for
loop.
syntax
interface ForStatement {
type: "for";
target: Expression;
index?: string;
value?: string;
body?: Step | Step[];
}
-
type
Always
"for"
, it's what identifies afor
statement from other statements and expressions. -
target
Expression resolving to an
array-like
object, whichlength
property will be used for the loop. -
index
(optional
)The
id
to be registered inside the loop body virtual environment containing the current iteration index, if not specified it won't be registered, the loop will still run. -
value
(optional
)The
id
to be registered inside the loop body virtual environment containing the current iteration value, if not specified it won't be registered, the loop will still run. -
body
(optional
)A
step
orarray of steps
to be executed for every iteration. See function steps for more information.
example
{
"type": "for",
"target": {
"type": "get",
"id": "array"
},
"index": "i",
"value": "item",
"body": {
"type": "call",
"func": {
"type": "get",
"id": "func1"
},
"args": [
{
"type": "get",
"id": "i"
},
{
"type": "get",
"id": "item"
}
]
}
}
... is equivalent to...
for (let i = 0; i < array.length; i++) {
const item = array[i];
func1(i, item);
}
Declares a break
statement, it will throw at build time if used outside a loop.
syntax
interface BreakStatement {
type: "break";
}
-
type
Always
"break"
, it's what identifies abreak
statement from other statements and expressions.
It represents a return
statement.
syntax
interface ReturnStatement {
type: "return";
value: Expression;
}
-
type
Always
"return"
, it's what identifies areturn
statement from other statements and expressions. -
value
Expression which result will be used as
return
value.
example
{
"type": "return",
"value": {
"type": "get",
"id": "result"
}
}
... is equivalent to...
return result;
It represents a try
statement.
syntax
interface TryStatement {
type: "try";
body?: Step | Step[];
error?: string;
catch?: Step | Step[];
}
-
type
Always
"try"
, it's what identifies atry
statement from other statements and expressions. -
body
(optional
)A
step
orarray of steps
to be executed insidetry
block. -
error
(optional
)The
id
to be registered inside thecatch
block virtual environment containing the error message, if not specified it won't be registered. -
catch
(optional
)A
step
orarray of steps
to be executed insidecatch
block.If
error
option provided you can access the error message using aget
expression.
example
{
"type": "try",
"body": {
"type": "call",
"func": {
"type": "get",
"id": "test"
},
"args": [
{
"type": "get",
"id": "a"
},
{
"type": "get",
"id": "b"
}
]
},
"error": "err",
"catch": {
"type": "call",
"func": {
"type": "get",
"id": "log"
},
"args": {
"type": "get",
"id": "err"
}
}
}
... is equivalent to...
try {
test(a, b);
} catch (err) {
log(err);
}
It represents a throw
statement.
syntax
interface ThrowStatement {
type: "throw";
msg: string | Expression;
}
-
type
Always
"throw"
, it's what identifies athrow
statement from other statements and expressions. -
msg
A
string
orExpression
resolving to astring
to be used as error message.
example
{
"type": "throw",
"msg": "Unknown Error"
}
... is equivalent to...
throw new Error("Unknown Error");
Any statement or expression is considered a step.
Multiterm operations are defined using the Operation Expression.
+
Addition Operator
-
Subtraction Operator
*
Multiplication Operator
/
Division Operator
%
Modulus Operator
**
Exponentiation Operator
&&
Logic AND Operator
||
Logic OR Operator
??
Nullish coalescing Operator
==
Equal Operator
===
Strict equal Operator
!=
Unequal Operator
!==
Strict unequal Operator
<
Less than Operator
<=
Less than or equal Operator
>
Greater than Operator
>=
Greater than or equal Operator
&
Bitwise AND Operator
|
Bitwise OR Operator
^
Bitwise XOR Operator
<<
Shift Left Operator
>>
Shift Right Operator
>>>
Unsigned Shift Right Operator
Transformations are defined using the Transform Expression.
typeof
Type Operator
!
NOT Operator
!!
To Boolean Operator
~
Bitwise NOT Operator