javascript conditional expression parser - javascript

I am working in JavaScript. I want to parse and evaluate conditional expression.
ex:
var param1=1;
var param2=2;
var param3=3;
var expression="(param1==param2)||(param3<param1)";
I want to write a function which will accept 'expression' as a input and parse the expression as well as evaluate expression and return evaluated result.
Please let me know for any suggestions.
Thanks in advance.

Here is the evil one: eval();
var param1=1;
var param2=2;
var param3=3;
var expression=eval("(param1==param2)||(param3<param1)");
Then your function comes,
function myEvaluator(s) {
return eval(s);
}
You must have variables in expression public.

This is a really old question and I am surprised that no libraries came out since to help with this.
Out of necessity, I built a library called angel-eval that does exactly that. It parses a string expression and evaluates with a context object. It uses a parser generator called Nearley under the hood and does not use eval or new Function(…).
To use, install: npm i angel-eval
const { evaluate } = require("angel-eval");
evaluate("(param1===param2)||(param3<param1)", { param1: 1, param2: 2, param3: 3 });
angel-eval does not support == or != so you need to use === and !==.
Here is a sandbox:

There are a few expression parsers out there, but I would like to suggest mine. It's called swan-js and it doesn't use eval or new Function.
Installation:
npm install #onlabsorg/swan-js
Usage (it works both in NodeJS and in the browser):
const swan = require('#onlabsorg/swan-js');
const evaluate = swan.parse("param1 == param2 | param3 < param1");
const context = swan.createContext({param1:1, param2:2, param3:3});
const value = await evaluate(context); // false

Related

(Angular, TypeScript) Create new function from string that has if condition (without eval())

How can I create a new function from a string when the string contains if condition?
The string will comes from outside but in the dummy example below everything is hardcoded to be easier.
let f1: number = 1;
let f2: number = 0;
let condition: string = 'if(this.f1===1){this.f2 = 1}';
let result = this.createFn(condition);
createFn(param: string) {
return new Function('return ' + param)();
// or return new Function(param)();
}
Of course this is not working and I'm searching a way to do it.
I don't want to use eval().
Passing a string to the constructor of Function is almost the same as using eval().
To safely execute arbitrary code in JavaScript you need to have a JavaScript interpreter written in JavaScript which will then execute the received string in a sandbox environment. A quick google search yields this package: https://github.com/NeilFraser/JS-Interpreter.
Others who faced this issue have decided to implement themselves a domain-specific subset of a programming language to then allow the strings to execute that language's code. e.g. SAP's SAPUI5/OpenUI5 solution for the Expression Binding they have.

How to parse functions bodies from a string using javascript?

I'm looking for a way to get a function declaration body by name from a string of js code. I'm in Nodejs environment.
Let's say I have some spaghetti.js file. I can read it into a string
const allJs = fs.readFileSync('spaghetti.js');
Now I need a function that receives that string and function name and returns a string with everything between { }.
Something like this
allJs = 'let a=1; const b=[2, 3]; function cook(items){return items}; cook(b)';
parseFunction(allJs, 'cook');//'return items'
The complexity of input js is not limited.
I tried to find an npm module for that, but no luck.
You should have a look at an AST parser for Javascript:
http://esprima.org/
https://github.com/ternjs/acorn
That should be more safe than using RegExp or something.
A String can be evaluated locally with the native eval() method. But remember, eval is a form of evil!
If the parseFunction() above is relying on something like this then the global Function constructor is being used and the 'new' function is bound to the return value of that operation (and thus that return value itself needs to be called).
A simple way to achieve this might be to do something like this...
var funcString = 'var a = 1, b = 3;';
funcString += 'function summit(){return a + b;}';
funcString += 'return summit();';
function makeNewFunc(str) {
return new Function(str);
}
var newFunc = makeNewFunc( funcString );
console.log('newFunc:',newFunc);
//-> newFunc: function anonymous()
console.log('newFunc():',newFunc());
//-> newFunc(): 4
This demonstrates how functions can be created and invoked from String snippets. (EDIT: Turning something like that into a Node module is a simple matter);
Hope that helped. :)

Calling a function bypassing eval()

