Reputation: 640
I've got the Task to write a filter for complex and nested JSONs. To do this I need to evaluate Boolean Expressions which the user enters in an input field. For the sake of the question lets say we have got the following input:
(Key1 = "foo" || key2 = 2 && key3 = "bar") && key4 = 7
I managed to write a function to evaluate each keyValue-Pair to a either "True" or "False" and replaced them in the string: e.g.:
(True || False && True) && True
Now I want to parse this String to a boolean Expression. Is there any "easy" way (built in Function, Plugin) for Javascript which can parse this String?
Upvotes: 11
Views: 8118
Reputation: 479
I also wrote a small expression parser for my projects. It's missing a few operators and features but is pretty useful, and super light (1.6k minified)
Here's how it works :
https://github.com/nicopowa/operators
var myObject = {key1: "foo", key2: 2, key3: "bar", key4: 7};
var myExpression = "(key1 || key2 && key3) && key4";
var result = Operate.parse(myObject, myExpression);
Upvotes: 1
Reputation: 3539
https://github.com/joewalnes/filtrex
A simple, safe, JavaScript Filter Expression compiler for end-users
// Input from user (e.g. search filter)
var expression = '(key1 == "foo" || key2 == 2 && key3 == "bar") && key4 == 7';
// Compile expression to executable function
var myfilter = compileExpression(expression);
// Execute function
myfilter({Key1: "foo", key2: 2, key3: "bar", key4: 7}); // returns 1
myfilter({}); // returns 0
// filter an array of objects
var data = [{key1: "foo"}, {key1: "foo2"}];
var results = data.filter(myfilter);
the cost of this beauty: 92KByte minified bundle size
i started the project logic_fn, to do a similar job in a 1KByte minified bundle, by translating a custom logic expression (with custom operators like &, AND, -) to a javascript expression string, and feeding that to var f = new Function('input', 'return ' + expression + ';')
. new Function
is a bit more secure than eval
, but i still must defend against arbitrary code execution attacks.
Upvotes: 2
Reputation: 456
To avoid using eval, I used http://jsep.from.so/ and then wrote a function to evaluate the output from jsep:
function evaluateBooleanExpressionTree(tree) {
if (tree.type == "Literal") {
return tree.value;
}
else if (tree.type == "LogicalExpression") {
if (tree.operator == "&&") {
return evaluateBooleanExpressionTree(tree["left"]) && this.evaluateBooleanExpressionTree(tree["right"]);
}
else {
return evaluateBooleanExpressionTree(tree["left"]) || this.evaluateBooleanExpressionTree(tree["right"]);
}
}
else {
// Unexpected node parsed
return null;
}
}
evaluateBooleanExpressionTree(jsep("(true || false && true) && true"))
// true
Upvotes: 1
Reputation: 96891
There's eval()
which is usually not recommended to use but maybe in your case it's suitable. See for more details: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
You could write:
eval("(true || false && true) && true") // Returns: 1
Note, that I had to rewrite True
to true
because True
is not a recognised as a boolean expression.
However, scopes are applied to eval()
so I could also write:
var True = 1;
var False = 0;
eval("(True || False && True) && True"); // Returns: 1
This could work for you if you know the variables in advance I guess. Otherwise, maybe have a look at https://silentmatt.com/javascript-expression-evaluator/ or http://jsep.from.so/.
Upvotes: 6
Reputation: 238
It seems like it's a perfect place to use eval
. However, look through answers to this SO question to learn about drawbacks of this solution.
Upvotes: 0