2

I'm trying to parse this type of string

1.2e3ex
1.2e3 ex

And have set up

x3::float_ >> "ex"

Unfortunately, this fails to parse

1ex

Full example code:

#include <iostream>
#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;

const auto parser = x3::float_ >> "em";

int main()
{
  std::string input = "1em";
  auto first = input.begin();
  auto last = input.end();

  float value{};
  bool result = x3::phrase_parse(first, last, parser, x3::blank, value);

  if(result)
  {
    if(first == last)
      std::cout << "parse succesful: " << value << '\n';
    else
      std::cout << "incomplete parse: " << value << '\n';
  }
  else
    std::cout << "parse unsuccesful\n";
}

Available live on Coliru as well.

It seems I'd need to jump through some hoops,

struct non_scientific_float_policy : x3::real_policies<float>
{
  template <typename Iterator>
  static bool parse_exp(Iterator& first, Iterator const& last)
  {
    return false;
  }
};

const auto non_scientific_float = x3::real_parser<float, non_scientific_float_policy>{};

and provide an alternative:

const auto parser = non_scientific_float >> "em" | x3::float_ >> "em";

Is there no other way?

rubenvb
  • 74,642
  • 33
  • 187
  • 332
  • Can you provide an [mcve] for what the problem is? – Nicol Bolas Feb 01 '19 at 19:00
  • @Nicol the first coliru link is exactly that. The 'e' in "em" is greedily taken by the `float_` parser, and never given back. – rubenvb Feb 01 '19 at 19:03
  • 3
    "*the first coliru link is exactly that.*" MCVE go *into* your post, not in links. – Nicol Bolas Feb 01 '19 at 19:41
  • Apparently, when it's not a new user, the whole "be welcoming" dance goes right out the window ;) – sehe Feb 01 '19 at 21:31
  • @NicolBolas I'm just going to be blunt: there is no "rule"/suggestion/whatever I can find that specifies MCVE's should go _in_ the question body, so please don't start making up rules for the sake of argument. Having it in an editable, runnable form is a bonus you get for free... – rubenvb Feb 02 '19 at 10:51
  • 1
    @rubenvb: [Yes, there is](https://meta.stackoverflow.com/a/334461/734069) [such a rule.](https://meta.stackoverflow.com/q/269253/734069) – Nicol Bolas Feb 02 '19 at 14:29
  • 1
    @NicolBolas Two posts on meta a rule does not make. At least link to the bit in the help center I overlooked: "If it is possible to create a live example of the problem that you can link to (for example, on http://sqlfiddle.com/ or http://jsbin.com/) then do so - but also include the code in your question itself. Not everyone can access external sites, and the links may break over time." Anyhoo, all the glorious code is now available for your viewing pleasure. – rubenvb Feb 02 '19 at 17:04

2 Answers2

4

You can solve the issue by tuning the real policy parse_exp in such way that exponent detection must expect not only [eE] character but [eE][-+]?[0-9].

#include <iostream>
#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;

template <typename T>
struct alt_real_policies : x3::real_policies<T>
{
    template <typename Iterator>
    static bool parse_exp(Iterator& first, Iterator const& last)
    {
        Iterator save = first;
        if (x3::real_policies<T>::parse_exp(first, last)) {
            Iterator iter = first;
            if (x3::extract_int<x3::unused_type, 10, 1, 1>::call(iter, last, x3::unused))
                return true;
        }
        first = save;
        return false;
    }

};

const x3::real_parser<float, alt_real_policies<float>> altfloat;
const auto parser = altfloat >> "em";

int main()
{
    std::string input = "1em";
    auto first = input.begin();
    auto last = input.end();

    float value{};
    bool result = x3::phrase_parse(first, last, parser, x3::blank, value);

    if (result)
    {
        if (first == last)
            std::cout << "parse succesful: " << value << '\n';
        else
            std::cout << "incomplete parse: " << value << '\n';
    }
    else
        std::cout << "parse unsuccesful\n";
}

http://coliru.stacked-crooked.com/a/f60f334c960cb602

Nikita Kniazev
  • 3,728
  • 2
  • 16
  • 30
  • I like it. Would be even better if instead of `extract_int` it would reuse `parse_exp_n` (quoting from memory), I feel – sehe Feb 02 '19 at 21:44
  • That was also my idea, but `parse_exp_n` needs a real value; it also will parse all digits and return false if the number overflows int - this will lead to incorrectly ignored exponent. – Nikita Kniazev Feb 02 '19 at 23:39
0

You can use an alternative policy for non-greedy parsing of the exponent. The very simplest I can think of is:

Live On Coliru

#include <boost/spirit/home/x3.hpp>
#include <iostream>
namespace x3 = boost::spirit::x3;

template <typename T>
struct no_exponent : x3::real_policies<T> {
    template <typename It>
        static bool parse_exp(It, It) { return false; }
};

x3::real_parser<double, no_exponent<double> > noexp_;

const auto parser = (x3::float_ | noexp_) >> "em";

int main() {
    std::string input = "-1.67em";
    auto first = input.begin();
    auto last = input.end();

    float value{};
    bool result = x3::phrase_parse(first, last, parser, x3::blank, value);

    if (result) {
        if (first == last)
            std::cout << "parse succesful: " << value << '\n';
        else
            std::cout << "incomplete parse: " << value << '\n';
    } else
    {
        std::cout << "parse unsuccesful\n";
    }
}

Printing:

parse succesful: -1.67
sehe
  • 374,641
  • 47
  • 450
  • 633
  • @BenVoigt Oh. Yeah. That was well hidden. I'm happy to have not spent a lot of time. I could elaborate on the subtle points where my take is slightly better, but hey. Already at -1 so why bother. – sehe Feb 01 '19 at 21:54
  • Here's a +1 from me. Why is your take better :)? – rubenvb Feb 02 '19 at 10:49
  • Cheers. Two very subtle/mild points: the ordering with non-scientific first is slightly less generic (imagine if `"em"` was just "e" - it would mean that `3e7` woudn't be accepted or leave the `7` unparsed). So the ordering is slightly better. The other thing is prior knowledge: I've obvserved `real_parser` introduce unnecessary inaccuracies so using `real_parser` is my preferred method, even to parse into floats. – sehe Feb 02 '19 at 21:48