I am working with a Javascript code that uses eval function.
eval(myString)
The value of myString = myFunc(arg), I want to call myFunc directly without using eval.
I dont have any control over the function to call as I am getting that function as a String (here myString).
The arguments to that function is also part of the same string.
So, is there any way through which I can call the intended function without using eval?
I'm a bit skeptical of allowing users to provide function names at all, but... Assume you have the function name in a variable and the value of arg in a variable. Boom:
var myString = window[fn](arg);
arg is already presumably in an argument, so that's simple enough. The next part is exatracting the function name. Just a bit of regex:
var fn = /^([a-z0-9_]+)\(arg\)$/i.exec(str)[1];
if (fn && typeof window[fn] === 'function') {
window[fn](arg);
}
This does of course assume that the function is always in the global scope, but if it's not, you should be able to adjust accordingly for that. Also, my regex is just the first thing I came up with. It probably doesn't cover all possible function names.
If you wanted to limit the string to just a certain set of functions (and you almost certainly should), that becomes quite easy as well once you have the function name:
var allowedFunctions = {fn1: fn1, fn2: fn2, someOtherFunction: function() {} },
fn = /^([a-z0-9_]+)\(arg\)$/i.exec(str)[1];
if (fn && allowedFunctions[fn]) {
allowedFunctions[fn](arg);
} else {
// Hah, nice try.
}
(If arg isn't actually a variable name but is some kind of literal or perhaps an arbitrary expression, this gets a little more complicated and a lot less safe.)
JavaScript does not provide any way of calling a function represented as a string, other than using eval. There's nothing wrong about using it, though. Given that you have no other option.
Possibly you may try using Function:
var sure = function(s) {
return confirm(s);
};
var str = 'sure("Are you sure?")';
var rtn = new Function('return ' + str)();
alert(rtn);

JavaScript: alert object name as a string

I'm trying to alert any JavaScript object as a string, in a function. This means if the parameter given to the function is window.document, the actual object, it should alert "window.document" (without quotes) as a literal string.
The following calls...
example(window);
example(window.document);
example(document.getElementById('something'));
...calling this function...
function example(o) {/* A little help here please? */}
...should output the following strings...
window
window.document
document.getElementById('something')
I've attempted to do this with combinations of toString() and eval() among some more miscellaneous shots in the dark without success.
No need insane backwards compatibility, newer ECMAScript / JavaScript features/functions are fine. Feel free to inquire for clarifications though the goal should be pretty straight forward.
This is not possible to do in a self contained script.
If using a preprocessor would be an option, then you could write one which converts example(whatever) into example('whatever'). Other than that I'm afraid you're out of luck.
The first problem is that objects don't have names.
The second problem is that from your examples, you're not really wanting to print the (nonexistent) name of an object, you want to print the expression that evaluated into a reference to an object. That's what you're trying to do in this example:
example(document.getElementById('something'));
For that to print document.getElementById('something'), JavaScript would have had to keep the actual text of that expression somewhere that it would make available to you. But it doesn't do that. It merely evaluates the parsed and compiled expression without reference to the original text of the expression.
If you were willing to quote the argument to example(), then of course it would be trivial:
example( "document.getElementById('something')" );
Obviously in this case you could either print the string directly, or eval() it to get the result of the expression.
OTOH, if you want to try a real hack, here's a trick you could use in some very limited circumstances:
function example( value ) {
var code = arguments.callee.caller.toString();
var match = code.match( /example\s*\(\s*(.*)\s*\)/ );
console.log( match && match[1] );
}
function test() {
var a = (1);
example( document.getElementById('body') );
var b = (2);
}
test();
This will print what you wanted:
document.getElementById('body')
(The assignments to a and b in the test() function are just there to verify that the regular expression isn't picking up too much code.)
But this will fail if there's more than one call to example() in the calling function, or if that call is split across more than one line. Also, arguments.callee.caller has been deprecated for some time but is still supported by most browsers as long as you're not in strict mode. I suppose this hack could be useful for some kind of debugging purposes though.
Don't know why you need this, but you can try walking the object tree recursively and compare its nodes with your argument:
function objectName(x) {
function search(x, context, path) {
if(x === context)
return path;
if(typeof context != "object" || seen.indexOf(context) >= 0)
return;
seen.push(context);
for(var p in context) {
var q = search(x, context[p], path + "." + p);
if(q)
return q;
}
}
var seen = [];
return search(x, window, "window");
}
Example:
console.log(objectName(document.body))
prints for me
window.document.activeElement

Calling javascript function with an objectstring in dot notation

Suppose I have the string:
var string = "function";
With
window[string];
I can call a function with the name of "function".
But, when I have:
var string2 = "function.method.weHaveTogoDeeper";
it should call
window["function"]["method"]["weHaveTogoDeeper"]
I can't do:
window[string2]
in this case. I dont know the number of "." in the string, so I need some kind of routine.
you can split the string across . by using the String.split method:
var string2 = "function.method.weHaveTogoDeeper";
var methods = string2.split(".");
In this examples, methods will be the array ["function","method","weHaveTogoDeeper"]. You should now be able to do a simple iteration over this array, calling each function on the result of the previous one.
Edit
The iteration I had in mind was something like this:
var result = window;
for(var i in methods) {
result = result[methods[i]];
}
In your example, result should now hold the same output as
window["function"]["method"]["weHaveTogoDeeper"]
function index(x,i) {return x[i]}
string2.split('.').reduce(index, window);
edit: Of course if you are calling functions from strings of their names, you are likely doing something inelegant which would be frowned upon, especially in a collaborative coding settings. The only use case I can think of that is sane is writing a testing framework, though there are probably a few more cases. So please use caution when following this answer; one should instead use arrays, or ideally direct references.
I wrote one a while back:
function RecursiveMapper(handlerName, stack) {
// check if empty string
if(!handlerName || handlerName === '' || (handlerName.replace(/\s/g,'') === '')) return null;
var buf = handlerName.split('.');
stack = stack || window;
return (buf.length === 1) ? stack[buf[0]] : this.RecursiveMapper(buf.slice(1).join('.'), stack[buf[0]]);
}
Call it like this: RecursiveMapper(window[string2]);
This one also checks if the function is defined in window scope first and returns the global one fi found.

Categories