4

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

};

an 的解析器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::bazast 对象这样掉出解析器的东西,它有一个奇怪的行为,它有所有的命名空间,在这种情况下foo和向量bar中的两次。namespaces我在 这里有一个这种奇怪行为的小例子。谁能解释我为什么会发生这种情况,以及如何避免这种情况?

4

2 回答 2

8

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

在您的情况下,会发生以下情况:

  • 最初的属性是[{},{},""]
  • 尝试了第一个替代分支。
  • id_string >> "::"匹配两次并将 and 添加foobar第一个向量 -> [{foo,bar},{},""]
  • id_string >> "."不匹配 -> 序列失败 -> 替代分支失败(保持属性不变)。
  • 尝试了第二个替代分支。
  • id_string >> "::"匹配两次并将 and 添加foobar第一个向量 -> [{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一种可能的替代方法可能是将每个替代分支显式或与sehe在这里as<ast::identifier>(alternative_branch)使用的一样放在自己的分支中。是一个显示该方法的简化示例。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{};
}}}
于 2016-09-16T22:55:02.987 回答
2

请注意,从 Boost1.70 开始,@sehe 提出的解决方案不再起作用(请参阅此讨论)。

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

于 2019-08-25T12:30:10.950 回答