バックトラッキングを防止する価値はありますか?
絶対。一般に、バック トラッキングを防止することは、パーサーのパフォーマンスを向上させる実証済みの方法です。
- (否定的な) 先読み (演算子 !、演算子 - および一部の演算子 &) の使用を減らす
- 最も頻繁な/可能性の高いブランチが最初に順序付けられるか、最もコストのかかるブランチが最後に試行されるように、ブランチ (演算子 |、演算子 ||、演算子^、および一部の演算子 */-/+) を並べ替えます。
期待値 ( >
) を使用しても、本質的にバックトラッキングが減少するわけではありません。許可しないだけです。これにより、対象を絞ったエラー メッセージが有効になり、無用な「未知への解析」が防止されます。
グループ化が必要な理由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;
以下の完全なコードは、適応された構造体を使用して、私がそれをどのように行うかを示しています。
上記の例は、 operationRule が一致した後にバックトラッキングを本当に停止しますか?
絶対。期待が満たされない場合、qi::expectation_failure<>
例外がスローされます。これにより、デフォルトで解析が中止されます。retry
、fail
、accept
またはに qi::on_error を使用できますrethrow
。MiniXML の例には、期待値を使用した非常に良い例があります。qi::on_error
前の質問に対する答えが「いいえ」の 場合、操作が一致する/一致しない場合はバックトラックを許可するが、操作が一致すると/一致する場合はバックトラックを許可しないルールを作成するにはどうすればよいですか?
グループ化演算子が属性文法を破壊するのはなぜですか? アクションが必要です?
属性の文法を破壊するのではなく、公開された型を変更するだけです。したがって、適切な属性参照をルール/文法にバインドすると、セマンティック アクションは必要ありません。さて、 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