【问题标题】:Boost.x3: attribute accumulates between alternativesBoost.x3:属性在备选方案之间累积
【发布时间】:2016-09-16 18:08:37
【问题描述】:

我有一个解析器用于解析像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;

};

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 对象具有所有命名空间,在这种情况下,namespaces 向量中有两次 foobar。 我有一个这种奇怪行为的小例子 here。 谁能解释一下为什么会发生这种情况,以及如何避免这种情况?

【问题讨论】:

  • 是什么让区分类和命名空间有用?例如。在 C++ 中,类命名空间。
  • 你说得对,我希望 ast 能保存尽可能多的信息。这就是为什么我将一个 id 后跟一个点解析到 classes 向量中。也许类不是最好的名字,但我找不到另一个快速

标签: c++ parsing boost-spirit boost-spirit-x3


【解决方案1】:

您得到这种行为的原因是,替代解析器在其分支之一失败时不会自动回滚对外部属性所做的更改。

你的情况是这样的:

  • 最初的属性是[{},{},""]
  • 尝试了第一个替代分支。
  • id_string &gt;&gt; "::" 匹配两次并将foobar 添加到第一个向量->[{foo,bar},{},""]
  • id_string &gt;&gt; "." 匹配失败 -> 序列失败 -> 替代分支失败(保持属性不变)。
  • 尝试了第二个替代分支。
  • id_string &gt;&gt; "::" 匹配两次并将foobar 添加到第一个向量->[{foo,bar,foo,bar},{},""]
  • attr(vector&lt;string&gt;({})) 成功(attr 总是成功)并将空的第二个向量替换为具有空字符串的向量 -> [{foo,bar,foo,bar},{""},""]
  • id_string 匹配,baz 被添加到属性 ->[{foo,bar,foo,bar},{""},baz]
  • 第二个替代分支成功。

在 Spirit.Qi 中,这种情况下的解决方案非常简单,只需使用 hold directive。不幸的是,该指令尚未在 Spirit.X3 中实现。一种可能的替代方法是将每个替代分支显式放置在自己的x3::rule 中,或者使用as&lt;ast::identifier&gt;(alternative_branch),就像sehe 使用的here 一样。 Here 是一个简化示例,展示了 as 方法。

另一种可能是实现hold 指令,这是我的尝试(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{};
}}}

【讨论】:

  • 以前从未使用过这个as&lt;something&gt;。它是如何工作的?非常感谢您的帮助,我没想到会出现这种行为。
  • 我认为它链接到解释它的地方:)
  • 对不起,我禁食了
  • @Exagon Here 是一个简化示例,展示了as 方法。
  • @sehe 和 jv_ 没有你们两个我会被搞砸的,谢谢你们一直以来的精神帮助我
【解决方案2】:

请注意,从 Boost1.70 开始,@sehe 提出的解决方案不再有效(参见this discussion)。

现在唯一的解决方法是重构语法,这样就不需要回滚了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-10
    相关资源
    最近更新 更多