-2

I try to learn how Boost.Spirit works. Unfortunately, the documentation is too laconic for me and I can't figure out, how it really works.

So, let's move to my problem. I try to write parser for std::complex type, that will work in the same way like built-in parsers, e.g. int_ or double_. I want it to parse complex number in different forms, like 1.2+2i, 3-i*4, 5*i, etc. And, to be honest, I stuck and don't know, what to do. I have tried this approach:

typedef std::complex<double> Complex;

struct complex_parser : qi::grammar<Iterator, Complex(), ascii::space_type>
{
    complex_parser() : complex_parser::base_type(value)
    {
        real = (
            double_[_val = _1]
        );

        imaginary = (
            ( double_[_val = Complex(0.0, _1)] >> -(lit('*')) >> 'i' )
          | ( 'i' >> -(lit('*')) >> double_[_val = Complex(0.0, _1)] )
        );

        value = (
            imaginary[_val = _1]
          | real[_val = _1] >> -('+' >> imaginary[_val += _1])
          | real[_val = _1] >> -('-' >> imaginary[_val -= _1])
        );
    }

    qi::rule<Iterator, Complex(), ascii::space_type> value;
    qi::rule<Iterator, double(), ascii::space_type> real;
    qi::rule<Iterator, double(), ascii::space_type> imaginary;
};

But it does not work. Doesn't even compile, because when imaginary is parsed, placeholder _1 is not double type:

error C2665: 'std::complex::complex' : none of the 6 overloads could convert all the argument types [...] while trying to match the argument list '(double, const boost::spirit::_1_type)'

But why? I gave in imaginary rule (as well as in real) double() parameter, instead of Complex(). Shouldn't it make placeholder be of double-equivalent type? If not, then why even set the type in rules? And how to use them properly?

Archie
  • 6,391
  • 4
  • 36
  • 44

1 Answers1

1

You can use boost::phoenix::construct.

#define BOOST_SPIRIT_USE_PHOENIX_V3

#include <complex>
#include <boost/spirit/include/qi.hpp>
#include <boost/phoenix.hpp>

namespace qi = boost::spirit::qi;
using namespace boost::spirit::qi;
namespace phx = boost::phoenix;

typedef std::complex<double> Complex;
typedef std::string::iterator Iterator;

struct complex_parser : qi::grammar<Iterator, Complex(), ascii::space_type>
{
    complex_parser() : complex_parser::base_type(value)
    {
        real = (
            double_[_val = _1]
        );

        imaginary = (
            ( double_[_val = phx::construct<Complex>(0.0, _1)] >> -(lit('*')) >> 'i' )
          | ( 'i' >> -(lit('*')) >> double_[_val = phx::construct<Complex>(0.0, _1)] )
        );

        value = (
            imaginary[_val = _1]
          | real[_val = _1] >> -('+' >> imaginary[_val += _1])
          | real[_val = _1] >> -('-' >> imaginary[_val -= _1])
        );
    }

    qi::rule<Iterator, Complex(), ascii::space_type> value;
    qi::rule<Iterator, double(), ascii::space_type> real;
    qi::rule<Iterator, Complex(), ascii::space_type> imaginary;
};
Akira Takahashi
  • 2,912
  • 22
  • 107
  • So let me understand correctly: the thing is about lazy evaluation, right? That is, without lazy evaluation by `phoenix::construct`, compiler doesn't know what type the `_1` placeholder is? – Archie Jan 18 '12 at 11:40
  • _1 is a special value and type. _1 is replace any types of value. _1 is knows nothing about the target type. (sorry, my english is bad.) – Akira Takahashi Jan 18 '12 at 14:34
  • 1
    To gain a good understanding of the internals of boost::spirit and boost::phoenix have a look at this excellent series. http://cpp-next.com/archive/2010/08/expressive-c-introduction/ – mark Jan 18 '12 at 19:40