First of all, if it is much easier using either Boost Variant or Utree, then I will settle with them, and i will try to solve my issues with them in another topic. However, i would very much like to be able to build a tree like i have below.
Background, ignore if you would like to go straight to the issue: I would like to be able to build an expression tree which parses something like
"({a} == 0) && ({b} > 5)"
or a standard mathmatic expression
"(2 * a) + b"
I will then define what a and b are before i evaluate my tree, something like this:
a = 10;
double val = myExpression->Evaluate();
My issue comes from when i try to build the try to parse the string into my Expression Tree. I am using an abstract class "Expression" which then derives "Variable", "Constant" and "Binary" expressions (it will also do unary, but it shouldnt effect my problem. I keep having problems with adding to the tree using my rules, so im clearly doing something wrong. Im having a hard time wrapping my head around the attributes.
My Tree is as follows (Tree.h):
class BinaryExpression;
typedef double (*func)(double, double);
class Expression
{
public:
virtual double Evaluate() = 0;
};
class BinaryExpression : public Expression
{
private:
Expression* lhs;
Expression* rhs;
func method;
double Evaluate();
public:
BinaryExpression(void);
BinaryExpression(char op, Expression* lhs, Expression* rhs);
BinaryExpression(char op);
void operator()(Expression* lhs, Expression* rhs);
};
class ConstantExpression : public Expression
{
private:
double value;
public:
ConstantExpression(void);
ConstantExpression(char op);
ConstantExpression(double val);
double Evaluate();
};
// Require as many types as there are fields in expression?
static double a;
static double b;
class VariableExpression : public Expression
{
private:
char op;
public:
VariableExpression(char op);
double Evaluate();
};
BOOST_FUSION_ADAPT_STRUCT(
BinaryExpression,
(Expression*, lhs)
(Expression*, rhs)
(func, method)
)
BOOST_FUSION_ADAPT_STRUCT(
VariableExpression,
(char, op)
)
BOOST_FUSION_ADAPT_STRUCT(
ConstantExpression,
(double, op)
)
Tree.cpp
typedef double (*func)(double, double);
/////////////////////////////////////////////////////////////////////////////
// BINARY EXPRESSION
////////////////////////////////////////////////////////////////////////////
BinaryExpression::BinaryExpression(void) {}
BinaryExpression::BinaryExpression(char op, Expression* lhs, Expression* rhs)
{
this->lhs = lhs;
this->rhs = rhs;
// Example, methods are held in another header
if (op == '+')
method = Add;
else if (op == '-')
method = Subtract;
}
double BinaryExpression::Evaluate()
{
return method(lhs->Evaluate(), rhs->Evaluate());
}
BinaryExpression::BinaryExpression(char op)
{
if (op == '+')
method = Add;
else if (op == '-')
method = Subtract;
}
void BinaryExpression::operator()(Expression* lhs, Expression* rhs)
{
this->lhs = lhs;
this->rhs = rhs;
}
/////////////////////////////////////////////////////////////////////////////
// CONSTANT EXPRESSION
////////////////////////////////////////////////////////////////////////////
ConstantExpression::ConstantExpression() {}
ConstantExpression::ConstantExpression(char op)
{
this->value = op - 48;
}
ConstantExpression::ConstantExpression(double val)
{
value = val;
}
double ConstantExpression::Evaluate()
{
return value;
}
/////////////////////////////////////////////////////////////////////////////
// VARIABLE EXPRESSION
////////////////////////////////////////////////////////////////////////////
VariableExpression::VariableExpression(char op)
{
this->op = op;
}
double VariableExpression::Evaluate()
{
// a and b are defined in the header, and are used to fill in the variables we want to evaluate
if (op == 'a')
return a;
if (op == 'b')
return b;
return 0;
}
Now if i build the tree manually it all works fine, so i dont think theres an issue with the way it is structured.
Here is Grammar.h (Lots of comments from where i tried various things, i could remove them, but i may be worth showing what i've tried / where i want to go with it)
#include "Tree.h"
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
qi::_1_type _1;
qi::_2_type _2;
// Pass functions to boost
boost::phoenix::function<BinaryExpression> plus = BinaryExpression('+');
boost::phoenix::function<BinaryExpression> minus = BinaryExpression('-');
template <typename Iterator>
struct ExpressionParser : qi::grammar<Iterator, BinaryExpression(), ascii::space_type>
{
ExpressionParser() : ExpressionParser::base_type(expression)
{
qi::_3_type _3;
qi::_4_type _4;
qi::char_type char_;
qi::uint_type uint_;
qi::_val_type _val;
qi::raw_type raw;
qi::lexeme_type lexeme;
qi::alpha_type alpha;
qi::alnum_type alnum;
qi::bool_type bool_;
qi::double_type double_;
expression = //?
additive_expr [_val = _1]
;
//equality_expr =
// relational_expr >>
// *(lit("==") > relational_expr) [/*Semantice action to add to tree*/]
// ;
additive_expr =
primary_expr >>
( '+' > primary_expr) [plus(_val, _1)]
| ( '-' > primary_expr) [minus(_val, _1)]
;
// Also tried "_val = plus(_1, _2)"
primary_expr =
constant [_val = _1]
| variable [_val = _1]
//| '(' > expression > ')' [_val = _1]
;
string %=
'{' >> *(char_ - '}') >> '}'
;
// Returns ConstantExpression
constant =
double_ [_val = _1];
// Returns VariableExpression
variable =
char_ [_val = _1]
;
}
// constant expression = double
// variable expression = string
qi::rule<Iterator, BinaryExpression(), ascii::space_type>
expression;
qi::rule<Iterator, BinaryExpression(), ascii::space_type>
// eventually will deal with all these rules
equality_expr,
relational_expr,
logical_expr,
additive_expr,
multiplicative_expr,
primary_expr
;
qi::rule<Iterator, ConstantExpression(), ascii::space_type>
constant
;
qi::rule<Iterator, VariableExpression(), ascii::space_type>
variable
;
qi::rule<Iterator, std::string(), ascii::space_type>
string
;
};
So this is a really hacked apart, but hopefully it will show what im trying to achieve. Any advice or tips would be really appreciated. Is there an example where someone has built a tree like this without using variant or utree.
Also sorry if ive broken convention, and for my formatting, i tried to make it as readable as possible.
It isn't clear to me what your gripe with (recursive) variants are, but here is a variation that goes along with your wish to use 'old fashioned' tree building using dynamically allocated nodes:
I have purposefully sidestepped the issue of operator precedence in your grammar because
You can learn about these in other answers:
Note how I
makebinary
actor now.Note how chains of operators (1+2+5+6-10) are now supported:
I added
{var}
,/
,*
and(expr)
supportadded serialization for display (
Print
virtual method,operator<<
) (for display convenience, BinaryExpression stores theoperator
instead of the resultantmethod
now)Expression
toAbstractExpression
(and made de constructor protected)PrimaryExpression
toExpression
(and this is now your main expression datatype)static
mapqi::symbols
andvariable
now)Uses the templated constructor trick to make it very easy to construct an expression from disparate parsed types:
is enough to efficiently support e.g.:
for fun have included a few more test cases:
Full Code