I want to understand what exactly happens under the hood of boost::spirit::qi. Suppose we have simple parser that parses and calculates expressions consisting of numbers and add/subtract operations:
int main()
{
std::string INPUT_DATA = "12e-1 + 3.4 - .67";
typedef std::string::iterator iterator_type;
iterator_type begin = std::begin(INPUT_DATA);
iterator_type end = std::end(INPUT_DATA);
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::qi::ascii;
auto parser = qi::double_[qi::_val = qi::_1] // (1)
>> *(
(qi::lit('+') >> qi::double_[qi::_val += qi::_1]) // (2)
|
(qi::lit('-') >> qi::double_[qi::_val -= qi::_1]) // (3)
);
double result;
bool ok = qi::phrase_parse(begin, end, parser, ascii::space, result);
if ( ok && begin == end)
{
std::cout << "parsed, result = " << result << std::endl;
}
else
{
std::cout << "not parsed" << std::endl;
}
return 0;
}
How come qi::_val
in semantic actions in lines (1)
, (2)
and (3)
refer to the same value? How do I achieve the same result without using boost::phoenix?
I suppose I have to write a bunch of functors that will receive the parsed value from qi::double_
, but what should I do with it? How do I access synthesized value of a parser?
In addition to the exquisite low-level information provided in the comments, let me show you the ways of Spirit.
Of course, I'll end on a demo that doesn't use semantic actions. Yes, it involves more code, but it also decouples the parsing from the evaluation. This is good in more complex situations (think about parsers that backtrack).
1.
Starting from a slight simplification of your code: step 1
2.
You could, of course use regular binds to functions: step 2
3.
Now, using BOOST_PHOENIX_ADAPT_FUNCTION makes it slightly prettier: step 3
4.
Or you can employ a functor: step 4
Ouch, so much for pretty.
5.
But, no worries, you can adapt those too: step 5
Finally: go Pro
And finally, you could do this totally without any semantic actions, by using a simple AST:
Now we parse into an expression AST:
And we print the result using an
eval
function:How does it work? See for yourself: