Вычислить строку, дающую логическое выражение в JavaScript

У меня есть строка, содержащая логическую логику, например:

var test = "(true)&&(false)&&!(true||true)"

Каков хороший способ оценить эту строку в JavaScript, чтобы получить логическое значение false в этом случае

  1. Я знаю, что мы могли бы использовать eval () или новую функцию () .. - но это безопасный подход?
  2. Я предполагаю, что другим вариантом было бы написать собственный парсер. Неужели для того, чтобы быть довольно новым человеком в JS, потребуется много усилий? Мне не удалось найти примеров парсеров для логических выражений
  3. Есть ли другие альтернативы?

person akoy    schedule 01.04.2016    source источник
comment
Что является источником "(true)&&(false)&&!(true||true)"?   -  person Rayon    schedule 01.04.2016
comment
Я анализирую ответ сервера и строю эту строку на основе некоторой логики   -  person akoy    schedule 01.04.2016
comment
После применения логики вы можете использовать данные, возвращаемые сервером, как bool. Почему вы конвертируете его в строку?   -  person Gibbs    schedule 01.04.2016
comment
eval безопасно использовать только тогда, когда вы можете гарантировать, что источник не является вредоносным и не содержит проблемы. Если вы создаете строку, и вы написали свой код, чтобы гарантировать создание только логической строки, все должно быть в порядке.   -  person Akshat Mahajan    schedule 01.04.2016
comment
Если бы синтаксис был похож на "(1)&(0)&!(1||1)" (все токены состоят только из 1 символа), было бы легче разобрать   -  person Oriol    schedule 01.04.2016
comment
Использование new Function() не имеет смысла, потому что на самом деле вам не нужна функция, вам просто нужен результат выражения.   -  person nnnnnn    schedule 01.04.2016
comment
Если бы я мог сделать строку (1) & (0) &! (1 || 1), как бы это упростило синтаксический анализ?   -  person akoy    schedule 01.04.2016
comment
@nnnnnn Может использоваться как new Function("return " + boolStr)(). Конечно eval(boolStr) было бы лучше. Оба могут запускать произвольный JS, поэтому избегайте их, если источнику не доверяют.   -  person Oriol    schedule 01.04.2016
comment
@akoy Потому что длина всех токенов - 1 символ. Это было бы не намного проще, но немного.   -  person Oriol    schedule 01.04.2016
comment
Я не понимаю ... Может ли ваш сервер отвечать более распространенными способами, например, JSON или что-то в этом роде. чтобы вы могли сравнивать свои вары, не используя eval? ИЛИ, если сервер возвращает всю строку, почему сервер не может самостоятельно проверить это условие и ответить одним _2 _ / _ 3_ для всего условия?   -  person haldagan    schedule 01.04.2016
comment
@Oriol - Да. Я не хотел сказать, что вы не можете достичь этого с помощью new Function(), я просто имел в виду, что это бессмысленно сложная альтернатива eval(). В проблеме с одним символом 1 | 0 вы можете просто использовать .replace () перед синтаксическим анализом ...   -  person nnnnnn    schedule 01.04.2016
comment
если ваш сервер возвращается как (true) && (false) &&! (true || true), тогда вы не сможете напрямую оценить все выражение при подаче и вернуть только результат как истинный или ложный?   -  person Dhananjaya Kuppu    schedule 01.04.2016


Ответы (5)


Если вы можете гарантировать безопасность, я думаю, вы можете использовать eval.

Может быть, обработав его перед выполнением eval?

var test = "(true)&&(false)&&!(true||true)" 

var safe = test.replace(/true/ig, "1").replace(/false/ig, "0");

var match = safe.match(/[0-9&!|()]*/ig);

if(match) {
   var result = !!eval(match[0]);
}
person loxxy    schedule 01.04.2016
comment
Невозможно использовать, если Политика безопасности контента отключила оценку кода. хотя. - person Alexander O'Mara; 01.04.2016

В Javascript есть тернарный оператор, который вы можете использовать:

var i = result ? 1 : 0;

Здесь результатом является значение Boolean либо True, либо False.

Итак, после этой операции Ваш вопрос будет примерно таким.

(1)&(0)&!(1||1)

Надеюсь, теперь вы сможете лучше оценить эту логику.

person Rohit Jindal    schedule 01.04.2016

вы можете использовать eval, например: eval ("(true) && (false) &&! (true || true)");

person Joseph    schedule 01.04.2016

Попробуйте этот код

function processExpression(expr)
{
  while (expr.indexOf("(" ) != -1 )
  {
    expr = expr.replace(/\([\w|]+\)/g, function(matched){ return processBrace(matched)});
  }
  return expr = processBrace( "(" + expr + ")" );
}
function processBrace(str)
{
    return str.substring(1).slice(0,-1).split(/(?=&|\|)/).map(function(value,index,arr){ 
        if ( index != 0 && index%2 == 0 ) { return arr[index-1] + value } else if(index==0){return value;} else {return ""}
    }).filter(function(val){return val.length > 0}).reduce(function(prev,current){
        var first = Boolean(prev);
        var operator = current.substring(0,2);
        var operand = current.substring(2); 
        while ( operand.indexOf("!") != -1 )
        {
           var boolval = operand.match(/\w+/)[0] == "false"; //flip the value by comparing it with false
           var negations = operand.match(/\W+/)[0];
           operand = negations.substring(1) + boolval;
        }
        var second = operand == "true";
        var output = operator == "&&" ? (first && second) : (first || second); 
        return output;
    });
}

ДЕМО

    function processExpression(expr)
    {
      while (expr.indexOf("(" ) != -1 )
      {
    	expr = expr.replace(/\([\w|]+\)/g, function(matched){ return processBrace(matched)});
      }
      return expr = processBrace( "(" + expr + ")" );
    }
    function processBrace(str)
    {
    	return str.substring(1).slice(0,-1).split(/(?=&|\|)/).map(function(value,index,arr){ 
    		if ( index != 0 && index%2 == 0 ) { return arr[index-1] + value } else if(index==0){return value;} else {return ""}
    	}).filter(function(val){return val.length > 0}).reduce(function(prev,current){
    		var first = Boolean(prev);
    		var operator = current.substring(0,2);
    		var operand = current.substring(2); 
    		while ( operand.indexOf("!") != -1 )
    		{
    		   var boolval = operand.match(/\w+/)[0] == "false"; //flip the value by comparing it with false
    		   var negations = operand.match(/\W+/)[0];
    		   operand = negations.substring(1) + boolval;
    		}
    		var second = operand == "true";
    		var output = operator == "&&" ? (first && second) : (first || second); 
    		return output;
    	});
    }


var example1 = "(true)&&(false)&&!(true||true)";
document.body.innerHTML += example1 + " -- " + processExpression(example1);

person gurvinder372    schedule 02.04.2016

Попробуйте использовать "".match() в условиях тернарного оператора

"(true)&&(true)&&!(true||true)".match(/false/ig)?false:true
person Muhammad Kamran    schedule 11.01.2021
comment
В заявлении есть символ !. - person thealpha93; 11.01.2021
comment
Спасибо за ваш комментарий, но понижение оценки вашего собственного мнения - неправильный путь, прошу вас быть позитивными и не понижать оценку моего комментария. На самом деле я не пишу ! в своем комментарии, я просто скопировал его у автора вопроса. Спасибо - person Muhammad Kamran; 16.03.2021