build-function v0.1.0
build-function
The way to describe and build simple functions using JSON
Motivation
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.
Performance Notice
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.
In This Guide
CDN
jsDelivr
<script src="https://cdn.jsdelivr.net/npm/build-function@latest/dist/build.umd.js"></script>for production...
<script src="https://cdn.jsdelivr.net/npm/build-function@latest/dist/build.umd.min.js"></script>UNPKG
<script src="https://unpkg.com/build-function@latest/dist/build.umd.js"></script>for production...
<script src="https://unpkg.com/build-function@latest/dist/build.umd.min.js"></script>API
build
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
optionsFunction options.
name(optional)A
namefor thefunction, if provided it will be registered to theenvironmentso 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
environmentfor the function. see environment section for more information.
compileExp
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
expressionExpressionorarray of Expressionsto be compiled.cacheCache object.
safeGet(optional)Whether or not to return
undefinedifidnot found on agetexpression. IfsafeGetis falsygetexpression will throw ifidnot present in theenvironment.
compileStep
Compiles a step or array of steps into a function.
function compileStep(
step: Step | Step[],
cache: object,
allowBreak?: boolean,
): (env: Environment) => StepResult;arguments
stepSteporarray of stepsto be compiled.cacheCache object.
allowBreak(optional)Whether or not to allow
breakstatements.
createEnv
Creates a new environment with parent as parent environment.
function createEnv(
parent: Environment | null,
lib?: EnvironmentLib | null,
): Environment;arguments
parentParent
environment.libVariables to be added to the newly created
environment.
findInEnv
Searches for an id in an environment.
function findInEnv(
env: Environment,
id: string,
topOnly?: boolean,
): EnvFound | void;
interface EnvFound {
env: Environment;
id: string;
}arguments
envThe
environment.idVariable
idto search for.topOnly(optional)Whether or not to search the top level
environmentonly. Otherwise it will keep searching every parentenvironment.
setInEnv
Sets a value into an environment.
function setInEnv(
env: Environment,
id: string,
value: any,
): void;arguments
envThe
environmentto set the variable.idThe variable
id.valueThe variable value.
Expressions
Literal Expression
It resolves to a literal expression.
syntax
interface LiteralExpression {
type: "literal";
value: any;
}typeAlways
"literal", it's what identifies aliteralexpression from other expressions and statements.valueValue to be used as literal when expression is evaluated. This value will be serialized using
JSON.stringifyand then reparsed usingJSON.parsewhen 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]Get Expression
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;
}typeAlways
"get", it's what identifies agetexpression from other expressions and statements.idString representing the
idto be used when expression is resolved.If the
idis not present in the current virtual environment, it will throw.
example
{
"type": "get",
"id": "current"
}... is equivalent to...
currentSet Expression
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;
}typeAlways
"set", it's what identifies asetexpression from other expressions and statements.idString representing the
idto be used when expression is resolved.valueExpression resolving to a value to be assigned to the corresponding
idin the current virtual environment.
example
{
"type": "set",
"id": "a",
"value": {
"type": "set",
"id": "b",
"value": {
"type": "literal",
"value": true
}
}
}... is equivalent to...
a = b = trueNote that set expressions resolve to the value being set so they can be chained together.
Ternary Expression
It resolves to a ternary operation expression.
syntax
interface TernaryExpression {
type: "ternary";
condition: Expression;
then: Expression;
otherwise: Expression;
}typeAlways
"ternary", it's what identifies aternaryexpression from other expressions and statements.conditionExpression which result will be used as condition for the
ternaryexpression.thenExpression which result will be used as resul for the
ternaryexpression ifconditionis truthy.otherwiseExpression which result will be used as resul for the
ternaryexpression ifconditionis 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"Operation Expression
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[];
}typeAlways
"oper", it's what identifies anoperationexpression from other expressions and statements.operThe operator to be used in the operation, see operations for more information.
expArray 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.
Transform Expression
It performs a transform operation to another expression, see transformations for supported operators and information.
syntax
interface TransformExpression {
type: "trans";
oper: TransformOperator;
exp: Expression;
}typeAlways
"trans", it's what identifies atrnsformexpression from other expressions and statements.operThe operator to be used in the operation, see transformations for more information.
expExpression which result will be transformed.
example
{
"type": "trans",
"oper": "typeof",
"exp": {
"type": "get",
"id": "value"
}
}... is equivalent to...
typeof valueFunction Expression
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";
}typeAlways
"func", it's what identifies afunctionexpression from other expressions and statements.params(optional)String representing the the param
id,objectrepresenting paramidandtype, or anarray of themrepresenting multiple parameters.body(optional)A
steporarray of stepsto 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;
}Function Call Expression
It represents a function call result expression.
syntax
interface FunctionCallExpression {
type: "call";
func: Expression;
args?: Expression | SpreadExpression | Array<Expression | SpreadExpression>;
}typeAlways
"call", it's what identifies afunction callexpression from other expressions and statements.funcExpression which resolves to a function to be called.
args(optional)Expression,spread expressionorarray of themto be used asargumentsto 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)Spread Expression
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;
}typeAlways
"spread", it's what identifies aspreadexpression from other expressions and statements.expExpression 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)Statements
let Statement
Declares variables into the current virtual environment.
syntax
interface LetStatement {
type: "let";
declare: string | DeclareWithValue | Array<string | DeclareWithValue>;
}
interface DeclareWithValue {
id: string;
value?: Expression;
}typeAlways
"let", it's what identifies aletstatement from other statements and expressions.declareAn
id,id-value-pairorarray of themto 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;if Statement
Declares an if statement.
syntax
interface IfStatement {
type: "if";
condition: Expression;
then?: Step | Step[];
otherwise?: Step | Step[];
}typeAlways
"if", it's what identifies anifstatement from other statements and expressions.conditionExpression which result will be used as condition for the
ifstatement.then(optional)A
steporarray of stepsto be executed ifconditionresolves to a truthy value. See function steps for more information.otherwise(optional)A
steporarray of stepsto be executed ifconditionresolves 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();
}for Statement
Declares a for loop.
syntax
interface ForStatement {
type: "for";
target: Expression;
index?: string;
value?: string;
body?: Step | Step[];
}typeAlways
"for", it's what identifies aforstatement from other statements and expressions.targetExpression resolving to an
array-likeobject, whichlengthproperty will be used for the loop.index(optional)The
idto 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
idto 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
steporarray of stepsto be executed for every iteration. See function steps for more information.
example
{
"type": "for",
"target": {
"type": "get",
"id": "array"
},
"index": "index",
"value": "item",
"body": {
"type": "call",
"func": {
"type": "get",
"id": "func"
},
"args": [
{
"type": "get",
"id": "index"
},
{
"type": "get",
"id": "item"
}
]
}
}... is equivalent to...
for (let i = 0; i < array.length; i++) {
func(i, array[i]);
}break Statement
Declares a break statement, it will throw at build time if used outside a loop.
syntax
interface BreakStatement {
type: "break";
}typeAlways
"break", it's what identifies abreakstatement from other statements and expressions.
return Statement
It represents a return statement.
syntax
interface ReturnStatement {
type: "return";
value: Expression;
}typeAlways
"return", it's what identifies areturnstatement from other statements and expressions.valueExpression which result will be used as
returnvalue.
example
{
"type": "return",
"value": {
"type": "get",
"id": "result"
}
}... is equivalent to...
return result;try Statement
It represents a try statement.
syntax
interface TryStatement {
type: "try";
body?: Step | Step[];
error?: string;
catch?: Step | Step[];
}typeAlways
"try", it's what identifies atrystatement from other statements and expressions.body(optional)A
steporarray of stepsto be executed insidetryblock.error(optional)The
idto be registered inside thecatchblock virtual environment containing the error message, if not specified it won't be registered.catch(optional)A
steporarray of stepsto be executed insidecatchblock.If
erroroption provided you can access the error message using agetexpression.
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);
}throw Statement
It represents a throw statement.
syntax
interface ThrowStatement {
type: "throw";
msg: string | Expression;
}typeAlways
"throw", it's what identifies athrowstatement from other statements and expressions.msgA
stringorExpressionresolving to astringto be used as error message.
example
{
"type": "throw",
"msg": "Unknown Error"
}... is equivalent to...
throw new Error("Unknown Error");Steps
Any statement or expression is considered a step.
Operations
Multiterm operations are defined using the Operation Expression.
Supported Operators
+ Addition Operator
- Subtraction Operator
* Multiplication Operator
/ Division Operator
% Modulus Operator
`Exponentiation Operator`**
&& Logic AND Operator
|| Logic OR 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
Transformations are defined using the Transform Expression.
Supported Transform Operators
typeof Type Operator
! NOT Operator
!! To Boolean Operator
~ Bitwise NOT Operator