约束现有的Boost.Spiritreal_parser(用一个策略)
Constraining the existing Boost.Spirit real_parser (with a policy)
我想解析一个浮点数,但不允许 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
部分这样做,它单独看起来 left/right 的点并且无法通信(...干净地),他们必须这样做,因为 总体 长度是相关的。
当时的想法是扩展 real_parser
(在 boost/spirit/home/qi/numeric/real.hpp
中)并 包装 parse
方法——但是这个 class 没有方法。 real_parser
旁边是 any_real_parser
结构, 有 parse
,但这两个结构似乎没有以任何明显的方式相互作用。
有没有办法轻松地注入我自己的 parse(),做一些预检查,然后调用 real parse (return boost::spirit::qi::any_real_parser<T, RealPolicy>::parse(...)
) 然后坚持给定的政策?写一个新的解析器将是不得已的方法,但我希望有更好的方法。
(使用 Boost 1.55,即 Spirit 2.5.2,C++11)
It seems I am so close, i.e. just a few changes to the double_ parser and I'd be done. This would probably be a lot more maintainable than adding a new grammar, since all the other parsing is done that way. – toting
更易于维护的是根本不编写另一个解析器。
您基本上想要解析一个浮点数(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_
的 "cooked" 值
_pass
是一个 Spirit 上下文标志,我们可以将其设置为 false 以使解析失败。
现在唯一剩下的就是把它们绑在一起。让我们制作 ::isnan
的延迟版本:
boost::phoenix::function<decltype(&::isnan)> isnan_(&::isnan);
我们准备好了。
测试程序
#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'
¹ 赋值必须在这里显式完成,因为我们使用语义动作,它们通常会抑制自动属性传播
我想解析一个浮点数,但不允许 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
部分这样做,它单独看起来 left/right 的点并且无法通信(...干净地),他们必须这样做,因为 总体 长度是相关的。
当时的想法是扩展 real_parser
(在 boost/spirit/home/qi/numeric/real.hpp
中)并 包装 parse
方法——但是这个 class 没有方法。 real_parser
旁边是 any_real_parser
结构, 有 parse
,但这两个结构似乎没有以任何明显的方式相互作用。
有没有办法轻松地注入我自己的 parse(),做一些预检查,然后调用 real parse (return boost::spirit::qi::any_real_parser<T, RealPolicy>::parse(...)
) 然后坚持给定的政策?写一个新的解析器将是不得已的方法,但我希望有更好的方法。
(使用 Boost 1.55,即 Spirit 2.5.2,C++11)
It seems I am so close, i.e. just a few changes to the double_ parser and I'd be done. This would probably be a lot more maintainable than adding a new grammar, since all the other parsing is done that way. – toting
更易于维护的是根本不编写另一个解析器。
您基本上想要解析一个浮点数(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_
的 "cooked" 值
_pass
是一个 Spirit 上下文标志,我们可以将其设置为 false 以使解析失败。
现在唯一剩下的就是把它们绑在一起。让我们制作 ::isnan
的延迟版本:
boost::phoenix::function<decltype(&::isnan)> isnan_(&::isnan);
我们准备好了。
测试程序
#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'
¹ 赋值必须在这里显式完成,因为我们使用语义动作,它们通常会抑制自动属性传播