2

i'm parsing a simple configuration file format consisting of name-value pairs:

an_int_option 42;
a_string_option "foo";
an_identifier_option somevalue;

i have a basic rule to parse each item:

  typedef boost::variant<int, double, std::string> config_value;
  struct config_item {
    std::string name;
    config_value value;
  };

  qi::rule<Iterator, config_value(), ascii::space_type> value;
  qi::rule<Iterator, config_item(), ascii::space_type> item;

  value = 
      identifier 
    | qstring
    | my_double 
    | qi::int_
    ;
  item = 
       identifier[at_c<0>(_val) = _1]
    >> value[at_c<1>(_val) = _1]
    >> ';'
    ;

this works fine and gives me the config_value for each item.

now i want to store the location in the input file where each value was found, so that if the user configures an invalid option, i can report the file line and column number where the error happened.

the best option i've found so far is raw[], which would let me do something like:

  item = 
       raw[ identifier ] [do_something_with_iterators(_1)]
    >> raw[ value ]      [do_something_with_iterators(_1)]
    >> ';'
    ;

... but since raw[] discards the attribute, my do_something_with_iterators now has to manually parse the value like in old-style Spirit - which seems like a lot of unnecessary work when i already have the parsed value right there.

unixwitch
  • 23
  • 1
  • 2

1 Answers1

1

You can use qi::raw[] to get the source iterator pair spanning a match.

There's a convenient helper iter_pos in Qi Repository that you can use to directly get a source iterator without using qi::raw[].

Also, with some semantic action trickery you can get both:

raw[ identifier [ do_something_with_attribute(_1) ] ]
   [do_something_with_iterators(_1)]

In fact,

raw[ identifier [ _val = _1 ] ] [do_something_with_iterators(_1)]

would be close to "natural behaviour".

Extra Mile

To get file name/line/column values you can either do some iterator arithmetics or use the line_pos_iterator adaptor:

#include <boost/spirit/include/support_line_pos_iterator.hpp>

This has some accessor functions that help with line number/column tracking. You can probably find a few answers of mine on here with examples.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • `iter_pos` and `line_pos_iterator` are exactly what i was looking for, thanks! out of interest, in `raw[ identifier [ _val = _1 ] ] [do_something_with_iterators(_1)]` - how would you access the inner value of the `identifier`? this was my first attempt at doing this with `raw[]`, but from what i could see, it completely discards its value. – unixwitch Feb 06 '21 at 22:10
  • Erm, that's precisely what I added `[ _val = _1 ]` for,. See examples https://stackoverflow.com/questions/8358975/cross-platform-way-to-get-line-number-of-an-ini-file-where-given-option-was-foun/8365427#8365427 or https://stackoverflow.com/questions/30375750/constraining-the-existing-boost-spirit-real-parser-with-a-policy/30385909#30385909 or https://stackoverflow.com/questions/47354226/how-to-provider-user-with-autocomplete-suggestions-for-given-boostspirit-gramm/47383910#47383910 – sehe Feb 07 '21 at 00:39
  • or https://stackoverflow.com/questions/38877702/boostspirit-parsing-into-structure-and-reusing-parts-of-it/38884727#38884727 or https://stackoverflow.com/questions/19216119/trigger-warning-from-boost-spirit-parser/19240806#19240806 – sehe Feb 07 '21 at 00:39
  • In a nutshell, `identifier [ _val = _1 ]` copies the parsed attribute to your bound attribute. Then `raw [....] [ do_something_with(_1) ]` _additionally_ does something with the source iterator range that comprised the match. – sehe Feb 07 '21 at 00:40