ブーストスピリットセマンティックアクションパラメーター
-
28-09-2019 - |
質問
これで ブーストスピリットセマンティックアクションに関する記事 それは言及されています
実際には、さらに2つの引数が渡されます。パーサーコンテキストと、ブールの「ヒット」パラメーターへの参照です。パーサーのコンテキストは、セマンティックアクションがルールの右側に添付されている場合にのみ意味があります。これについてはまもなく詳細に説明します。ブール値は、セマンティックアクション内でfalseに設定することができます。回顧展で一致を無効にし、パーサーを失敗させます。
すべて問題ありませんが、他のパラメーター(パーサーコンテキストとヒットブール)を使用するセマンティックアクションとして関数オブジェクトを渡す例を見つけようとしていますが、私は見つかりませんでした。 Phoenix Voodooをかろうじてgrokったので、通常の機能や関数オブジェクトを使用した例を見たいです
解決
これは、QiとPhoenixのインターフェイスに到達するため、本当に良い質問(そしてワームの缶)です。私も例を見たことがないので、この方向に記事を少し拡張します。
あなたが言うように、機能します セマンティックアクション 最大3つのパラメーターを取ることができます
- 一致した属性 - 記事で説明されています
- コンテキスト-Qi -Phoenixインターフェイスが含まれています
- マッチフラグ - マッチ状態を操作します
フラグをマッチします
記事が述べているように、式がルールの一部でない限り、2番目のパラメーターは意味がないため、3番目のパラメーターから始めましょう。 2番目のパラメーターのプレースホルダーはまだ必要ですが、この使用には boost::fusion::unused_type
. 。したがって、3番目のパラメーターを使用する記事からの変更された関数は次のとおりです。
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
void f(int attribute, const boost::fusion::unused_type& it, bool& mFlag){
//output parameters
std::cout << "matched integer: '" << attribute << "'" << std::endl
<< "match flag: " << mFlag << std::endl;
//fiddle with match flag
mFlag = false;
}
namespace qi = boost::spirit::qi;
int main(void){
std::string input("1234 6543");
std::string::const_iterator begin = input.begin(), end = input.end();
bool returnVal = qi::phrase_parse(begin, end, qi::int_[f], qi::space);
std::cout << "return: " << returnVal << std::endl;
return 0;
}
出力:
matched integer: '1234' match flag: 1 return: 0
この例は、一致を非試合に切り替えることです。これはパーサー出力に反映されます。 Hkaiserによると、Boost 1.44以降はMatch FlagをFalseに設定すると、通常の方法でマッチが失敗します。代替案が定義されている場合、パーサーはバックトラックし、予想されるようにそれらを一致させようとします。ただし、Boost <= 1.43では、スピリットのバグがバックトラッキングを防ぎ、奇妙な動作を引き起こします。これを見るには、Phoenixを追加します boost/spirit/include/phoenix.hpp
式を変更します
qi::int_[f] | qi::digit[std::cout << qi::_1 << "\n"]
Qi :: intパーサーが失敗すると、「1」での入力の開始と一致する代替qi ::桁がありますが、出力は次のと期待しています。
matched integer: '1234' match flag: 1 6 return: 1
6
入力の2番目のINTの最初の数字は、スキッパーを使用してバックトラッキングなしで使用されていることを示す入力の最初の桁です。また、代替案に基づいて、試合が成功していると見なされていることにも注意してください。
ブースト1.44が発売されると、マッチフラグは、パーサーシーケンスで表現するのが難しいかもしれない一致基準を適用するのに役立ちます。マッチフラグは、フェニックス式で操作できることに注意してください。 _pass
プレースホルダー。
コンテキストパラメーター
より興味深いパラメーターは、セマンティックアクションのコンテキストであるQi-Phoenixインターフェイス、またはQI用語を含む2番目のパラメーターです。これを説明するために、まずルールを調べます。
rule<Iterator, Attribute(Arg1,Arg2,...), qi::locals<Loc1,Loc2,...>, Skipper>
コンテキストパラメーターは、属性、arg1、... argn、qi :: localsテンプレートパラメーターを具体化します。この属性は関数パラメーターとは異なります。関数パラメーター属性は解析値であり、この属性はルール自体の値です。セマンティックアクションは、前者を後者にマッピングする必要があります。これは、可能なコンテキストタイプの例です(示されているフェニックス式の等価物):
using namespace boost;
spirit::context< //context template
fusion::cons<
int&, //return int attribute (phoenix: _val)
fusion::cons<
char&, //char argument1 (phoenix: _r1)
fusion::cons<
float&, //float argument2 (phoenix: _r2)
fusion::nil //end of cons list
>,
>,
>,
fusion::vector2< //locals container
char, //char local (phoenix: _a)
unsigned int //unsigned int local (phoenix: _b)
>
>
return属性と引数リストに注意してくださいLispスタイルのリストの形を取ります( 短所リスト)。関数内のこれらの変数にアクセスするには、 attribute
また locals
のメンバー context
Fusion :: at <>()を備えたstructテンプレート。たとえば、コンテキスト変数の場合 con
//assign return attribute
fusion::at_c<0>(con.attributes) = 1;
//get the second rule argument
float arg2 = fusion::at_c<2>(con.attributes);
//assign the first local
fusion::at_c<1>(con.locals) = 42;
記事の例を修正して2番目の引数を使用するには、関数定義とfrase_parse呼び出しを変更します。
...
typedef
boost::spirit::context<
boost::fusion::cons<int&, boost::fusion::nil>,
boost::fusion::vector0<>
> f_context;
void f(int attribute, const f_context& con, bool& mFlag){
std::cout << "matched integer: '" << attribute << "'" << std::endl
<< "match flag: " << mFlag << std::endl;
//assign output attribute from parsed value
boost::fusion::at_c<0>(con.attributes) = attribute;
}
...
int matchedInt;
qi::rule<std::string::const_iterator,int(void),ascii::space_type>
intRule = qi::int_[f];
qi::phrase_parse(begin, end, intRule, ascii::space, matchedInt);
std::cout << "matched: " << matchedInt << std::endl;
....
これは、解析された値を出力属性値にマップするだけの非常に簡単な例ですが、拡張機能はかなり明らかです。コンテキスト構造テンプレートパラメーターをルールの出力、入力、ローカルタイプに一致させるだけです。解析されたタイプ/値から出力タイプ/値の間のこのタイプの直接一致は、自動ルールを使用して自動的に実行できることに注意してください。 %=
aの代わりに =
ルールを定義するとき:
qi::rule<std::string::const_iterator,int(void),ascii::space_type>
intRule %= qi::int_;
IMHO、各アクションの関数を書くことは、簡単で読みやすいフェニックス式の同等物と比較して、かなり退屈です。私はブードゥー教の視点に同情しますが、フェニックスとしばらく作業すると、セマンティクスと構文はそれほど難しくありません。
編集:フェニックス付きルールコンテキストへのアクセス
コンテキスト変数は、パーサーがルールの一部である場合にのみ定義されます。パーサーは、入力を消費する式であると考えてください。ここでは、ルールがパーサー値(QI :: _ 1)をルール値(QI :: _ VAL)に変換します。たとえば、Qi :: valにポッド解析値から構築する必要があるクラスタイプがある場合、違いはしばしば自明です。以下は簡単な例です。
入力の一部が3つのCSV整数のシーケンスであるとしましょう(x1, x2, x3
)、そして、私たちはこれら3つの整数(f = x0 +(x1 + x2)*x3)の算術関数のみを気にします。ここで、x0は他の場所で得られる値です。 1つのオプションは、整数で読み取り、関数を計算するか、またはPhoenixを使用して両方を実行することです。
この例では、出力属性(関数値)と入力(x0)を持つ1つのルールを使用し、ローカル(ルールを使用して個々のパーサー間で情報を渡す)を使用します。これが完全な例です。
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
#include <iostream>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
int main(void){
std::string input("1234, 6543, 42");
std::string::const_iterator begin = input.begin(), end = input.end();
qi::rule<
std::string::const_iterator,
int(int), //output (_val) and input (_r1)
qi::locals<int>, //local int (_a)
ascii::space_type
>
intRule =
qi::int_[qi::_a = qi::_1] //local = x1
>> ","
>> qi::int_[qi::_a += qi::_1] //local = x1 + x2
>> ","
>> qi::int_
[
qi::_val = qi::_a*qi::_1 + qi::_r1 //output = local*x3 + x0
];
int ruleValue, x0 = 10;
qi::phrase_parse(begin, end, intRule(x0), ascii::space, ruleValue);
std::cout << "rule value: " << ruleValue << std::endl;
return 0;
}
あるいは、すべてのINTをベクトルとして解析することができ、単一のセマンティックアクションで評価された関数( %
以下はリスト演算子とベクトルの要素にPhoenix :: atでアクセスされます):
namespace ph = boost::phoenix;
...
qi::rule<
std::string::const_iterator,
int(int),
ascii::space_type
>
intRule =
(qi::int_ % ",")
[
qi::_val = (ph::at(qi::_1,0) + ph::at(qi::_1,1))
* ph::at(qi::_1,2) + qi::_r1
];
....
上記の場合、入力が正しくない場合(3つではなく2つのINT)、実行時に悪いことが発生する可能性があるため、解析値の数を明示的に指定する方が良いため、解析は悪い入力で失敗します。以下の使用 _1
, _2
, 、 と _3
最初、2番目、および3番目の一致値を参照するには:
(qi::int_ >> "," >> qi::int_ >> "," >> qi::int_)
[
qi::_val = (qi::_1 + qi::_2) * qi::_3 + qi::_r1
];
これは不自然な例ですが、アイデアを与えるはずです。 Phoenixのセマンティックアクションは、入力から直接複雑なオブジェクトを構築するのに非常に役立つことがわかりました。これは、セマンティックアクション内でコンストラクターとメンバー機能を呼び出すことができるため可能です。