Search code examples
c++grammardefault-valueboost-spirit-qiboost-fusion

Boost.Spirit.Qi grammar for default values in Boost.Fusion adapt struct


struct coordinate {
  int x;
  int y;
  int z;
};

BOOST_FUSION_ADAPT_STRUCT(
  coordinate,
  (int, x)
  (int, y)
  (int, z)
)

template <typename Iterator>
struct coordinate_grammar : qi::grammar<Iterator, coordinate(), ascii::space_type>
{
  coordinate_grammar()
  :
    coordinate_grammar::base_type(start),
    optional_x(-(qi::int_ >> ':')),
    optional_z(-('@' >> qi::int_)),
    start(optional_x >> qi::int_ >> optional_z)
  {
  }

  qi::rule<Iterator, int, ascii::space_type> optional_x;
  qi::rule<Iterator, int, ascii::space_type> optional_z;
  qi::rule<Iterator, coordinate(), ascii::space_type> start;
};

I would like to set 0 as the default value of x, and 1000 as the default value of z.

Input --> Desired Output:

  • 200:400@300 --> [200,400,300]
  • 200:400 --> [200,400,1000]
  • 400@300 --> [0,400,300]
  • 400 --> [0,400,1000]

How do I need to modify the rule syntax to provide a default value when a value has not been specified for the optional (optional_x and optional_z) parsers?


Solution

  • The trick is to reconsider the notion of "optional"...

    optional_x(-(qi::int_ >> ':')),
    optional_z(-('@' >> qi::int_)),
    

    As we know, the - prefix indicates this rule can be specified 0 or 1 times, thus making it optional.

    However, we don't actually want to have an "optional" value. We want either the value that has been parsed OR a default value. This is not optional, it is quite simply one or the other.

    By modifying the rule syntax, when can better describe our intent.

    optional_x((qi::int_ >> ':') | qi::attr(0)),
    optional_z(('@' >> qi::int_) | qi::attr(1000)),
    

    Now when the option is processed, if you have not provided a value for x or z, then a value will be provided on your behalf by qi:attr(...).