在JavaScript逻辑表达式中查找将使表达式求值为true的变量值

时间:2018-02-23 21:10:17

标签: javascript

我有一个作为事件数据流发布的数据结构:

first = 12,
second = 20,
third = 120,
fourth = false,
fifth = "Fault"

下面我有关于上述值的警报/条件,如果警报逻辑表达式的计算结果为true,则会在后端产生警报。 例如。 报警1: (first < 10 && second > 25) || (first > 7 && second <= 22)

报警2: fourth = true && (fifth == "Fault" || fifth == "Error")

如果我的表达式用JavaScipt语法表达,我怎么能以编程方式找到哪些变量值/组合将使逻辑表达式成为真。

例如: Alarm1:first = 9,second = 20将评估为true。 (我想找到这些值)         first = 9将不足以评估为真。

那么如何找到一个或多个将使条件评估为真的变量值。

任何可以帮我解决这个问题的JavaScript解析器?

谢谢, 弧度

1 个答案:

答案 0 :(得分:0)

这听起来像是一台简单的状态机。

状态机知道要监视的变量名称,要评估的布尔表达式以及要执行的回调。回调接收布尔表达式的结果。

当将变量赋值表达式添加到状态机时,机器会检查它正在观察的所有变量是否已累积。如果没有,机器只存储变量赋值并等待下一个赋值。否则,机器使用其累积的变量赋值来评估布尔表达式。

这个ES6代码实现了这样一个状态机。它调用eval(),这会打开一个巨大的安全漏洞。所以请注意将JavaScript输入此代码。

function State(
  _variableNames /* array */,
  _booleanExpression /* string */,
  _callback /* function(boolean) -> void */)
{
  /* Mapping of variable names to their local assignments.
     E.g. 'first' -> 'let first = 42'

     See this.addVar(). */
  let _variableMap = new Map();
  
  function haveAllValuesBeenSet()
  {
    return _variableNames.every(name => _variableMap.has(name));
  }
  
  function getSafeAssignment(assignment)
  {
    /* Turn global assignments into local assignments
       by prepending 'let'.  This way the assigments
       won't pollute the global namespace when they're
       run thru eval(). */
       
    if (assignment.startsWith('let '))
      return assignment;
    else if (assignment.startsWith('var '))
      return 'let ' + assignment.substring(4);
    else
      return 'let ' + assignment;    
  }
  
  function evalExpression()
  {
    let assigments = '';
    
    /* IE 11 doesn't support the Map.values property. */
    for (let [_, assignment] of _variableMap)
    {
      assigments += assignment + ';\n';
    }
    
    let expression = assigments + _booleanExpression;
    
    /* Uncomment to see the full expression
       that's being evaluated. */
    console.log(expression);
    
    try
    {
      return eval(expression);
    }
    catch (ex)
    {
      console.log('ERROR in evalExpression(): ' + ex.message + '\n' + expression);
    }
  }

  /* Add a variable assigment to the state machine,
     e.g. "second = 55" */  
  this.addVar =
    function(assignment /* string */)
    {
      let parts = assignment.split('=').map(s => s.trim());
      let name = parts[0];
      let safeAssigment = getSafeAssignment(assignment);
      
      if (!_variableNames.includes(name))
        return;
      
      if (!_variableMap.get(name))
        _variableMap.set(name, safeAssigment);

      if (haveAllValuesBeenSet())
      {
        _callback(evalExpression());        
        _variableMap.clear();
      }
    };
}

function StateAggregator()
{
  let _states = [];
  
  this.add =
    function(state)
    {
      _states.push(state);
    };
    
  this.applyAssignment =
    function(assignment)
    {
      for (let state of _states)
      {
        state.addVar(assignment);
      }
    };
}

/* Create two state machines. */

var firstAndSecondState = new State(
  ['first', 'second'],
  '(first < 10 && second > 25) || (first > 7 && second <= 22)',
  function(booleanResult)
  {
    console.log('firstAndSecondState: ' + booleanResult);
  });

var fourthAndFifthState = new State(
  ['fourth', 'fifth'],
  'fourth === true && (fifth === "Fault" || fifth === "Error")',
  function(booleanResult)
  {
    console.log('fourthAndFifthState: ' + booleanResult);
  });
  
/* For convenience, a simple aggregator that allows a single variable
   assigment to be broadcast to all of the state machines. */
   
var agg = new StateAggregator();
agg.add(firstAndSecondState);
agg.add(fourthAndFifthState);
  
var dataStream = [
  'first = 12',
  'second = 20',
  'third = 120',
  'fourth = false',
  'fifth = "Fault"'
  ];
  
for (let assignment of dataStream)
{
  agg.applyAssignment(assignment);
}