@ibyar/expressions
v2.5.0
Published
Aurora expression, an template expression and evaluation, An 100% spec compliant ES2022 JavaScript toolchain,
Downloads
125
Maintainers
Readme
Ibyar Expression
Your Mirror Dimension in the javascript world.
Ibyar expression, is a JavaScript expression parsing and evaluation. and template expression evaluation, with stack and scope.
- this package has no dependance, can work on both (Node.js) and any Web Browsers, that support modules and class syntax
- An 100% spec compliant ES2022 JavaScript toolchain
Install
npm i --save @ibyar/expressions
yarn add @ibyar/expressions
Try the parser
- Ibyar Expression parser: https://ibyar.github.io/astexplorer/
Example
import { JavaScriptParser, Scope, Stack } from '@ibyar/expressions';
const source = `
let { x, y } = {x: 5, y: 55};
const z = x / y;
console.log({x, y, z});
const alex = { firstName: 'alex', lastName: 'jon', age: 30 };
alex.fullName = function(){ return this.firstName + ' ' + this.lastName;};
console.log(alex.fullName());
console.log(alex.fullName.toString());
setTimeout(() => console.log('setTimeout', alex), 500);
const sara = { firstName: 'sara', lastName: 'jon', age: 28, [Symbol.toStringTag]: 'SARA'};
console.log(sara['age']);
console.log('toStringTag', Object.prototype.toString.call(sara));
console.log('compare', sara.age <=> alex.age);
console.log('older', sara.age >? alex.age);
console.log('younger', sara.age <? alex.age);
console.log('typeof', typeof alex);
console.log('typeof', typeof alex.age);
console.log('regex 1', /regex/g.test('reg'));
console.log('regex 2', /regex/g.test('regex'));
`
+
'let stringLiteralExample = `${alex.firstName} and ${sara.firstName} are friends`; console.log({stringLiteralExample});'
+
`
function latex(str) {
return { "cooked": str[0], "raw": str.raw[0] }
}
`
+
'let taggedStringLiteral = latex`Hi\n${2+3}!`; console.log({taggedStringLiteral});'
;
const pipeSource = `
const add = (arg1, arg2) => arg1 + arg2;
a
|> function(s) {return s;}
|> (c => b = c + 1)
|> add(?, 2)
|> add(3, ?)
|> console.log:4:5;`;
const ast = JavaScriptParser.parse(source + pipeSource);
const esTree = ast.toJSON();
const esTreeString = JSON.stringify(ast, void 0, 2);
console.log({ esTree, esTreeString });
const context = {
Promise,
setTimeout,
console,
Symbol,
Object
};
const stack = Stack.for(context);
const globalScope = Scope.functionScopeFor({
a: 7,
b: undefined
});
stack.pushScope(globalScope);
ast.get(stack);
console.log(
'from global context values: x: %s, y: %s',
globalScope.getContext().x,
globalScope.getContext().y
);
console.log(
globalScope.getContext()
);
follow ast of ESTree
Feature
- V8 JavaScript engine to parse js source code.
- for now this parser does not provide any info about
SourceLocation
- class expression
- Import/Export module
- Custom Factory Builder to convert the code to ESTree directly or to executable expression node
Operators
| Operator type | Individual operators |
| ---------------------- | ------------------------------------------------------- |
| member | . []
|
| call / create instance | () new
|
| negation/increment | ! ~ - + ++ -- typeof void delete
|
| multiply/divide | * / % ** %%
|
| addition/subtraction | + -
|
| bitwise shift | << >> >>>
|
| relational | < <= > >= >? <? <=> in instanceof
|
| equality | == != === !==
|
| bitwise-and | &
|
| bitwise-xor | ^
|
| bitwise-or | \|
|
| logical-and | &&
|
| logical-or | \|\|
|
| conditional | ?:
|
| pipeline | \|>
|
| assignment | = += -= *= **= /= %= <<= >>= >>>= &= ^= \|= &&= \|\|= ??= >?= <?= %%=
|
| comma | ,
|
non-ecma operator are %% >? <? <=> >?= <?= %%=
Pipeline Operator |> Support
- support angular-like syntax and partial operator for a call syntax:
x |> methodName1:arg2:arg3
|> methodName2(arg1, ?, ...arg3);
function add(x, y, z) { return x + y + z };
const a = 88;
const b = 99;
const c = 11;
const z = a |> add:b:c; // === add(a, b, c)
function map(num){
return { number: num };
}
function plus(num1){
return function(num2){
return num1 + num2;
};
}
function add(x, y) { return x + y };
const a = 88, b = 11;
b |> function(x) { console.log(x); return x; }
|> x => { console.log(x); return x; }
|> (x => x + 10)
|> plus(a)
|> add(99, ?)
|> add : 88 : ?
|> add : ? : 77
|> map
How to use stack and scope:
1- create stack:
import { Scope, Stack } from '@ibyar/expressions';
const stack1 = new Stack();
// OR
const scope1 = new Scope<{x: number, y: number}>( {x: 8, y: 6});
const scope2 = Scope.for({z: 9});
const stack2 = new Stack([scope1, scope2]);
2- any of the previous stacks, does not provide and public api like Math
, String
, ....
you need to provide these values to the stack, the stack work as a sandbox, isolated from the js-VM.
import { ReadOnlyScope } from '@ibyar/expressions';
stack1.pushScope(ReadOnlyScope.for({
Math: {
E: Math.E,
LN10: Math.LN10,
LN2: Math.LN2,
LOG10E: Math.LOG10E,
LOG2E: Math.LOG2E,
PI: Math.PI,
SQRT1_2: Math.SQRT1_2,
SQRT2: Math.SQRT2,
abs: Math.abs,
acos: Math.acos,
asin: Math.asin,
atan: Math.atan,
atan2: Math.atan2,
ceil: Math.ceil,
....
},
// or
Math,
// another object
Array: {
// in js a statement like `let x = new Array()` will throw exception.
isArray: Array.isArray,
},
Array, // `let x = new Array()` no Error.
parseFloat,
parseInt,
String,
console,
}));
so now you can write js code the use these as identifier
3- the stack is ready to use with expression
4- try to parse an expression like let x = 'data'; console.log(x);
.
import { JavaScriptParser } from '@ibyar/expressions';
const ast = JavaScriptParser.parse(`let x = 'data'; console.log(x);`);
// use get method to execute an expression and return a value if available.
ast.get(stack1);
// it should print 'data' on the console
5- you can visit the ast
import { expressionVisitor, VariableDeclaration } from '@ibyar/expressions';
expressionVisitor.visit(ast, (expression, type, control) => {
if (type === 'VariableDeclaration') {
const kind = (expression as VariableDeclaration).getKind();
....
}
});
How to use Reactive Scope:
reactive scope is used to
import { JavaScriptParser, ReactiveScope, Stack } from '@ibyar/expressions';
// parse JS code
const ast = JavaScriptParser.parse(`
const x = 'Hello';
const y = "World";
let z = x + ' ' + y + '!';
z = 123 + 456;
z = (x + ' ' + y).toUpperCase();
z = 'Reactive Scope is Awesome!!';
`);
// create reactive scope, can listen to changes of variables
const reactiveScope = ReactiveScope.blockScope();
// listen for changes of 'z' variable
const zSubscription = reactiveScope.subscribe('z', (nz, oz) => {
console.log('new value of Z = ' + nz, 'old value of Z = ' + oz);
});
// listen for changes of 'z' const
const xSubscription = reactiveScope.subscribe('x', (nx, ox) => {
console.log('new value of X = ' + nx, 'old value of X = ' + ox);
});
// create stack
const stack = new Stack(reactiveScope);
// execute js code
ast.get(stack);
// unsubscribe from all created ScopeSubscription
zSubscription.unsubscribe();
xSubscription.unsubscribe();