2

This question in strictly related to boost-spirit-x3-parse-into-structs

I have this grammar

     #include <iostream>
 //#define BOOST_SPIRIT_X3_DEBUG
 #include <boost/spirit/home/x3.hpp>
 #include <boost/fusion/include/adapt_struct.hpp>
 #include <boost/fusion/include/io.hpp>

 struct sectionInfo
 {
     std::string name;
    int number = 0;
    float pitch = 0.0f;
    int visible = 0;
    float minCutsTblSize = 0.0f;
    //technology section attributes
     float gridResolution = 0.0f;
     float lengthPrecision = 0.0f;
 };

 const char START_SECTION = '{';
 const char END_SECTION = '}';
 const char QUOTE = '"';
 const char EQUALS = '=';
 const char* LAYER_SECTION = "Layer";
 const char* TECHNOLOGY_SECTION = "Technology";
 const char* NUMBER_ATTR = "layerNumber";
 const char* VISIBLE_ATTR = "visible";
 const char* COLOR_ATTR = "color";
 const char* PITCH_ATTR = "pitch";
 const char* MIN_CUTS_TBL_SIZE_ATTR = "minCutsTblSize";
 const char* GRID_RESOLUTION_ATTR = "gridResolution";
 const char* LENGTH_PRECISION_ATTR = "lengthPrecision";

 namespace Parser {
     namespace x3 = boost::spirit::x3;

     namespace detail {
         template <typename T> auto propagate(T member) {
             return [=](auto& ctx) { x3::traits::move_to(x3::_attr(ctx), x3::_val(ctx).*member); };
         }

         template <typename T = sectionInfo, typename P>
         auto rule(const char* debug, P p) { return x3::rule<struct _, T> {debug} = x3::skip(x3::space)[p]; };

         auto quoted = rule<std::string>("quoted", x3::lexeme[QUOTE >> +(x3::char_ - QUOTE) >> QUOTE]);

         template <typename T> auto make_member_parser(bool T::* const member) { return x3::bool_[propagate(member)]; }
         template <typename T> auto make_member_parser(int T::* const member) { return x3::int_[propagate(member)]; }
         template <typename T> auto make_member_parser(double T::* const member) { return x3::double_[propagate(member)]; }
         template <typename T> auto make_member_parser(float T::* const member) { return x3::double_[propagate(member)]; }
         template <typename T> auto make_member_parser(std::string T::* const member) { return quoted[propagate(member)]; }

         auto property = [](auto label, auto member) {
             return x3::as_parser(label) >> EQUALS >> make_member_parser(member);
         };
     }

     using detail::rule;
     using detail::propagate;
     using detail::property;
     using detail::quoted;

     auto number = property(NUMBER_ATTR, &sectionInfo::number);
     auto visible = property(VISIBLE_ATTR, &sectionInfo::visible);
     auto pitch = property(PITCH_ATTR, &sectionInfo::pitch);
     auto minCutsTblSize = property(MIN_CUTS_TBL_SIZE_ATTR, &sectionInfo::minCutsTblSize);
     auto lengthPrecision = property(LENGTH_PRECISION_ATTR, &sectionInfo::lengthPrecision);
     auto gridResolution = property(GRID_RESOLUTION_ATTR, &sectionInfo::gridResolution);

     auto skipLine = *(x3::char_ - x3::eol);

     x3::rule<struct sectionInfoId, sectionInfo> const layer = "layer";
     x3::rule<struct mainRuleId, std::vector<sectionInfo>> const mainRule = "mainRule";

     auto layer_def =
         LAYER_SECTION >> quoted[propagate(&sectionInfo::name)] >> START_SECTION >> x3::eol
         >> *( (number | visible | pitch | minCutsTblSize | lengthPrecision | gridResolution) >> +x3::eol )
         >> END_SECTION;

     auto skipper = x3::blank;

     auto mainRule_def = *(*x3::eol >> layer >> *x3::eol);

     BOOST_SPIRIT_DEFINE(layer, mainRule);
 }

 std::ostream& operator<<(std::ostream& os, const sectionInfo& s)
 {

         os
             << "name=" << " " << s.name << "\n"
             << "number=" << " " << s.number << "\n"
             << "visible=" << " " << s.visible << "\n"
             << "pitch=" << " " << s.pitch << "\n"
             << "minCutsTblSize=" << " " << s.minCutsTblSize << "\n"
             << "lengthPrecision=" << " " << s.lengthPrecision << "\n"
             << "gridResolution=" << " " << s.gridResolution << "\n\n";

     return os;
 }

 int main() {

     std::stringstream ss;

     ss 
        <<"\r\nLayer \"UBMB\" {\r\n"
        << "       layerNumber = 170\r\n"
        << "       pitch = 33.6\r\n"
        << "}\r\n"
        << "\r\n"
        << "Layer \"RV\" {\r\n"
        << "       gridResolution = 0.34\r\n"
        << "        minCutsTblSize = 22.7\r\n"
        << "       layerNumber = 85\r\n"
        << "        visible = 2\r\n"
        << "       pitch = 331\r\n"
        << "}\r\n"
        << " \r\n"
        << "Layer \"foffo\" {\r\n"
        << "       layerNumber = 125\r\n"
        << "       pitch = 0.005\r\n"
        << "        gridResolution = 21.7\r\n"
        << "        lengthPrecision = 0.15\r\n"
        << "}\r\n"
        << "\r\n";

     std::vector<sectionInfo> sections;
     auto sample = ss.str();
     auto f = sample.begin(), l = sample.end();
     bool ok = boost::spirit::x3::phrase_parse(
         f, l, 
         Parser::mainRule, 
         Parser::skipper,
         sections
     );


     if (ok && f==l)
     {
         std::cout << "\n\n Parsed successfully \n\n";
         for(auto& s : sections)
         {
             std::cout << s;
         }
     }
     else
         std::cout << "Parse failed\n";

 }

