2

I want to evaluate a conditional expression string, I defined following BNF:

X     ::= <value>    
COND  ::= X | X '==' X | '!{' COND '}' | '{' COND '&&' COND '}' | '{' COND '||' COND '}'

Based on this structur I wrote this evaluating-function, returning a boolean value:

-- Bedingung auswerten
  function eval(exp, valueTab, loopValue)
  -- !{COND} - Negierung
    if string.find(exp, '^!{(.+)}$') then
      return not eval(string.gsub(exp, '^!{(.+)}$', '%1'))
  -- {COND&&COND} - AND
    elseif string.find(exp, '^{(.+)&&(.+)}$') then
      return (eval(string.gsub(exp, '^{(.+)&&(.+)}$', '%1')) and eval(string.gsub(exp, '^{(.+)&&(.+)}$', '%2')))
  -- {COND||COND} - OR
    elseif string.find(exp, '^{(.+)||(.+)}$') then
      return (eval(string.gsub(exp, '^{(.+)||(.+)}$', '%1')) or eval(string.gsub(exp, '^{(.+)||(.+)}$', '%2')))
  -- X==X - Gleichheit -> true/false
    elseif string.find(exp, '^(.+)==(.+)$') then
      return (getValue(string.gsub(exp, '^(.+)==(.+)$', '%1'), valueTab, loopValue) == getValue(string.gsub(exp, '^(.+)==(.+)$', '%2'), valueTab, loopValue))
  -- X -> false wenn X nil/false auswertet ansonsten true
    else
      return (getValue(exp, valueTab, loopValue) and true or false)
    end
  end

But it's not working for some nested conditions like

exp = '{{1||boolean:false}&&{boolean:true&&!{boolean:false}}}'

The first recursive step splits the expression into

eval('{1||boolean:false}&&{boolean:true') and
eval('!{boolean:false}}'

Any idea how I can check, if the number of '{' is equal to the number of '}'? It should split the expression like

eval('{1||boolean:false}') and
eval('{boolean:true&&!{boolean:false}}')

I hope u understand my question, if u have any further questions let me know. I'm also willing to change my syntax, if u have a better idea. But negation, AND- and OR-Clauses should be supported.

3
  • 1
    Parsing expressions requires a proper parser. But try the pattern %b{}, which matches balanced {}. Commented Oct 11, 2018 at 12:16
  • '^{(!?%b{})&&(!?%b{})}$' looks pretty good, but i doesnt fetch the values. any idea how to get them without editing the BNF grammar? (for instance to '!'? '{' X }' and '!'? '{'X '==' X'}') Commented Oct 11, 2018 at 12:56
  • Anyway, thanks for this tip. I'll post the solution. Commented Oct 11, 2018 at 13:26

2 Answers 2

3

The grammar is pretty simple and LPEG allows you to basically copy the grammar almost verbatim, sprinkle it with some semantic actions and it just works™.

If you want to learn more about LPEG and parsing expression grammars, I recommend this tutorial: http://leafo.net/guides/parsing-expression-grammars.html

local lpeg = require"lpeg"
local P, V = lpeg.P, lpeg.V

local True = function() return true end
local False = function() return false end
local Not = function(a) return not a end
local Eq = function(a,b) return a == b end
local And = function(a,b) return a and b end
local Or = function(a,b) return a or b end

local grammar = P{
    "cond",
    cond = (V"x")
         + (V"x" * "==" * V"x") / Eq
         + (P"!{" * V"cond" * P"}") / Not
         + (P"{" * V"cond" * P"&&" * V"cond" * P"}") / And
         + (P"{" * V"cond" * P"||" * V"cond" * P"}") / Or,
    x = P"1" / True
      + P"0" / False
      + P"boolean:true" / True
      + P"boolean:false" / False
}

local eval = function(exp)
    return grammar:match(exp)
end

print(eval('{{1||boolean:false}&&{boolean:true&&!{boolean:false}}}'))
Sign up to request clarification or add additional context in comments.

Comments

1

Solution

Parsing expressions requires a proper parser. But try the pattern %b{}, which matches balanced {}.

I did it this way:

  1. Editing the BNF

    COND  ::= '{' (X|X '==' X|COND '&&' COND|COND '||' COND) '}'|'!' COND
    
  2. Editing pattern

    -- Bedingung auswerten
      function eval(exp, valueTab, loopValue)
      -- !{COND} - Negierung
        if string.find(exp, '^!(%b{})$') then
          return not eval(string.gsub(exp, '^!(%b{})$', '%1'))
      -- {COND&&COND} - AND
        elseif string.find(exp, '^{(!?%b{})&&(!?%b{})}$') then
          return (eval(string.gsub(exp, '^{(!?%b{})&&(!?%b{})}$', '%1')) and eval(string.gsub(exp, '^{(!?%b{})&&(!?%b{})}$', '%2')))
      -- {COND||COND} - OR
        elseif string.find(exp, '^{(!?%b{})||(!?%b{})}$') then
          return (eval(string.gsub(exp, '^{(!?%b{})||(!?%b{})}$', '%1')) or eval(string.gsub(exp, '^{(!?%b{})||(!?%b{})}$', '%2')))
      -- X==X - Gleichheit -> true/false
        elseif string.find(exp, '^{(.+)==(.+)}$') then
          return (getValue(string.gsub(exp, '^{(.+)==(.+)}$', '%1'), valueTab, loopValue) == getValue(string.gsub(exp, '^{(.+)==(.+)}$', '%2'), valueTab, loopValue))
      -- X -> false wenn X nil/false auswertet ansonsten true
        elseif string.find(exp, '^{(.+)}$') then
          return (getValue(string.gsub(exp, '^{(.+)}$', '%1'), valueTab, loopValue) and true or false)
        else
          print('wrong syntax')
          return
        end
      end
    

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.