Boost.x3:属性在备选方案之间累积

时间:2016-09-16 18:08:38

标签: 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;

};

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

2 个答案:

答案 0 :(得分:6)

您获得该行为的原因是,当其中一个分支失败时,备用解析器不会自动回滚对外部属性所做的更改。

在你的情况下,会发生这种情况:

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

在Spirit.Qi中,这种情况下的解决方案非常简单,只需使用hold directive即可。不幸的是,这个指令尚未在Spirit.X3中实现。一个可能的替代方案可能是将每个备用分支明确地放在其自己的x3::rule中,或者使用as<ast::identifier>(alternative_branch)来使用hereHere是一个显示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{};
}}}

答案 1 :(得分:0)

请注意,自Boost1.70起,@ sehe提出的解决方案不再起作用(请参见this discussion)。

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