Exa*_*gon 3 c++ parsing boost-spirit boost-spirit-x3
我有一个用于解析标识符的解析器foo, bar, baz和一个用于解析嵌套标识符的解析器,foo::bar, foo::bar.baz, foo::bar.baz.baham
它们都解析为相同的ast结构,如下所示:
struct identifier : x3::position_tagged{
std::vector <std::string> namespaces;
std::vector <std::string> classes;
std::string identifier;
};
Run Code Online (Sandbox Code Playgroud)
解析器identifier看起来像这样:
#define VEC_ATR x3::attr(std::vector<std::string>({})) //ugly hack
auto const identifier_def =
VEC_ATR
>> VEC_ATR
>> id_string;
Run Code Online (Sandbox Code Playgroud)
对于nested_identifier这样的:
auto const nested_identifier_def =
x3::lexeme[
(+(id_string >> "::") >> +(id_string >> ".") > id_string)
| (+(id_string >> "::") >> VEC_ATR > id_string)
| (VEC_ATR >> +(id_string >> ".") > id_string)
| identifier
];
Run Code Online (Sandbox Code Playgroud)
我知道这个宏让我感到羞耻.标识符解析器工作正常,但
nested_identifier有一个奇怪的行为,如果我尝试解析像foo::bar::baz落在了解析器的AST对象,拥有所有的命名空间,在这种情况下foo,并bar在两次namespaces载体.我在这里有一个这种奇怪行为的小例子
.任何人都可以解释为什么会发生这种情况,以及如何避免这种情况?
您获得该行为的原因是,当其中一个分支失败时,备用解析器不会自动回滚对外部属性所做的更改.
在你的情况下,这是发生的事情:
- 最初属性是
[{},{},""].- 尝试了第一个替代分支.
id_string >> "::"匹配两次,并增加了foo与bar所述第一矢量- >[{foo,bar},{},""].id_string >> "."无法匹配 - >序列失败 - >替代分支失败(保持属性不变).- 尝试了第二个替代分支.
id_string >> "::"匹配两次,并增加了foo与bar所述第一矢量- >[{foo,bar,foo,bar},{},""].attr(vector<string>({}))成功(attr总是成功)并将空的第二个向量替换为带有空字符串的向量 - >[{foo,bar,foo,bar},{""},""].id_string匹配并baz添加到属性 - >[{foo,bar,foo,bar},{""},baz].- 第二个替代分支成功.
在Spirit.Qi中,这种情况下的解决方案非常简单,只需使用hold指令即可.不幸的是,这个指令尚未在Spirit.X3中实现.另一种可能,可以将在其各自的选择分支的x3::rule明示或as<ast::identifier>(alternative_branch)如使用这里的sehe.这是一个简化的示例,显示了该as方法.
另一种可能是实现该hold指令,这是我的尝试(在WandBox上运行):
#include <boost/spirit/home/x3/support/context.hpp>
#include <boost/spirit/home/x3/core/skip_over.hpp>
#include <boost/spirit/home/x3/core/parser.hpp>
namespace boost { namespace spirit { namespace x3
{
template <typename Subject>
struct hold_directive : unary_parser<Subject, hold_directive<Subject>>
{
typedef unary_parser<Subject, hold_directive<Subject> > base_type;
static bool const is_pass_through_unary = true;
static bool const handles_container = Subject::handles_container;
hold_directive(Subject const& subject)
: base_type(subject) {}
template <typename Iterator, typename Context
, typename RContext, typename Attribute>
bool parse(Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, Attribute& attr) const
{
Attribute copy(attr);
if (this->subject.parse(first, last, context, rcontext, copy))
{
traits::move_to(copy, attr);
return true;
}
return false;
}
};
struct hold_gen
{
template <typename Subject>
hold_directive<typename extension::as_parser<Subject>::value_type>
operator[](Subject const& subject) const
{
return { as_parser(subject) };
}
};
auto const hold = hold_gen{};
}}}
Run Code Online (Sandbox Code Playgroud)