Boost.Spirit.Qiで自作int型パーサーの使用

Boost.Spirit.Qiはパーサー作成のライブラリです。
このライブラリの説明書に、主に文字列の解析で説明した。
ほかの型も解析できると思うが、今試した。

int型の解析器で練習してた。いろいろの問題に会ったが最後にできた。

まず、基本のパーサーを作る。Qiの説明書のレファレンスにコンセプトのチャプターがあります。よく見ると、パーサーはいろいろの種別がある。私は今ParserとPrimitiveParserしか試してなかった。パーサーについて、四つの条件がある。

  1. parse関数がある。パラメータは五つ:1と2、解析されるもののIterator範囲(〜から〜まで、パラメータ二つ)3、作用しらない(おい!)コンテキスト4、読み飛ばすパーサーと5、読み取る値。戻り値はbool型の成功(true)と失敗(false)。
  2. what関数。戻り値は boost::spirit::info の型の知らないもの。私はとりあえずreturn boost::spirit::info();にした(←
  3. attribute::type 。読み取る値の型。こっちはintにする。
  4. traits::is_parser

    ::type ←なにこれわからない。しかしこのパーサーはboost::spirit::qi::parser を流用すればよさそうだ。この条件は放置しも大丈夫と思う

使わないパラメータはunused_typeにする。

すると、何もしないint型のパーサー(後は読み飛ばすパーサーとして使う)は作った:


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; };
};

parseに初めてのパラメータのIteratorはconstじゃない、それにレファレンスを使う。Qiの説明書について、成功するときfは「次はどこから続く」を示すべき。詳しいのはQiの説明書に参考してください。

後は何かをするパーサー。自分は「範囲パーサー」作った。もし解析される値は特定の範囲に入れば成功になる。こうなる:


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; };
};

今度はSkipperを使い。例えば解析されるint配列に0を無視すべきにしたら、0のパーサーを作ってSkipperとして使えばいい。
parse関数はQiの説明書により:

  1. 成功するときtrueの値を戻る、そして初めてのパラメータ(f)は「次はどこから続く」を示す、最後のパラメータは読み取る値を示す。
  2. そうじゃなくとfを変更じゃだめ
  3. 読み取る値はint型からattribute::typeの型はintだ。

そして、私はその自作パーサーを使いたい。説明書のtutorialにより、大体こんな感じ:


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);
}
};

解説は、「初めての数字は1〜5の範囲に入って、次は5〜10、最後の数字は3〜7」。
でもこうするとエラーになる。
MyIntParserに「>>」演算子は使えない。自分で>>演算子を書くより、もっと簡単な方法がある:ruleに入る。
そしてこうなる:

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;
}
};

_m1、_m2、_m3のruleテンプレートに、int()は省略しちゃだめだ。このテンプレートパラメータは「このパーサーの戻り値はintだ」と示す。デフォルトでunused_typeになっちゃう。省略したら何も読み取れなかった

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::iteratorの可能性もあるとかなんとか・・・ね

success ←w
このプログラムのソースコードを取得する ←CPPです


これだけで足りない。読み取る値を知りたいです。方法は二つある。

1、Parser Semantic Actionsを使う。方法は簡単です。tutorialの言うとおり:


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];
}
};

ソースコードをこっち ←CPPです

もう一つの方法は、MyParserを三つのintの値を返せる。

Qiの説明書の「Employee - Parsing into structs」チャプターによると

Well, typically, the attribute of:
a >> b >> c

is:
fusion::vector<A, B, C>

三つのint()型のruleは>>演算子で接続すると、読み取る値はfusion::vector型になる。
では_startとMyParserの型は
rule<..., boost::fusion::vector(), ...>
にすれば、fusion::vector型の値を読み取れる。

ではMyParserは、こうなる


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;
}
};

読み取る値はphrase_parse関数のパラメータで返す。

boost::fusion::vector3<int, int, int> xval;
boost::spirit::qi::phrase_parse(xf, xf + 3, mp, sk, xval);
ソースコードを取得する ←CPPです


実はね、そのint()とかboost::fusion::vector()とか、書き間違えたらコンパイルできる可能性もある。しかし当たり前で何も取れなかった。


Skipperは、元々boost::spirit::qi::no_skip使いたいですが、なんだか使えないようだ。仕方ない、自作する。→MySkipper


私はSpirit.Qiに初心者です。興味ある方は交流してくれるとうれしいです。


追記:
私の書いたコードは正しいかどうかわからない。ここでまた何かを発見した。公式の例で、しかも使い方は私と違ってる。