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;

};

のパーサーは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

        ];

私はマクロの恥を知っています。識別子パーサーは正常に 動作しますが、パーサーから外れる ast オブジェクトのnested_identifierようなものを解析しようとすると、奇妙な動作が発生し、すべての名前空間 (この場合はベクター内に2 回) があります。ここに、この奇妙な動作の小さな例があり ます。なぜこれが起こるのか、どうすればこれを回避できるのか、誰か説明してもらえますか?foo::bar::bazfoobarnamespaces

4

2 に答える 2

8

この動作が発生する理由は、分岐の 1 つが失敗したときに、代替パーサーが外部属性に加えられた変更を自動的にロールバックしないためです。

あなたの場合、これは何が起こるかです:

  • 最初の属性は[{},{},""]です。
  • 最初の代替ブランチが試行されます。
  • id_string >> "::"2 回一致し、最初のベクトルに and をfoo追加します-> . bar[{foo,bar},{},""]
  • id_string >> "."一致しない -> シーケンスが失敗する -> 代替分岐が失敗する (属性は変更されない)。
  • 2 番目の代替分岐が試行されます。
  • id_string >> "::"2 回一致し、最初のベクトルに and をfoo追加します-> . bar[{foo,bar,foo,bar},{},""]
  • attr(vector<string>({}))成功 (attr常に成功) し、空の 2 番目のベクトルを空の文字列を持つベクトルに置き換えます -> [{foo,bar,foo,bar},{""},""].
  • id_string一致bazし、属性に追加されます -> [{foo,bar,foo,bar},{""},baz]
  • 2 番目の代替分岐は成功します。

Spirit.Qi では、この場合の解決策は非常に簡単です。 hold ディレクティブを使用するだけです。残念ながら、このディレクティブはまだ Spirit.X3 に実装されていません。可能な代替手段は、x3::rule明示的に、またはここで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 に答える