Boost.x3:属性在替代方案之间累积 [英] Boost.x3: attribute accumulates between alternatives
问题描述
我有一个解析器,用于解析像foo, bar, baz
这样的标识符,还有一个解析器,也用于解析像foo::bar, foo::bar.baz, foo::bar.baz.baham
这样的嵌套标识符
它们都解析为相同的ast结构,如下所示:
I have a parser for parsing an Identifier like foo, bar, baz
and one for parsing also nested identifiers like foo::bar, foo::bar.baz, foo::bar.baz.baham
They both parse into the same ast struct, which looks like this:
struct identifier : x3::position_tagged{
std::vector <std::string> namespaces;
std::vector <std::string> classes;
std::string identifier;
};
identifier
的解析器如下所示:
#define VEC_ATR x3::attr(std::vector<std::string>({})) //ugly hack
auto const identifier_def =
VEC_ATR
>> VEC_ATR
>> id_string;
,对于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
];
我知道我对宏感到羞耻.
标识符解析器可以正常工作,但是
nested_identifier
的行为异常
如果我尝试解析掉解析器之类的foo::bar::baz
ast对象,则它具有所有命名空间,在这种情况下,foo
和bar
在namespaces
向量中有两次.
我有一个小例子说明这种奇怪的行为
此处.
谁能解释我为什么会发生这种情况,以及如何避免这种情况发生?
I know shame on me for the macro.
The identifier parser works fine, but the
nested_identifier
has a strange behaviour
if I try to parse something like foo::bar::baz
the ast objects which falls out of the parser, has all the namespaces, in this case foo
and bar
twice in the namespaces
vector.
I have a small example of this strange behaviour
here.
Can anybody explain me why this happens, and how I can avoid this?
推荐答案
之所以会出现这种情况,是因为当其分支之一发生故障时,备用解析器不会自动回滚对外部属性所做的更改.
The reason why you get that behaviour is that the alternative parser does not automatically rollback the changes made to the external attribute when one of its branches fails.
在您的情况下,会发生以下情况:
In your case this is what happens:
- 最初,该属性为
[{},{},""]
.- 尝试第一个替代分支.
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]
.- 第二个替代分支成功.
- Initially the attribute is
[{},{},""]
.- The first alternative branch is tried.
id_string >> "::"
matches twice and addsfoo
andbar
to the first vector ->[{foo,bar},{},""]
.id_string >> "."
fails to match -> the sequence fails -> the alternative branch fails (leaving the attribute unchanged).- The second alternative branch is tried.
id_string >> "::"
matches twice and addsfoo
andbar
to the first vector ->[{foo,bar,foo,bar},{},""]
.attr(vector<string>({}))
succeeds (attr
always succeeds) and substitutes the empty second vector with a vector with an empty string ->[{foo,bar,foo,bar},{""},""]
.id_string
matches andbaz
is added to the attribute ->[{foo,bar,foo,bar},{""},baz]
.- The second alternative branch succeeds.
在Spirit.Qi中,这种情况下的解决方案非常简单,只需使用此处通过sehe. 此处是显示as
方法的简化示例.
In Spirit.Qi the solution in this case is quite easy, simply use the hold directive. Unfortunately this directive is not yet implemented in Spirit.X3. A possible alternative could be putting each of the alternative branches in its own x3::rule
either explicitly or with as<ast::identifier>(alternative_branch)
as used here by sehe. Here is a simplified example that shows the as
approach.
另一种可能是实现hold
指令,这是我的尝试(在WandBox上运行):
Another possibility could be implementing the hold
directive, here is my attempt(running on 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{};
}}}
这篇关于Boost.x3:属性在替代方案之间累积的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!