which successfully parses the input:

output is:

name= UBMB number= 170 visible= 0 pitch= 33.6 minCutsTblSize= 0 lengthPrecision= 0 gridResolution= 0

name= RV number= 85 visible= 2 pitch= 331 minCutsTblSize= 22.7 lengthPrecision= 0 gridResolution= 0.34

name= foffo number= 125 visible= 0 pitch= 0.005 minCutsTblSize= 0 lengthPrecision= 0.15 gridResolution= 21.7

The problem arises because I need to skip some lines, i.e. the lines with properties not of interest (not defined in my grammar)

edit: For example, there may be a property dummy = "foo" which I want to skip.

To achieve this, the layer rule

auto layer_def = LAYER_SECTION >> quoted[propagate(&sectionInfo::name)] >> START_SECTION >> x3::eol
             >> *( (number | visible | pitch | minCutsTblSize | lengthPrecision | gridResolution ) >> +x3::eol )
             >> END_SECTION;

becomes

auto layer_def = LAYER_SECTION >> quoted[propagate(&sectionInfo::name)] >> START_SECTION >> x3::eol
             >> *( (number | visible | pitch | minCutsTblSize | lengthPrecision | gridResolution | skipLine) >> +x3::eol )
             >> END_SECTION;

The parser succeeds, but the output is now

name= UBMB number= 125 visible= 2 pitch= 0.005 minCutsTblSize= 22.7 lengthPrecision= 0.15 gridResolution= 21.7

which is wrong (only one section, properties taken here and there...)

It is evident the problem resides in the skipLine rule

auto skipLine = *(x3::char_ - x3::eol);

and I can't figure out why. I thought it was obvious that the rule *(char - eol) >> eol would match any line, but I guess it isn't..

Any clues?

Filippo
  • 361
  • 1
  • 5
  • 16
  • I noticed the - yet unused - TECHNOLOGY_SECTION. Can you tell us the end-goal, of what you're actually trying to parse? I think you can do all this a lot simpler. And match your domain better. – sehe Aug 18 '17 at 13:15
  • @sehe I really can't :) btw thank you very much for your contribution, you can't imagine how helpful you've been. – Filippo Aug 18 '17 at 13:36

1 Answers1

1

Firstly, I'd simplify skipping. The skipper doesn't belong in the call-site because it's important to the grammar. Encapsulate it into the mainRule, and simplify:

auto mainRule_def = x3::skip(x3::blank) [ -layer % x3::eol ];

The -layer expression is a trick that accepts empty lines. Now using the parser becomes:

bool ok = boost::spirit::x3::parse(f, l, Parser::mainRule, sections);

Next: skipLine eats '}' as well, making everything fall apart: everything after that is taken as (invalid) properties for the same layer, and finally there is no END_SECTION so the grammar fails to match.

Simply:

auto skipLine = *(x3::char_ - x3::eol - END_SECTION);

Pro Tip:

When in doubt, debug: Live On Coliru

// debug it
auto skipLine = x3::rule<struct skipLine_> {"skipLine"} = *(x3::char_ - x3::eol/* - END_SECTION*/);

Full Demo

With some minor simplifications

Live On Coliru

#include <iostream>
#define BOOST_SPIRIT_X3_DEBUG
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/x3.hpp>

struct sectionInfo {
    std::string name;
    int number            = 0;
    float pitch           = 0.0f;
    int visible           = 0;
    float minCutsTblSize  = 0.0f;
    // technology section attributes
    float gridResolution  = 0.0f;
    float lengthPrecision = 0.0f;
};

char const  START_SECTION          = '{';
char const  END_SECTION            = '}';
char const  QUOTE                  = '"';
char const  EQUALS                 = '=';
char const* LAYER_SECTION          = "Layer";
char const* TECHNOLOGY_SECTION     = "Technology";
char const* NUMBER_ATTR            = "layerNumber";
char const* VISIBLE_ATTR           = "visible";
char const* COLOR_ATTR             = "color";
char const* PITCH_ATTR             = "pitch";
char const* MIN_CUTS_TBL_SIZE_ATTR = "minCutsTblSize";
char const* GRID_RESOLUTION_ATTR   = "gridResolution";
char const* LENGTH_PRECISION_ATTR  = "lengthPrecision";

