Search code examples
c++boostboost-spirit-x3

Attribute propagation to nested map


I want to parse the following (first Column is Identifier, second Column (date) is unique for each Identifier followed by a tuple of float's):

Max,2016-02-01,1.0,2.0,3.0
Max,2016-02-02,1.0,2.0,3.0
Rob,2016-02-01,1.0,2.0,3.0
Max,2016-02-03,1.0,2.0,3.0

my favorite structure would be

using ValueType = std::tuple<float, float, float>;
using ValueMap  = std::map<std::time_t, ValueType>;
using DataType  = std::unordered_map<std::string, ValueMap>;

Is this possible to create a valid grammar with attribute propagation (without semantic actions and/or later copy to this structure ) ?

The parsing grammar could look like:

namespace grammar
{
        using namespace x3;

        auto str2date = [](auto& ctx)
        {
                int y,m,d;
                auto tub = std::tie(y,m,d);
                fusion::copy(_attr(ctx), tub);

                std::tm t{};
                t.tm_year = y - 1900;
                t.tm_mon  = m - 1;
                t.tm_mday = d;

                auto time = std::mktime(&t);
                _val(ctx) = time;
                _pass(ctx) = time > 0;
        };

        auto date_  = rule<struct date_, std::time_t>{"date"}
                                = (int_ >> '-' >> int_ >> int_)[str2date];

        auto values_= rule<struct values_, ValueType>{"values"}
                                = float_ >> ',' >> float_ >> ',' >> float_;

        auto line_ = rule<struct line_, std::pair<std::time_t, ValueType>>{"line"}
                                = date_ >> ',' >> values_;

        auto start_ = rule<struct start_, DataType>{"start"}
                                = (+~char_(',') >> ',' >> line_) % eol >> (eol | eoi); 

};

not working live example at coliru


Solution

  • I made a working example:

    http://coliru.stacked-crooked.com/a/70ba89f254467f9e

    I've used the natural attributes of x3, along with fusion struct mapping to make it easy to parse into an intermediary ast::Row.

    This way, there's no need to right rule<> everywhere; only once. x3 does the work of getting it into the intermediary form, then it's easy for us to write the 'store()' method to get the data into the final destination.

    We only parse one row at a time into the intermediary form, then store and destroy that temporary varable. That way we don't use up a tonne of memory for the intermediary stage.