ExprTK unknown variable resolution depending on expression type
Asked Answered
M

1

8

I am trying to create a parser for boolean expressions. The symbols inside the expression are read from an XML-like data structure.

It is simple to implement a parser for something like

a.b == 'some value'

using ExprTK by using a "unknown symbol resolver" which resolves a.b as a string by returning the string value of <a><b>some value</b></a>.

But now consider the XML <a><b>5</b></a>

Is there any way to write a unknown symbol resolver which allows to evaluate both a.b == 5 and a.b == '5'?

Musclebound answered 11/4, 2017 at 12:56 Comment(0)
G
11

Initially, in ExprTk, a variable (user defined or expression local) can only be of one type (scalar, string or vector-of-scalars). So if your expression is:

"a.b == 5 and a.b == '5'"

Then that is an invalid expression, as the variable a.b can only have one type - either a scalar or string, but not both.

However if you're wanting to have two separate expressions that use the same variable name but in different contexts, like so:

  • a.b == 5
  • a.b == '5'

Then Yes, ExprTk's USR (Unknown Symbol Resolver) functionality does provide one the means to determine the unknown symbol's type during the invocation of USR callback, allowing for the expression to be correctly compiled.


As an example, lets assume we'd like to define a USR that will only resolve unknown symbols with the prefixes "var_" and "str_" with the types Scalar and String respectively.

Example expressions might look like the following:

var_x := 2; var_x + 7

str_y := 'abc';  str_y + '123' == 'abc123'

The following is an example USR utilising the extended callback mechanism that will resolve variables in the above denoted format, and furthermore add them to the primary symbol table of the expression being parsed:

typedef exprtk::symbol_table<double> symbol_table_t;
typedef exprtk::parser<double>             parser_t;

template <typename T>
struct my_usr : public parser_t::unknown_symbol_resolver
{
   typedef typename parser_t::unknown_symbol_resolver usr_t;

   my_usr()
   : usr_t(usr_t::e_usrmode_extended)
   {}

   virtual bool process(const std::string& unknown_symbol,
                        symbol_table_t&      symbol_table,
                        std::string&        error_message)
   {
      bool result = false;

      //Is this unknown symbol in the format var_xyz ?
      if (0 == unknown_symbol.find("var_"))
      {
         const T default_scalar = T(0);
         result = symbol_table.create_variable(unknown_symbol, default_scalar);
         if (!result)
         {
            error_message =
             "Failed to create variable(" + unknown_symbol + ") in primary symbol table";
         }
      }
      //Is this unknown symbol in the format str_xyz ?
      else if (0 == unknown_symbol.find("str_"))
      {
         const std::string default_string = "N/A";
         result = symbol_table.create_stringvar(unknown_symbol,default_string)
         if (!result)
         {
            error_message =
             "Failed to create string variable(" + unknown_symbol + ") in primary symbol table";
         }
      }
      else
         error_message = "Indeterminable symbol type.";

      return result;
   }
};

The rest of the code is the same: One registers the instantiated USR with the parser and then proceeds to compile their expression with said parser.

For more information have a review of Section 18 - Unknown Unknowns

Gassing answered 17/4, 2017 at 20:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.