Boost.x3:属性在替代方案之间累积 [英] Boost.x3: attribute accumulates between alternatives

查看:70
本文介绍了Boost.x3:属性在替代方案之间累积的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个解析器,用于解析像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对象,则它具有所有命名空间,在这种情况下,foobarnamespaces向量中有两次. 我有一个小例子说明这种奇怪的行为 此处. 谁能解释我为什么会发生这种情况,以及如何避免这种情况发生?

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 >> "::"匹配两次,并将foobar添加到第一个向量-> [{foo,bar},{},""].
  • id_string >> "."不匹配->序列失败->备用分支失败(保留属性不变).
  • 尝试第二个替代分支.
  • id_string >> "::"匹配两次,并将foobar添加到第一个向量-> [{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 adds foo and bar 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 adds foo and bar 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 and baz 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