Boost.Spirit.Qiで自作int型パーサーの使用
Boost.Spirit.Qiはパーサー作成のライブラリです。
このライブラリの説明書に、主に文字列の解析で説明した。
ほかの型も解析できると思うが、今試した。
int型の解析器で練習してた。いろいろの問題に会ったが最後にできた。
まず、基本のパーサーを作る。Qiの説明書のレファレンスにコンセプトのチャプターがあります。よく見ると、パーサーはいろいろの種別がある。私は今ParserとPrimitiveParserしか試してなかった。パーサーについて、四つの条件がある。
- parse関数がある。パラメータは五つ:1と2、解析されるもののIterator範囲(〜から〜まで、パラメータ二つ)3、作用しらない(おい!)コンテキスト4、読み飛ばすパーサーと5、読み取る値。戻り値はbool型の成功(true)と失敗(false)。
- what関数。戻り値は boost::spirit::info の型の知らないもの。私はとりあえずreturn boost::spirit::info();にした(←
- attribute
::type 。読み取る値の型。こっちはintにする。 - traits::is_parser
::type ←なにこれわからない。しかしこのパーサーはboost::spirit::qi::parser を流用すればよさそうだ。この条件は放置しも大丈夫と思う
使わないパラメータはunused_typeにする。
すると、何もしないint型のパーサー(後は読み飛ばすパーサーとして使う)は作った:
parseに初めてのパラメータのIteratorはconstじゃない、それにレファレンスを使う。Qiの説明書について、成功するときfは「次はどこから続く」を示すべき。詳しいのはQiの説明書に参考してください。
template<class Iter>
struct MySkipper : boost::spirit::qi::parser<MySkipper<Iter> > {
bool parse(Iter& f, const Iter& l, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type) const
{
return false;
}boost::spirit::info what(boost::spirit::unused_type) const {
boost::spirit::info i;
return i;
}template<class Context, class Iter>
struct attribute { typedef void type; };
};
後は何かをするパーサー。自分は「範囲パーサー」作った。もし解析される値は特定の範囲に入れば成功になる。こうなる:
今度はSkipperを使い。例えば解析されるint配列に0を無視すべきにしたら、0のパーサーを作ってSkipperとして使えばいい。
template<class Iter, class Skipper>
struct MyIntParser : boost::spirit::qi::parser<MyIntParser<Iter, Skipper> > {int _min, _max;
MyIntParser(int min, int max) : _min(min), _max(max) { }template<class SK, class Cont>
bool parse(Iter& f, const Iter& l, Cont& c, SK& sk, int& attr) const
{
boost::spirit::qi::skip_over(f, l, sk);
if ( *f >= _min && *f < _max) {
attr = *f;
++f;
return true;
} else {
return false;
}
}boost::spirit::info what(boost::spirit::unused_type) const {
boost::spirit::info i;
return i;
}template<class Context, class Iter>
struct attribute { typedef int type; };
};
parse関数はQiの説明書により:
- 成功するときtrueの値を戻る、そして初めてのパラメータ(f)は「次はどこから続く」を示す、最後のパラメータは読み取る値を示す。
- そうじゃなくとfを変更じゃだめ
- 読み取る値はint型からattribute::typeの型はintだ。
そして、私はその自作パーサーを使いたい。説明書のtutorialにより、大体こんな感じ:
解説は、「初めての数字は1〜5の範囲に入って、次は5〜10、最後の数字は3〜7」。
template<class Iter, class Skipper>
struct MyParser : boost::spirit::qi::grammar<Iter, Skipper> {
boost::spirit::qi::rule<Iter, Skipper> _start;
typedef MyIntParser<Iter, Skipper> MIP;MyParser() : base_type(_start)
{
_start = MIP(1,5) >> MIP(5,10) >> MIP(3,7);
}
};
でもこうするとエラーになる。
MyIntParserに「>>」演算子は使えない。自分で>>演算子を書くより、もっと簡単な方法がある:ruleに入る。
そしてこうなる:
_m1、_m2、_m3のruleテンプレートに、int()は省略しちゃだめだ。このテンプレートパラメータは「このパーサーの戻り値はintだ」と示す。デフォルトでunused_typeになっちゃう。省略したら何も読み取れなかった。
template<class Iter, class Skipper>
struct MyParser : boost::spirit::qi::grammar<Iter, Skipper> {
boost::spirit::qi::rule<Iter, Skipper> _start;
boost::spirit::qi::rule<Iter, int(), Skipper> _m1;
boost::spirit::qi::rule<Iter, int(), Skipper> _m2;
boost::spirit::qi::rule<Iter, int(), Skipper> _m3;
typedef MyIntParser<Iter, Skipper> MIP;MyParser() : base_type(_start)
{
_m1 = MIP(1,5);
_m2 = MIP(5,10);
_m3 = MIP(3,7);
_start = _m1 >> _m2 >> _m3;
}
};
Skipperは省略しちゃだめだ。grammarのテンプレートパラメータにSkipperを省略したら、デフォルトでunused_typeになっちゃう。後はphrase関数に何のSkipperを指定しても読み飛ばせない。
さあ、使ってみよう:
int main()
{
int x[] = {3, 6, 6};
MyParser<int*, MySkipper<int*> > mp;
MySkipper<int*> sk;
int* xf = x;
boost::fusion::vector3<int, int, int> xval;
if (boost::spirit::qi::phrase_parse(xf, xf + 3, mp, sk))
std::cout << "success" << std::endl;
else
std::cout << "failed" << std::endl;
return 0;
}
さっきIterは直接int*と書かなかった理由は、std::vector
success ←w
このプログラムのソースコードを取得する ←CPPです
これだけで足りない。読み取る値を知りたいです。方法は二つある。
1、Parser Semantic Actionsを使う。方法は簡単です。tutorialの言うとおり:
ソースコードをこっち ←CPPです
template<class Iter, class Skipper>
struct MyParser : boost::spirit::qi::grammar<Iter, Skipper> {
boost::spirit::qi::rule<Iter, Skipper> _start;
boost::spirit::qi::rule<Iter, int(), Skipper> _m1;
boost::spirit::qi::rule<Iter, int(), Skipper> _m2;
boost::spirit::qi::rule<Iter, int(), Skipper> _m3;
typedef MyIntParser<Iter, Skipper> MIP;
int x[3];MyParser() : base_type(_start)
{
using boost::phoenix::ref;
using boost::spirit::qi::_1;
_m1 = MIP(1,5);
_m2 = MIP(5,10);
_m3 = MIP(3,7);
_start = _m1[ref(x[0])=_1] >> _m2[ref(x[1])=_1] >> _m3[ref(x[2])=_1];
}
};
もう一つの方法は、MyParserを三つのintの値を返せる。
Qiの説明書の「Employee - Parsing into structs」チャプターによると
Well, typically, the attribute of:
a >> b >> cis:
fusion::vector<A, B, C>
三つのint()型のruleは>>演算子で接続すると、読み取る値はfusion::vector
では_startとMyParserの型は
rule<..., boost::fusion::vector
にすれば、fusion::vector
ではMyParserは、こうなる
読み取る値はphrase_parse関数のパラメータで返す。
template<class Iter, class Skipper>
struct MyParser : boost::spirit::qi::grammar<Iter, boost::fusion::vector3<int, int, int>(), Skipper> {
boost::spirit::qi::rule<Iter, boost::fusion::vector3<int, int, int>(), Skipper> _start;
boost::spirit::qi::rule<Iter, int(), Skipper> _m1;
boost::spirit::qi::rule<Iter, int(), Skipper> _m2;
boost::spirit::qi::rule<Iter, int(), Skipper> _m3;
typedef MyIntParser<Iter, Skipper> MIP;int a,b,c;
MyParser() : base_type(_start)
{
_m1 = MIP(1,5);
_m2 = MIP(5,10);
_m3 = MIP(3,7);
_start = _m1 >> _m2 >> _m3;
}
};
ソースコードを取得する ←CPPです
boost::fusion::vector3<int, int, int> xval;
boost::spirit::qi::phrase_parse(xf, xf + 3, mp, sk, xval);
実はね、そのint()とかboost::fusion::vector
Skipperは、元々boost::spirit::qi::no_skip使いたいですが、なんだか使えないようだ。仕方ない、自作する。→MySkipper
私はSpirit.Qiに初心者です。興味ある方は交流してくれるとうれしいです。
追記:
私の書いたコードは正しいかどうかわからない。ここでまた何かを発見した。公式の例で、しかも使い方は私と違ってる。