Question

I would like to be able to parse a Number, to store its original source and to track its position in the source preserving it in the structure itself.

This is what I have so far:

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/home/support/iterators/line_pos_iterator.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>

#include <iostream>
#include <iomanip>
#include <ios>
#include <string>
#include <complex>

#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>

struct Position
{
    Position()
        : line(-1)
    {
    }

    size_t line;
};

struct Number : public Position
{
    Number()
        : Position()
        , value(-1)
        , source()
    {
    }

    unsigned    value;
    std::string source;
};

using namespace boost::spirit;

BOOST_FUSION_ADAPT_STRUCT(Number,
                            (unsigned,    value)
                            (std::string, source)
                            (size_t,      line)
                          );

template <typename Iterator>
struct source_hex : qi::grammar<Iterator, Number()>
{
    source_hex() : source_hex::base_type(start)
    {
        using qi::eps;
        using qi::hex;
        using qi::lit;
        using qi::raw;
        using qi::_val;
        using qi::_1;
        using ascii::char_;

        namespace phx = boost::phoenix;
        using phx::at_c;
        using phx::begin;
        using phx::end;
        using phx::construct;

        start = raw[   (lit("0x") | lit("0X"))
                     >> hex [at_c<0>(_val) = _1]
                   ][at_c<2>(_val) = get_line(begin(_1))]
                    [at_c<1>(_val) = construct<std::string>(begin(_1), end(_1))]

        ;
    }

    qi::rule<Iterator, Number()> start;
};

and the test code is:

typedef line_pos_iterator<std::string::const_iterator> Iterator;
source_hex<Iterator> g;
Iterator iter(str.begin());
Iterator end(str.end());

Number number;
bool r = parse(iter, end, g, number);
if (r && iter == end) {
    std::cout << number.line << ": 0x" << std::setw(8) << std::setfill('0') << std::hex << number.value << " // " << number.source << "\n";
} else
    std::cout << "Parsing failed\n";

what I am not getting is why the iterator on line:

[at_c<2>(_val) = get_line(begin(_1))]

is not a line_pos_iterator even this is the one I am using for the parser. I will appreciate explanation as well as ideas how to solve the problem - in whatever way.

Was it helpful?

Solution

Have a look at

#include <boost/spirit/repository/include/qi_iter_pos.hpp>

This defines a parser that directly exposes the position as an attribute. Let me add an example in a few minutes.

Edit I found it hard to shoe-horn iter_pos into your sample without "assuming" things and changing your data type layout. I'd very much favour this (I'd strive to lose the semantic actions all the way.). However, time's limited.

Here's a little helper that you can use to fix your problem:

struct get_line_f
{
    template <typename> struct result { typedef size_t type; };
    template <typename It> size_t operator()(It const& pos_iter) const
    {
        return get_line(pos_iter);
    }
};

^ The polymorphic actor, use as such:

    start = raw[ qi::no_case["0x"] >> hex [at_c<0>(_val) = _1] ]
               [ 
                   at_c<1>(_val) = construct<std::string>(begin(_1), end(_1)),
                   at_c<2>(_val) = get_line_(begin(_1)) 
               ]
    ;

    // with

boost::phoenix::function<get_line_f> get_line_;

Note I changed a few minor points.

Fully running demo with output: Live On Coliru

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top