约束现有的 Boost.Spirit real_parser (with a policy)

时间:2023-03-22
本文介绍了约束现有的 Boost.Spirit real_parser (with a policy)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想解析一个浮点数,但不允许 NaN 值,所以我生成了一个继承自默认策略的策略,并用它创建了一个 real_parser:

I want to parse a float, but not allow NaN values, so I generate a policy which inherits from the default policy and create a real_parser with it:

// using boost::spirit::qi::{real_parser,real_policies,
//                           phrase_parse,double_,char_};

template <typename T>
struct no_nan_policy : real_policies<T>
{
    template <typename I, typename A>
    static bool
    parse_nan(I&, I const&, A&) {
          return false;
    }    
};

real_parser<double, no_nan_policy<double> > no_nan;

// then I can use no_nan to parse, as in the following grammar
bool ok = phrase_parse(first, last, 
   no_nan[ref(valA) = _1] >> char_('@') >> double_[ref(b) = _1],
space);

但现在我要保证用no_nan解析的字符串的总长度不超过4,即1.23"或.123"甚至2.e6"或inf"可以,3.2323"不行,nan"也不行.我不能在策略的 parse_n/parse_frac_n 部分这样做,它分别看起来点的左/右并且无法通信(...干净地),因为整体长度是相关的.

But now I also want to ensure that the overall length of the string parsed with no_nan does not exceed 4, i.e. "1.23" or ".123" or even "2.e6" or "inf" is ok, "3.2323" is not, nor is "nan". I can not do that in the parse_n/parse_frac_n section of the policy, which separately looks left/right of the dot and can not communicate (...cleanly), which they would have to since the overall length is relevant.

当时的想法是扩展real_parser(在boost/spirit/home/qi/numeric/real.hpp中)并包装parse method -- 但是这个类没有方法.real_parser 旁边是 any_real_parser 结构,确实parse,但这两个结构似乎没有交互任何明显的方式.

The idea then was to extend real_parser (in boost/spirit/home/qi/numeric/real.hpp) and wrap the parse method -- but this class has no methods. Next to real_parser is the any_real_parser struct which does have parse, but these two structs do not seem to interact in any obvious way.

有没有办法轻松注入我自己的parse(),做一些预检查,然后调用real parse(return boost::spirit::qi::any_real_parser<T, RealPolicy>::parse(...)) 然后遵守给定的策略?编写新的解析器将是最后的手段,但我希望有更好的方法.

Is there a way to easily inject my own parse(), do some pre-checks, and then call the real parse (return boost::spirit::qi::any_real_parser<T, RealPolicy>::parse(...)) which then adheres to the given policies? Writing a new parser would be a last-resort method, but I hope there is a better way.

(使用 Boost 1.55,即 Spirit 2.5.2,使用 C++11)

(Using Boost 1.55, i.e. Spirit 2.5.2, with C++11)

推荐答案

看来我已经很接近了,也就是说,只需对 double_ 解析器进行一些更改,我就大功告成了.这可能比添加新语法更易于维护,因为所有其他解析都是以这种方式完成的.– 携带 7 小时前

更易于维护的是根本不编写另一个解析器.

Even more maintainable would be to not write another parser at all.

您基本上想解析一个浮点数(Spirit 已经帮您解决了),但之后要进行一些验证.我会在语义操作中进行验证:

You basically want to parse a floating point numbers (Spirit has got you covered) but apply some validations afterward. I'd do the validations in a semantic action:

raw [ double_ [_val = _1] ] [ _pass = !isnan_(_val) && px::size(_1)<=4 ]

就是这样.

解剖学:

  • double_ [_val = _1] 像往常一样解析双精度值并将其分配给暴露的属性¹
  • raw [ parser ] 匹配封闭的 parser 但是 将原始源迭代器范围作为属性公开
  • [ _pass = !isnan_(_val) &&px::size(_1)<=4 ] - 业务部分!

  • double_ [_val = _1] parses a double and assigns it to the exposed attribute as usual¹
  • raw [ parser ] matches the enclosed parser but exposes the raw source iterator range as an attribute
  • [ _pass = !isnan_(_val) && px::size(_1)<=4 ] - the business part!

此语义操作附加到 raw[] 解析器.因此

This semantic action attaches to the raw[] parser. Hence

  • _1 现在指的是已经解析了 double_
  • 的原始迭代器范围
  • _val 已经包含成功匹配 double_
  • 的cooked"值
  • _pass 是 Spirit 上下文标志,我们可以将其设置为 false 以使解析失败.
  • _1 now refers to the raw iterator range that already parsed the double_
  • _val already contains the "cooked" value of a successful match of double_
  • _pass is a Spirit context flag that we can set to false to make parsing fail.

现在唯一剩下的就是将它们联系在一起.让我们制作一个延迟版本的 ::isnan:

Now the only thing left is to tie it all together. Let's make a deferred version of ::isnan:

boost::phoenix::function<decltype(&::isnan)> isnan_(&::isnan);

我们可以出发了.

生活在 Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <cmath>
#include <iostream>

int main ()
{
    using It = std::string::const_iterator;

    auto my_fpnumber = [] { // TODO encapsulate in a grammar struct
        using namespace boost::spirit::qi;
        using boost::phoenix::size;

        static boost::phoenix::function<decltype(&::isnan)> isnan_(&::isnan);

        return rule<It, double()> (
                raw [ double_ [_val = _1] ] [ _pass = !isnan_(_val) && size(_1)<=4 ]
            );
    }();

    for (std::string const s: { "1.23", ".123", "2.e6", "inf", "3.2323", "nan" })
    {
        It f = s.begin(), l = s.end();

        double result;
        if (parse(f, l, my_fpnumber, result))
            std::cout << "Parse success:  '" << s << "' -> " << result << "
";
        else
            std::cout << "Parse rejected: '" << s << "' at '" << std::string(f,l) << "'
";
    }
}

印刷品

Parse success:  '1.23' -> 1.23
Parse success:  '.123' -> 0.123
Parse success:  '2.e6' -> 2e+06
Parse success:  'inf' -> inf
Parse rejected: '3.2323' at '3.2323'
Parse rejected: 'nan' at 'nan'

<小时>

¹ 必须在此处显式完成赋值,因为我们使用语义操作并且它们通常会抑制自动属性传播


¹ The assignment has to be done explicitly here because we use semantic actions and they normally suppress automatic attribute propagation

这篇关于约束现有的 Boost.Spirit real_parser (with a policy)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持html5模板网!