ExprTK unknown variable resolution depending on expression type
I am trying to create a parser for boolean expressions. The characters within the expression are read from an XML-like data structure.
It's easier to implement a parser for something like
a.b == 'some value'
with ExprTK with an "unknown character recognizer" which resolves ab as a string, returning a string value <a><b>some value</b></a>
.
But now let's look at XML <a><b>5</b></a>
Is there a way to write an unknown character recognition, which allows you to evaluate how a.b == 5
, and a.b == '5'
?
Initially in ExprTk, a variable (user-defined or local expression) can only be of one type (scalar, string, or scalar vector). So if your expression is:
"a.b == 5 and a.b == '5'"
Then this is not a valid expression, since the variable ab can only be of one type โ either a scalar or a string, but not both.
However, if you want to have two separate expressions that use the same variable name but in different contexts, for example:
-
a.b == 5
-
a.b == '5'
Then Yes, the ExprTk USR ( Unknown Symbol Resolver ) functionality provides one means of detecting an unknown symbol type during a USR callback , which allows the expression to be used to compile correctly.
As an example, suppose we would like to define a USR that will only allow unknown characters prefixed with "var _" and "str _" with types Scalar and String, respectively.
Examples of expressions might look like this:
var_x := 2; var_x + 7
str_y := 'abc'; str_y + '123' == 'abc123'
Below is an example of a USR using an extended callback mechanism that will resolve variables in the above format and additionally add them to the primary character table of the parsed expression:
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 USR instance with the parser, and then proceeds to compile its expression with the specified parser.
For more information see Section 18 - Unknown unknowns