7

I have a string that contains Boolean logic, something along the lines of:

((true && true) || false && !true)

What is the best way in Javascript to safely evaluate this string to get the boolean result? I would like to avoid using eval().

9
  • 4
    If you don't want to use eval() you'll have to write a parser for your expression grammar. Commented Dec 6, 2015 at 15:06
  • 1
    You can use Function constructor. Function('return ' + expression)() Commented Dec 6, 2015 at 15:10
  • 2
    @jcubic that's true, but somebody who doesn't want to use eval() probably doesn't want to use something equivalently dangerous. Commented Dec 6, 2015 at 15:11
  • 1
    You can use parser generator like PEG pegjs.org to parse your expressions. Commented Dec 6, 2015 at 15:12
  • You can do setTimeout(expressionString,0) but this is like eval Commented Dec 6, 2015 at 15:13

2 Answers 2

3

I wrote this boolean string parser for another question:

var exp1 = "(true && true || false) && (true || (false && true))";
var exp2 = "((true && true) || false && !true)";
var exp3 = "(true && !false) && true && !false";
var exp4 = "(a && b) && c && d";

console.log(exp1 + ' = ' + parseBoolStr(exp1));
console.log(exp2 + ' = ' + parseBoolStr(exp2));
console.log(exp3 + ' = ' + parseBoolStr(exp3));
console.log(exp4 + ' = ' + parseBoolStr(exp4));

function parseBoolStr(str) {
  var expressions = {};
  var expressionRegex = new RegExp("\\((?:(?:!*true)|(?:!*false)|(?:&&)|(?:\\|\\|)|\\s|(?:!*\\w+))+\\)");
  var expressionIndex = 0;
  str = str.trim();
  while (str.match(expressionRegex)) {
    var match = str.match(expressionRegex)[0];
    var expression = 'boolExpr' + expressionIndex;
    str = str.replace(match, expression);
    match = match.replace('(', '').replace(')', '');
    expressions[expression] = match;
    expressionIndex++;
  }
  return evalBoolStr(str, expressions);
}

function evalBoolStr(str, expressions) {
  var conditions = str.split(' ');
  if (conditions.length > 0) {
    var validity = toBoolean(conditions[0], expressions);
    for (var i = 1; i + 1 < conditions.length; i += 2) {
      var comparer = conditions[i];
      var value = toBoolean(conditions[i + 1], expressions);
      switch (comparer) {
        case '&&':
          validity = validity && value;
          break;
        case '||':
          validity = validity || value;
          break;
      }
    }
    return validity;
  }
  return 'Invalid input';
}

function toBoolean(str, expressions) {
  var inversed = 0;
  while (str.indexOf('!') === 0) {
    str = str.replace('!', '');
    inversed++;
  }
  var validity;
  if (str.indexOf('boolExpr') === 0) {
    validity = evalBoolStr(expressions[str], expressions);
  } else if (str == 'true' || str == 'false') {
    validity = str == 'true';
  } else {
    validity = window[str]();
  }
  for (var i = 0; i < inversed; i++) {
    validity = !validity;
  }
  return validity;
}

function a() {
  return false;
}
function b() {
  return true;
}
function c() {
  return true;
}
function d() {
  return false;
}

Functions a, b, c and d can, and should, be removed. Just an example.

Usage: parseBoolStr('((true && true) || false && !true)');

Sign up to request clarification or add additional context in comments.

Comments

0

You can use Angular $parse service. Angular parsing expressions, without eval them.

Example of use:

var x=$parse(' ((true && true) || false && !true) ')()
console.log(x)  // will return true

ReadMore:

var a=angular.module('app',[])
a.run(function($parse){
  alert($parse('true || false || true || false')())
  })
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app=app>

1 Comment

This is getting close. Is there an equivalent to this for ReactJS?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.