@ibyar/expressions
TypeScript icon, indicating that this package has built-in type declarations

1.2.2 • Public • Published

Ibyar Expression

NPM Version NPM Downloads LICENSE lerna GitHub contributors

	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

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();

Package Sidebar

Install

npm i @ibyar/expressions

Weekly Downloads

3

Version

1.2.2

License

MIT

Unpacked Size

1.97 MB

Total Files

296

Last publish

Collaborators

  • salem.ebo