namespace Parser {
    namespace x3 = boost::spirit::x3;

    namespace detail {
        template <typename T> auto propagate(T member) {
            return [=](auto &ctx) { x3::traits::move_to(x3::_attr(ctx), x3::_val(ctx).*member); };
        }

        template <typename T = sectionInfo, typename P> auto rule(const char *debug, P p) {
            return x3::rule<struct _, T>{ debug } = x3::skip(x3::space)[p];
        };

        auto quoted = rule<std::string>("quoted", x3::lexeme[QUOTE >> +(x3::char_ - QUOTE) >> QUOTE]);

#define MMP_(T, p) template <typename U> auto make_member_parser(T U::*const member) { return (p)[propagate(member)]; }
        MMP_(bool,   x3::bool_);
        MMP_(int,    x3::int_);
        MMP_(double, x3::double_);
        MMP_(float,  x3::double_);
        MMP_(std::string, quoted);
#undef MMP_

        auto property = [](auto label, auto member) { return x3::as_parser(label) >> EQUALS >> make_member_parser(member); };
    }

    using detail::rule;
    using detail::propagate;
    using detail::property;
    using detail::quoted;

    auto number          = property(NUMBER_ATTR,            &sectionInfo::number);
    auto visible         = property(VISIBLE_ATTR,           &sectionInfo::visible);
    auto pitch           = property(PITCH_ATTR,             &sectionInfo::pitch);
    auto minCutsTblSize  = property(MIN_CUTS_TBL_SIZE_ATTR, &sectionInfo::minCutsTblSize);
    auto lengthPrecision = property(LENGTH_PRECISION_ATTR,  &sectionInfo::lengthPrecision);
    auto gridResolution  = property(GRID_RESOLUTION_ATTR,   &sectionInfo::gridResolution);

    auto skipLine = *(x3::char_ - x3::eol - END_SECTION);

    x3::rule<struct sectionInfoId, sectionInfo> const layer = "layer";
    x3::rule<struct mainRuleId, std::vector<sectionInfo> > const mainRule = "mainRule";

    auto layer_def = 
        LAYER_SECTION >> quoted[propagate(&sectionInfo::name)]
        >> START_SECTION >> x3::eol
        >> *((number | visible | pitch | minCutsTblSize | lengthPrecision | gridResolution | skipLine) >> +x3::eol)
        >> END_SECTION
        ;

    auto mainRule_def = x3::skip(x3::blank) [ -layer % x3::eol ];

    BOOST_SPIRIT_DEFINE(layer, mainRule);
}

std::ostream &operator<<(std::ostream &os, const sectionInfo &s) {
    return os 
      << "name="            << " " << s.name            << "\n"
      << "number="          << " " << s.number          << "\n"
      << "visible="         << " " << s.visible         << "\n"
      << "pitch="           << " " << s.pitch           << "\n"
      << "minCutsTblSize="  << " " << s.minCutsTblSize  << "\n"
      << "lengthPrecision=" << " " << s.lengthPrecision << "\n"
      << "gridResolution="  << " " << s.gridResolution  << "\n\n";
}

int main() {

    std::string const sample = 
       "\r\nLayer \"UBMB\" {\r\n"
       "       layerNumber = 170\r\n"
       "       pitch = 33.6\r\n"
       "}\r\n"
       "\r\n"
       "Layer \"RV\" {\r\n"
       "       gridResolution = 0.34\r\n"
       "        minCutsTblSize = 22.7\r\n"
       "       layerNumber = 85\r\n"
       "        visible = 2\r\n"
       "       pitch = 331\r\n"
       "}\r\n"
       " \r\n"
       "Layer \"foffo\" {\r\n"
       "       layerNumber = 125\r\n"
       "       pitch = 0.005\r\n"
       "        gridResolution = 21.7\r\n"
       "        lengthPrecision = 0.15\r\n"
       "}\r\n"
       "\r\n";

    std::vector<sectionInfo> sections;
    {
        auto f = sample.begin(), l = sample.end();
        bool ok = boost::spirit::x3::parse(f, l, Parser::mainRule, sections);

        if (ok && f == l) {
            std::cout << "\n\n Parsed successfully \n\n";
            for (auto &s : sections) {
                std::cout << s;
            }
        } else
            std::cout << "Parse failed\n";
    }
}

Prints

 Parsed successfully 

name= UBMB
number= 170
visible= 0
pitch= 33.6
minCutsTblSize= 0
lengthPrecision= 0
gridResolution= 0

name= RV
number= 85
visible= 2
pitch= 331
minCutsTblSize= 22.7
lengthPrecision= 0
gridResolution= 0.34

name= foffo
number= 125
visible= 0
pitch= 0.005
minCutsTblSize= 0
lengthPrecision= 0.15
gridResolution= 21.7
sehe
  • 374,641
  • 47
  • 450
  • 633