I am a beginner in using boost spirit
Say that I have the following code that parse a simple arithmetic expression with variables:
#incl
The first, more involved approach described here modifies the AST to represent the variable references.
This has multiple advantages:
Let's start:
Modifying the AST:
We want to be able to have a variable name in addition to the existing types of expressions:
typedef boost::variant<
nil
, std::string // THIS LINE ADDED
, double
, boost::recursive_wrapper
, boost::recursive_wrapper
>
operand;
Modifying the rule:
Wwe want to keep the name of the variable instead of immediately replacing it by a fixed value:
factor =
qi::double_
| qi::as_string[qi::raw[symbolTable]] // THIS LINE CHANGED
| '(' > expression > ')'
| (char_('-') > factor)
| (char_('+') > factor)
;
Modifying the evalation visitor
Now we need to "replace" the variables by their value during evaluation.
Let's add a simple overload to the visitor:
double operator()(std::string const& var) const { return symbols.at(var); }
We have given the visitor a reference to a map symbols for lookup:
std::map& symbols;
eval(std::map& symbols) : symbols(symbols) {}
Calling it from main:
So we need to have a map of variables handy:
std::map symbols { {"var1", 2}, {"var2", 15}, {"var4", 5}, {"var", 5}, {"x", 5} };
And pass a reference to the visitor:
ast_eval eval(symbols) ; // Evaluates the program
At this point, the program operates exactly as the original program, but with an enriched AST:
Live On Coliru
Printing
-------------------------
Parsing succeeded
Result: 80
-------------------------
The point of the story becomes simple now: we just define another visitor like eval to extract references:
namespace client { namespace ast {
struct extract_refs : boost::static_visitor
{
std::set& _references;
extract_refs(std::set& refs) : _references(refs) {}
void operator()(std::string const& var) const { _references.insert(var); }
void operator()(operation const& x) const { boost::apply_visitor(*this, x.operand_); }
void operator()(signed_ const& x) const { boost::apply_visitor(*this, x.operand_); }
void operator()(program const& x) const {
boost::apply_visitor(*this, x.first);
BOOST_FOREACH(operation const& oper, x.rest) (*this)(oper);
}
// ignore anything else
template void operator()(Other const&) const {}
};
} }
This can simply be applied to the AST:
std::set references;
client::ast::extract_refs extract(references);
extract(program);
std::cout << "References: ";
std::copy(references.begin(), references.end(), std::ostream_iterator(std::cout, " "));
And the output once again becomes
-------------------------
Parsing succeeded
References: var var2 x
Result: 80
-------------------------
Quod Erat Demonstrandum.
Live On Coliru