I'm having trouble writing a Qi grammar which utilizes another Qi grammar. A similar question was asked here, but I'm also trying to use phoenix::construct and having compilation difficulties.
Here's a simplified version of what I'm trying to do. I realize that this example could probably be done easily using BOOST_FUSION_ADAPT_STRUCT
, but my actual code deals with more complex object types so I'm hoping there's a way to accomplish this using semantic actions.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_container.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <cstdlib>
#include <iostream>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
// grammar for real numbers
template <typename Iterator>
struct Real : qi::grammar<Iterator, long double()>
{
qi::rule<Iterator, long double()> r;
Real() : Real::base_type(r)
{
r %= qi::long_double;
}
};
// grammar for complex numbers of the form a+bi
template <typename Iterator>
struct Complex : qi::grammar<Iterator, std::complex<long double>()>
{
qi::rule<Iterator, std::complex<long double>()> r;
Real<Iterator> real;
Complex() : Complex::base_type(r)
{
r = real [qi::_a = qi::_1] >> (qi::lit("+") | qi::lit("-"))
>> real [qi::_b = qi::_1] >> -qi::lit("*") >> qi::lit("i")
[
qi::_val = phx::construct<std::complex<long double> >(qi::_a, qi::_b)
];
}
};
int main()
{
// test real parsing
std::cout << "Parsing '3'" << std::endl;
std::string toParse = "3";
Real<std::string::iterator> real_parser;
long double real_val;
std::string::iterator beginIt = toParse.begin();
std::string::iterator endIt = toParse.end();
bool r = qi::parse(beginIt, endIt, real_parser, real_val);
if(r && beginIt == endIt)
std::cout << "Successful parse: " << real_val << std::endl;
else
std::cout << "Could not parse" << std::endl;
// test complex parsing
std::cout << "Parsing '3+4i'" << std::endl;
toParse = "3+4i";
Complex<std::string::iterator> complex_parser;
std::complex<long double> complex_val;
beginIt = toParse.begin();
endIt = toParse.end();
r = qi::parse(beginIt, endIt, complex_parser, complex_val);
if(r && beginIt == endIt)
std::cout << "Successful parse: " << real_val << std::endl;
else
std::cout << "Could not parse" << std::endl;
}
I'm able to parse a Complex using the phrase_parse approach demonstrated in Spirit's documentation, but I'd like to be able to easily integrate the Complex grammar into other parsers (an expression parser, for instance). Is there something I'm missing that would allow me to parse Real and Complex objects as distinct entities while still being able to effectively use them in other rules/grammars?
qi::_a
and qi::_b
represent the first and second local variables for a rule. These variables are only available if you add qi::locals<long double, long double>
as a template parameter in the declaration of rule r
(and in this case also to qi::grammar...
since the start rule passed to the constructor of the grammar needs to be compatible with the grammar, ie have the same template parameters).
Below you can see another alternative without the need for the local variables:
// grammar for complex numbers of the form a+bi
template <typename Iterator>
struct Complex : qi::grammar<Iterator, std::complex<long double>()>
{
qi::rule<Iterator, std::complex<long double>()> r;
Real<Iterator> real;
Complex() : Complex::base_type(r)
{
r = (
real >> (qi::lit("+") | qi::lit("-"))
>> real >> -qi::lit("*") >> qi::lit("i")
)
[
qi::_val = phx::construct<std::complex<long double> >(qi::_1, qi::_2)
];
}
};
In this case the semantic action is attached to the whole parser sequence and we can get the attributes we need with the _N placeholders. Here, qi::_1 refers to the attribute matched by the first Real parser, and qi::_2 to the second one.
Using any of the alternatives we can then use those grammars normally:
//using complex_parser, real_parser, complex_val and real_val declared in your code
std::cout << "Parsing 'variable=3+4i-2'" << std::endl;
toParse = "variable=3+4i-2";
beginIt = toParse.begin();
endIt = toParse.end();
std::string identifier;
r = qi::parse(beginIt, endIt, *qi::char_("a-z") >> '=' >> complex_parser >> '-' >> real_parser, identifier, complex_val, real_val);
if(r && beginIt == endIt)
std::cout << "Successful parse: " << identifier << complex_val.real() << " " << complex_val.imag() << " " << real_val << std::endl;
else
std::cout << "Could not parse" << std::endl;