使用Boost::Spirit::X3解析复数
Parse complex numbers with Boost::Spirit::X3
我想写一个Boost::Spirit::X3
解析器来解析具有以下可能输入格式的复数:
"(X+Yi)"
"Yj"
"X"
到目前为止,我最好的尝试是以下(在Coliru上打开(:
#include <complex>
#include <iostream>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/utility/error_reporting.hpp>
namespace x3 = boost::spirit::x3;
struct error_handler {
template <typename iterator_t, typename error_t, typename context_t>
auto on_error(iterator_t& /* iter */, const iterator_t& /* end */, const error_t& error,
const context_t& context) {
namespace x3 = boost::spirit::x3;
const auto& handler = x3::get<x3::error_handler_tag>(context).get();
handler(error.where(), "error: expecting: " + error.which());
return x3::error_handler_result::fail;
}
};
// -----------------------------------------------------------------------------
namespace ast {
template <typename T>
struct complex_number {
T real;
T imag;
operator std::complex<T>() {
return {real, imag};
}
};
} // namespace ast
BOOST_FUSION_ADAPT_STRUCT(ast::complex_number<double>, real, imag);
// -----------------------------------------------------------------------------
namespace parser {
const auto pure_imag_number = x3::attr(0.) > x3::double_ > x3::omit[x3::char_("ij")];
const auto pure_real_number = x3::double_ > x3::attr(0.);
struct complex_class : error_handler {};
const x3::rule<complex_class, ast::complex_number<double>> complex = "Complex number";
static const auto complex_def = ('(' > (x3::double_ > -(x3::double_ > x3::omit[x3::char_("ij")])) >> ')')
| pure_imag_number
| pure_real_number;
BOOST_SPIRIT_DEFINE(complex);
} // namespace parser
// =============================================================================
void parse(const std::string& str) {
using iterator_t = std::string::const_iterator;
auto iter = std::begin(str);
auto end = std::end(str);
boost::spirit::x3::error_handler<iterator_t> handler(iter, end, std::cerr);
const auto parser = boost::spirit::x3::with<boost::spirit::x3::error_handler_tag>(
std::ref(handler))[parser::complex];
std::complex<double> result{};
if (boost::spirit::x3::phrase_parse(iter, end, parser, x3::space, result) && iter == end) {
std::cout << "Parsing successful for:' " << str << "'n";
} else {
std::cout << "Parsing failed for:' " << str << "'n";
}
}
int main() {
for (const auto& str : {
"(1+2j)",
"(3+4.5j)",
"1.23j",
"42",
}) {
parse(str);
}
return 0;
}
当运行编译后的代码(使用GCC 12.1.1和Boost 1.79.0(时,它给出了以下结果:
Parsing successful for:' (1+2j)'
Parsing successful for:' (3+4.5j)'
Parsing successful for:' 1.23j'
In line 1:
error: expecting: N5boost6spirit2x314omit_directiveINS1_8char_setINS0_13char_encoding8standardEcEEEE
42
__^_
Parsing failed for:' 42'
我感到困惑的是,为什么在解析只有实数的字符串时,最后一个选项被认为是无效的
您已经发现,如果需要允许回溯,那么期望点太强了。
不过,请注意,您的语法有点有趣,因为在double_
解析器中只包含一个一元符号来分隔值。
这里有一个简化的测试,突出了一些边缘情况:
static const auto ij = x3::omit[x3::char_("ij")];
static const auto implied = x3::attr(0.);
static const auto complex =
x3::rule<struct complex_, ast::complex_number<double>>{"complex"} //
= ('(' >> x3::double_ >> ((x3::double_ >> ij) | implied) >> ')') //
| implied >> x3::double_ >> ij //
| x3::double_ >> implied;
通过完整的测试Live On Coliru打印
Parsing successful for: '(1+2j)' -> (1,2)
Parsing successful for: '(1 2j)' -> (1,2)
Parsing successful for: '(+1+2j)' -> (1,2)
Parsing successful for: '(+1-2j)' -> (1,-2)
Parsing successful for: '(-1-2j)' -> (-1,-2)
Parsing successful for: '(3+4.5j)' -> (3,4.5)
Parsing successful for: '1.23j' -> (0,1.23)
Parsing successful for: '42' -> (42,0)
Parsing successful for: 'inf' -> (inf,0)
Parsing successful for: '-infj' -> (0,-inf)
Parsing successful for: 'NaNj' -> (0,nan)
Parsing successful for: '(.0e9)' -> (0,0)
Parsing successful for: '(.0e-4)' -> (0,0)
Parsing successful for: '.0e-4i' -> (0,0)
Parsing successful for: '.0e-4j' -> (0,0)
Parsing successful for: '(3-0.e-4j)' -> (3,-0)
Parsing successful for: '(3-.0e-4j)' -> (3,-0)
请注意,在未加括号的版本中允许空白很容易导致问题(不明确的输入/令人惊讶的错误解析(。我建议你也许只想跳过括号内的空格:
static const auto complex =
x3::rule<struct complex_, ast::complex_number<double>>{"complex"} //
= x3::skip(x3::blank)['(' >> x3::double_ >>
((x3::double_ >> ij) | implied) >> ')'] //
| x3::lexeme[implied >> x3::double_ >> ij //
| x3::double_ >> implied];
所以,@Eljay的评论是对的。。。
该问题源于使用>
而不是>>
来允许失败,而不会在失败时触发错误处理程序。
因此,为了真正取得成功,我们需要在以下地方使用>>
:
const auto pure_imag_number = x3::attr(0.) >> x3::double_ >> x3::omit[x3::char_("ij")];
const auto pure_real_number = x3::double_ >> x3::attr(0.);
只有当我们真的想立即中止并报告错误时,才使用>
。
相关文章:
- boost::spirit::x3 中的通用解析器生成器
- 如何处理Boost Spirit X3导致Visual Studio 2019 "static initialization order fiasco"?
- Boost Spirit X3:将(一些)空格解析为枚举
- Boost Spirit x3 条件(三元)运算符解析器
- 你如何从 Boost Spirit X3 词法解析器中获取字符串?
- 将 Boost.Spirit.X3 解析器拆分为多个 TU
- boost::spirit::x3 中的简单字符串解析器不起作用
- 自定义预期失败的完整错误消息(boost::spirit::x3)
- (如何)我可以在不安装完整的提升库的情况下使用 boost::spirit X3 吗?
- Boost.Spirit X3 替代操作员
- Boost.Spirit X3 解析器"no type named type in(...)"
- boost::spirit::x3 phrase_parse 在进入 Vector 之前进行算术运算
- 如何使用 Boost Spirit x3 编写具有两个后操作数语法的二进制运算符?
- boost.spirit x3 move_to and list ast member
- 无法使用 Boost Spirit X3 解析空的 C++ 结构
- Boost.X3:char_ >> char_丢弃角色并表现得像点亮一样
- boost :: spirit :: x3中的connathesting std ::对属性属性
- 如何使用boost :: spirit :: x3测试有效的双重内容
- Boost.Spirit X3 编译时间因递归规则而爆炸
- boost spirit x3在拆分后的奇怪语义行为