1

I'm working on a test project that will ideally be built into a larger project (wanting to build my own scripting language for fun) in the future that parses expressions into a tree structure using my custom classes.

My expected result is an Expression that can be evaluated but the actual result is an expression that has a null AbstractExpression pointer.

Expression Classes

Note: State is a struct that I plan to use to hold variables and other information needed to evaluate

class AbstractExpression
{
public:
    AbstractExpression()
    {
    }

    virtual double const Evaluate(State &state) = 0;
    virtual ~AbstractExpression() {}
};

class BinaryExpression : public AbstractExpression
{
public:
    typedef double(*OperatorFunction)(double, double);

    BinaryExpression(char op, AbstractExpression *leftOperand, AbstractExpression *rightOperand)
    {
        this->left = leftOperand;
        this->right = rightOperand;
        this->operatorFunction = GetFunction(std::string(1, op));
    }

    BinaryExpression(std::string op, AbstractExpression *leftOperand, AbstractExpression *rightOperand)
    {
        this->left = leftOperand;
        this->right = rightOperand;
        this->operatorFunction = GetFunction(op);
    }

    ~BinaryExpression()
    {
        delete this->left;
        delete this->right;
    }

    double const Evaluate(State &state)
    {
        return this->operatorFunction(this->left->Evaluate(state), this->right->Evaluate(state));
    }

private:
    AbstractExpression *left;
    AbstractExpression *right;
    OperatorFunction operatorFunction;

    static double Add(double left, double right) { return left + right; }
    //<... A bunch of definitions like the above ...>    
    static double NoOp(double left, double right) { return 0; }

    static BinaryExpression::OperatorFunction GetFunction(std::string op)
    {
        if (op.compare("+") == 0)
        {
            return &Add;
        }
        //<... Lost of else if statements ...>
        else
        {
            return &NoOp;
        }
    }
};

class ConstantExpression : public AbstractExpression
{
public:
    ConstantExpression(double value)
    {
        this->value = value;
    }

    ConstantExpression()
    {
        this->value = 0;
    }

    double const Evaluate(State &state)
    {
        return this->value;
    }

private:
    double value;
};

class Expression : public AbstractExpression
{
private:
    AbstractExpression *expression;

public:
    Expression()
    {
    }

    Expression(AbstractExpression *expression)
    {
        this->expression = expression;
    }

    ~Expression()
    {
        //delete this->expression;
    }

    double const Evaluate(State &state)
    {
        return this->expression->Evaluate(state);
    }
};

Parser

namespace Test
{
    template <typename Iterator>
    struct StatementGrammar : qi::grammar < Iterator, Expression(), ascii::space_type >
    {
        StatementGrammar() : StatementGrammar::base_type(expression)
        {
            expression =
                constant[qi::_val = phoenix::construct<Expression>(qi::_1)]
                ;

            constant =
                qi::double_[qi::_val = &(phoenix::construct<ConstantExpression>(qi::_1))]
                ;
        }

        qi::rule < Iterator, Expression(), ascii::space_type > expression;
        //qi::rule < Iterator, BinaryExpression*(), ascii::space_type> binaryExpression;
        qi::rule < Iterator, ConstantExpression*(), ascii::space_type> constant;
    };
}

Below is the code that I'm using to call the parser:

std::string string;
Expression exp;

std::getline(std::cin, string);

using boost::spirit::ascii::space;
typedef std::string::const_iterator iterator_type;

Test::StatementGrammar<iterator_type> grammar;

std::string::const_iterator iter = string.begin();
std::string::const_iterator end = string.end();

bool result = qi::phrase_parse(iter, end, grammar, space, epx);

if (result)
{
    State s;
    double foo = exp.Evaluate(s);
}
else
{
    std::cout << "No Match!" << std::endl;
}

return 0;

I'm mostly a C# developer (read: I'm a C++/Boost noob) please correct me on everything I'm doing wrong in C++ (I need to learn where my mistakes are!).

amura.cxg
  • 2,348
  • 3
  • 21
  • 44
  • Thanks @sehe for the link to your answer. I did read through it previously and it's a very detailed answer but I couldn't make sense of all of it, probably due to my lack of experience with C++ – amura.cxg Apr 15 '15 at 04:35

1 Answers1

1

In this line:

qi::double_[qi::_val = &(phoenix::construct<ConstantExpression>(qi::_1))]

phoenix::construct constructs a temporary ConstantExpression, whose address is later taken with the operator &. Normally taking the address of a temporary results in a compilation error, but most likely this language feature has been lost somewhere inside phoenix implementation. What you probably want to do is:

qi::double_[qi::_val = phoenix::new_<ConstantExpression>(qi::_1)]

As a side note, Expression should probably implement some kind of reference counting of the wrapped expression in order to call delete when done. Ideally you don't need ref counting here, since move semantics/unique ownership is sufficient, but I do not believe that Spirit v2 works well with move semantics.

sbabbi
  • 11,070
  • 2
  • 29
  • 57
  • Thanks for the answer, that makes sense. I'm a little fuzzy on how I would make this work with the `BinaryExpression` since it's constructor expects pointers. Would I need to have a constructor that takes parameters by value and makes copies? – amura.cxg Apr 15 '15 at 04:33