约束现有的Boost.Spirit real_parser(带策略)

时间:2015-05-21 13:51:08

标签: c++ parsing boost boost-spirit boost-spirit-qi

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

// 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部分执行此操作,该部分分别位于点的左侧/右侧且无法通信( ...干净地),因为整体长度是相关的,所以他们必须这样做。

接下来的想法是扩展real_parser(在boost/spirit/home/qi/numeric/real.hpp中)和包装parse方法 - 但这个类没有方法。 real_parser旁边的any_real_parser结构 具有parse,但这两个结构似乎没有任何明显的交互方式。

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

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

1 个答案:

答案 0 :(得分:2)

  

我似乎非常接近,即对double_ parser进行了一些更改,我就完成了。这可能比添加新语法更易于维护,因为所有其他解析都是以这种方式完成的。 - 携带7 hours ago

更可维护的是根本不写另一个解析器。

你基本上想要解析一个浮点数( Spirit已经涵盖了),但之后应用了一些验证。我会在语义操作中进行验证:

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

就是这样。

说明

解剖:

  • double_ [_val = _1]解析一个double并像往常一样将它分配给公开的属性¹
  • raw [ parser ]与随附的 parser 匹配,但会将原始源迭代器范围公开为属性
  • [ _pass = !isnan_(_val) && px::size(_1)<=4 ] - 业务部分!

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

    • _1现在引用已经解析double_
    • 的原始迭代器范围
    • _val已包含double_
    • 成功匹配的“熟”值
    • _pass是一个Spirit上下文标志,我们可以将其设置为false以使解析失败。

现在唯一剩下的就是将它们捆绑在一起。让我们制作::isnan的延迟版本:

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

我们很高兴。

测试计划

<强> Live On 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 << "\n";
        else
            std::cout << "Parse rejected: '" << s << "' at '" << std::string(f,l) << "'\n";
    }
}

打印

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'

¹必须在这里明确地完成赋值,因为我们使用语义动作,它们通常会抑制自动属性传播