4

>スピリットの解析でand>>演算子を使用することについての私の無知を誰かが照らしてくれることを願っています。

トップレベルのルールが次のように見える実用的な文法があります

test = identifier >> operationRule >> repeat(1,3)[any_string] >> arrow >> any_string >> conditionRule;

これは、アトリビュートに依存して、解析された値をフュージョンに適応した構造体 (つまり、ブースト タプル) に自動的に割り当てます。

ただし、 operationRule に一致したら、続行するか失敗する必要があることはわかっています (つまり、 で始まる他のルールを試すバックトラックを許可したくありませんidentifier)。

test = identifier >> 
           operationRule > repeat(1,3)[any_string] > arrow > any_string > conditionRule;

これにより、不可解なコンパイラ エラー ( 'boost::Container' : use of class template requires template argument list) が発生します。少しふざけると、次のようにコンパイルされます。

test = identifier >> 
           (operationRule > repeat(1,3)[any_string] > arrow > any_string > conditionRule);

しかし、属性設定は機能しなくなりました-解析後にデータ構造にゴミが含まれています。これは のようなアクションを追加することで修正できます[at_c<0>(_val) = _1]が、それは少し不格好に思えます - また、boost docs によると動作が遅くなります。

だから、私の質問は

  1. バックトラッキングを防止する価値はありますか?
  2. グループ化演算子が必要な理由()
  3. operationRule上記の最後の例は、一致した後にバックトラッキングを本当に停止し(...)ますか?
  4. 前の質問に対する答えが /いいえ/ である場合、operation操作が /一致する/一致しない場合はバックトラックを許可するが、操作が/一致する場合はバックトラックを許可しないルールをどのように作成すればよいですか?
  5. グループ化演算子が属性文法を破壊するのはなぜですか? アクションが必要です?

私はこれが非常に幅広い質問であることを認識しています - 正しい方向を指し示すヒントは大歓迎です!

4

1 に答える 1

5
  1. バックトラッキングを防止する価値はありますか?

    絶対。一般に、バック トラッキングを防止することは、パーサーのパフォーマンスを向上させる実証済みの方法です。

    • (否定的な) 先読み (演算子 !、演算子 - および一部の演算子 &) の使用を減らす
    • 最も頻繁な/可能性の高いブランチが最初に順序付けられるか、最もコストのかかるブランチが最後に試行されるように、ブランチ (演算子 |、演算子 ||、演算子^、および一部の演算子 */-/+) を並べ替えます。

    期待値 ( >) を使用しても、本質的にバックトラッキングが減少するわけではありません。許可しないだけです。これにより、対象を絞ったエラー メッセージが有効になり、無用な「未知への解析」が防止されます。

  2. グループ化が必要な理由operator ()

    わからない。ここからヘルパーを使ってチェックしましたwhat_is_the_attr

    • ident >> op >> repeat(1,3)[any] >> "->" >> any
      属性を合成します:

      fusion::vector4<string, string, vector<string>, string>
      
    • ident >> op > repeat(1,3)[any] > "->" > any
      属性を合成します:

      fusion::vector3<fusion::vector2<string, string>, vector<string>, string>
      

    かっこを使用して部分式をグループ化する必要性はわかりませんでしたが(物事はコンパイルされます)、DataT変更されたレイアウトに合わせて変更する必要があることは明らかです。

    typedef boost::tuple<
        boost::tuple<std::string, std::string>, 
        std::vector<std::string>, 
        std::string
    > DataT;
    

以下の完全なコードは、適応された構造体を使用して、私がそれをどのように行うかを示しています。

  1. 上記の例は、 operationRule が一致した後にバックトラッキングを本当に停止しますか?

    絶対。期待が満たされない場合、qi::expectation_failure<>例外がスローされます。これにより、デフォルトで解析が中止されます。retryfailacceptまたはに qi::on_error を使用できますrethrowMiniXML の例には、期待値を使用した非常に良い例がありますqi::on_error

  2. 前の質問に対する答えが「いいえ」の 場合、操作が一致する/一致しない場合はバックトラックを許可するが、操作が一致すると/一致する場合はバックトラックを許可しないルールを作成するにはどうすればよいですか?

  3. グループ化演算子が属性文法を破壊するのはなぜですか? アクションが必要です?

    属性の文法を破壊するのではなく、公開された型を変更するだけです。したがって、適切な属性参照をルール/文法にバインドすると、セマンティック アクションは必要ありません。さて、 grouping なしで行く方法があるはずだと思うので、試してみましょう(できれば短い自己完結型のサンプルで)。 実際、私はそのような必要性を発見していません。セマンティック アクションを使用せずに、私のテストで何が起こっているかを確認できるように、完全な例を追加しました。

完全なコード

完全なコードは、5 つのシナリオを示しています。

  • オプション 1: 期待を裏切らないオリジナル

    (関連する変更はありません)

  • オプション 2: 期待を持って

    DataT の変更された typedef を使用する (上記のように)

  • オプション 3: 期待せずに構造体を適合させる

    BOOST_FUSION_ADAPT_STRUCT でユーザー定義の構造体を使用する

  • オプション 4: 構造体を適応させ、期待を込めて

    OPTION 3 から適合した構造体を変更する

  • オプション 5: 先読みハック

    これは「巧妙な」(?) ハックを利用して、すべて>>を期待値にし、operationRule-match の存在を事前に検出します。DataTもちろん、これは最適ではありませんが、セマンティック アクションを使用せずに変更せずに保つことができます。

