Spirit:回溯时丢弃属性
Spirit: discarding attribute during backtracking
我不明白以下示例中 x3 的行为(取自较大的语法)。
语法有点奇怪,当然,但大致实现了(lal)?(<char>)?
。当第二组不存在时,它默认为 <default>
。我不明白为什么在输入 "lal<char>"
时我得到 defaultchar
作为结果:
#include <iostream>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
int main()
{
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
using ascii::alnum;
using ascii::char_;
using ascii::space;
using x3::attr;
using x3::eoi;
using x3::lexeme;
using x3::lit;
using x3::string;
const auto letter
= lit("<") >> string("char") >> lit('>')
| attr(std::string{"default"})
;
const auto letterset
= letter >> eoi
| lit("lal") >> letter >> eoi
;
for (std::string i: {"", "<char>", "lal", "lal<char>"})
{
auto res = std::string{};
auto first = i.cbegin();
auto last = i.cend();
auto r = x3::phrase_parse(first, last, letterset, space, res);
if (r && first == last)
std::cout << i << ": " << res << '\n';
else
std::cout << i << ": failed\n";
}
}
结果:
: default
<char>: char
lal: defaultdefault
lal<char>: defaultchar
我确实注意到,如果我交换 letterset
的备选方案,那么我会得到预期的结果,这可能是与回溯相关的标志:在原始情况下,我得到 default
第一次尝试,第二次尝试 char
,成功。
我很想将此称为错误,但我是 x3 新手...
这是来自 Boost 1.65 的 X3。
提前致谢@sehe :)
是的,您遇到了回溯的副作用。如果属性是容器(std::string
是容器),Spirit 解析器会附加其结果,但如果分支失败,则不会清除容器。
Qi
中有hold
指令来处理它,但X3
中没有。我会看看我能对 X3
做些什么,我认为有一些解决方案在没有可怕的 hold
指令的情况下几乎为零开销。
一般的建议是尝试重写语法以防止回溯,但如果不可能,您可以使用变通方法(它应该是比 hold
性能稍高的替代方法):
const auto recover_from_backtrack = (rule<class _, std::string>{} = eps) /
[](auto const& ctx) { x3::_attr(ctx).clear(); x3::_pass(ctx) = false; };
const auto letterset
= letter >> eoi
| recover_from_backtrack
| lit("lal") >> letter >> eoi
;
我不明白以下示例中 x3 的行为(取自较大的语法)。
语法有点奇怪,当然,但大致实现了(lal)?(<char>)?
。当第二组不存在时,它默认为 <default>
。我不明白为什么在输入 "lal<char>"
时我得到 defaultchar
作为结果:
#include <iostream>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
int main()
{
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
using ascii::alnum;
using ascii::char_;
using ascii::space;
using x3::attr;
using x3::eoi;
using x3::lexeme;
using x3::lit;
using x3::string;
const auto letter
= lit("<") >> string("char") >> lit('>')
| attr(std::string{"default"})
;
const auto letterset
= letter >> eoi
| lit("lal") >> letter >> eoi
;
for (std::string i: {"", "<char>", "lal", "lal<char>"})
{
auto res = std::string{};
auto first = i.cbegin();
auto last = i.cend();
auto r = x3::phrase_parse(first, last, letterset, space, res);
if (r && first == last)
std::cout << i << ": " << res << '\n';
else
std::cout << i << ": failed\n";
}
}
结果:
: default
<char>: char
lal: defaultdefault
lal<char>: defaultchar
我确实注意到,如果我交换 letterset
的备选方案,那么我会得到预期的结果,这可能是与回溯相关的标志:在原始情况下,我得到 default
第一次尝试,第二次尝试 char
,成功。
我很想将此称为错误,但我是 x3 新手...
这是来自 Boost 1.65 的 X3。
提前致谢@sehe :)
是的,您遇到了回溯的副作用。如果属性是容器(std::string
是容器),Spirit 解析器会附加其结果,但如果分支失败,则不会清除容器。
Qi
中有hold
指令来处理它,但X3
中没有。我会看看我能对 X3
做些什么,我认为有一些解决方案在没有可怕的 hold
指令的情况下几乎为零开销。
一般的建议是尝试重写语法以防止回溯,但如果不可能,您可以使用变通方法(它应该是比 hold
性能稍高的替代方法):
const auto recover_from_backtrack = (rule<class _, std::string>{} = eps) /
[](auto const& ctx) { x3::_attr(ctx).clear(); x3::_pass(ctx) = false; };
const auto letterset
= letter >> eoi
| recover_from_backtrack
| lit("lal") >> letter >> eoi
;