4

I am trying to write a parser, which parses either a int32_t or a double. As a first try I wrote this parser:

const auto int_or_double = boost::spirit::x3::int32 | boost::spirit::x3::double_;

which I expect to get back a boost::variant<int32_t, double> the parser succeed to parse ints like 12, 100, -42, 7 but it fails to parse doubles like 13.243, 42.7, 12.0 -10000.3

here is a live demo

Why does this parser fail on doubles?

Exagon
  • 4,798
  • 6
  • 25
  • 53

2 Answers2

6

Your problem is very similar to this question.

When the integer parser occurs first in your grammar, it is preferred. For the input "12.9" the parser will parse the integer part of "12.9 which is 12 and will stop at the .. live example

You have to reverse the order so the double parser is preferred over the integer one:

const auto double_or_int =  boost::spirit::x3::double_ | boost::spirit::x3::int32;

This will now work for "12.9": live example

However, since a double parser also parses an integer, you will always get a double, even if the input is "12": live example

In order to prevent this, you need a strict double parser:

boost::spirit::x3::real_parser<double, boost::spirit::x3::strict_real_policies<double> > const double_ = {};

live example

Community
  • 1
  • 1
m.s.
  • 16,063
  • 7
  • 53
  • 88
1

I didn't know about strict_real_policies either, that sounds handy.

I worked around this in a more direct way once like so:

(qi::int_ >> !lit('.') >> !lit('e') >> !lit('E')) | qi::float_

If you have a look at the flow chart on http://www.json.org/ you can see that those three characters cover all the legal ways that a number could be caused to parse as a float. (In my particular problem.)

Chris Beck
  • 15,614
  • 4
  • 51
  • 87
  • 3
    That approach will usually work, but in a case like [this one](http://coliru.stacked-crooked.com/a/07be3ea0e2824259) it would fail. If you know that the integer part will never be missing, I think that `(int_ >> !char_(".eE")) | float_` should be equivalent and (slightly) less noisy. – llonesmiz Jun 19 '16 at 15:07
  • 3
    That's forgetting about skippers, so it should likely become `lexeme[int_ >> !char_(".eE")] | double_` – sehe Jun 19 '16 at 19:27
  • @sehe: In the grammar I was working in, I disabled all skippers. The full rule was actually `non_nil_value = true_ | false_ | (qi::int_ >> !lit('.') >> !lit('e') >> !lit('E')) | qi::float_ | tstring_ | lua_string_ | table;`. But yeah in general I guess `lexeme` should be involved. – Chris Beck Jun 19 '16 at 19:29