明らかに、OPTIONコンパイルする前に目的の値に定義してください。

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/adapted.hpp>
#include <iostream>

namespace qi    = boost::spirit::qi; 
namespace karma = boost::spirit::karma; 

#ifndef OPTION
#define OPTION 5
#endif

#if OPTION == 1 || OPTION == 5 // original without expectations (OR lookahead hack)
    typedef boost::tuple<std::string, std::string, std::vector<std::string>, std::string> DataT;
#elif OPTION == 2 // with expectations
    typedef boost::tuple<boost::tuple<std::string, std::string>, std::vector<std::string>, std::string> DataT;
#elif OPTION == 3 // adapted struct, without expectations
    struct DataT
    {
        std::string identifier, operation;
        std::vector<std::string> values;
        std::string destination;
    };

    BOOST_FUSION_ADAPT_STRUCT(DataT, (std::string, identifier)(std::string, operation)(std::vector<std::string>, values)(std::string, destination));
#elif OPTION == 4 // adapted struct, with expectations
    struct IdOpT
    {
        std::string identifier, operation;
    };
    struct DataT
    {
        IdOpT idop;
        std::vector<std::string> values;
        std::string destination;
    };

    BOOST_FUSION_ADAPT_STRUCT(IdOpT, (std::string, identifier)(std::string, operation));
    BOOST_FUSION_ADAPT_STRUCT(DataT, (IdOpT, idop)(std::vector<std::string>, values)(std::string, destination));
#endif

template <typename Iterator>
struct test_parser : qi::grammar<Iterator, DataT(), qi::space_type, qi::locals<char> >
{
    test_parser() : test_parser::base_type(test, "test")
    {
        using namespace qi;

        quoted_string = 
               omit    [ char_("'\"") [_a =_1] ]             
            >> no_skip [ *(char_ - char_(_a))  ]
             > lit(_a); 

        any_string = quoted_string | +qi::alnum;

        identifier = lexeme [ alnum >> *graph ];

        operationRule = string("add") | "sub";
        arrow = "->";

#if OPTION == 1 || OPTION == 3   // without expectations
        test = identifier >> operationRule >> repeat(1,3)[any_string] >> arrow >> any_string;
#elif OPTION == 2 || OPTION == 4 // with expectations
        test = identifier >> operationRule  > repeat(1,3)[any_string]  > arrow  > any_string;
#elif OPTION == 5                // lookahead hack
        test = &(identifier >> operationRule) > identifier > operationRule > repeat(1,3)[any_string] > arrow > any_string;
#endif
    }

    qi::rule<Iterator, qi::space_type/*, qi::locals<char> */> arrow;
    qi::rule<Iterator, std::string(), qi::space_type/*, qi::locals<char> */> operationRule;
    qi::rule<Iterator, std::string(), qi::space_type/*, qi::locals<char> */> identifier;
    qi::rule<Iterator, std::string(), qi::space_type, qi::locals<char> > quoted_string, any_string;
    qi::rule<Iterator, DataT(),       qi::space_type, qi::locals<char> > test;
};

int main()
{
    std::string str("addx001 add 'str1'   \"str2\"       ->  \"str3\"");
    test_parser<std::string::const_iterator> grammar;
    std::string::const_iterator iter = str.begin();
    std::string::const_iterator end  = str.end();

    DataT data;
    bool r = phrase_parse(iter, end, grammar, qi::space, data);

    if (r)
    {
        using namespace karma;
        std::cout << "OPTION " << OPTION << ": " << str << " --> ";
#if OPTION == 1 || OPTION == 3 || OPTION == 5 // without expectations (OR lookahead hack)
        std::cout << format(delimit[auto_ << auto_ << '[' << auto_ << ']' << " --> " << auto_], data) << "\n";
#elif OPTION == 2 || OPTION == 4 // with expectations
        std::cout << format(delimit[auto_ << '[' << auto_ << ']' << " --> " << auto_], data) << "\n";
#endif
    }
    if (iter!=end)
        std::cout << "Remaining: " << std::string(iter,end) << "\n";
}

すべての OPTIONS の出力:

for a in 1 2 3 4 5; do g++ -DOPTION=$a -I ~/custom/boost/ test.cpp -o test$a && ./test$a; done
OPTION 1: addx001 add 'str1'   "str2"       ->  "str3" --> addx001 add [ str1 str2 ]  -->  str3 
OPTION 2: addx001 add 'str1'   "str2"       ->  "str3" --> addx001 add [ str1 str2 ]  -->  str3 
OPTION 3: addx001 add 'str1'   "str2"       ->  "str3" --> addx001 add [ str1 str2 ]  -->  str3 
OPTION 4: addx001 add 'str1'   "str2"       ->  "str3" --> addx001 add [ str1 str2 ]  -->  str3 
OPTION 5: addx001 add 'str1'   "str2"       ->  "str3" --> addx001 add [ str1 str2 ]  -->  str3 
于 2012-04-30T20:56:46.010 に答える