スペアりぶ

びぼうろく

C++で構文解析 Boost.Spirit.Qi #1

Boost.Spirit.Qi(以下Qi)はC++用の構文解析ライブラリです。テンプレートや演算子オーバーロードを最大限に駆使しており、C++内で文法定義を完結させることができます。その分コンパイルがとても遅いです。

準備

QiはBoostの中に含まれています。Qiはビルド不要なライブラリなのでVisual Studioを使用している場合はNuGet経由でインストールすることができます。 ヘッダファイルをインクルードして準備は完了です。namespaceなどはお好みで。

#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;

基本

Qiでは文法を定義して、文字列をパースして構造体などに落としこむという感じで構文解析を行います。まずは例として整数が書かれた文字列をパースしてint型の値に変換してみましょう。

std::string str("12345"); // パース対象の文字列
auto itr = str.begin(), end = str.end();
int result;
bool success = qi::parse(itr, end, qi::int_, result); // パース
if (success && itr == end) {
    std::cout << result << std::endl;
}

qi::parse()が実際に文字列をパースする関数です。最初の2つはパースする文字列のイテレータを渡します(開始のイテレータは非const参照で、解析中に変更されます)。その次には文法を指定します(後述)。最後に、パースした値の格納先の参照を渡します。

qi::parse()の戻り値は、パースする文字列をすべて解析したかどうかにかかわらず、パースが成功したかどうかを示します。すべて解析したかどうかを調べるには、qi::parse()に渡したイテレータ同士を比較します。

このコードの実行結果は以下の通りとなります。

12345

パーサプリミティブ

前節のqi::int_にあたる部分が、文法を指定するところです。Qiではよく使う原始的なパーサが予め定義されています。

パーサ 説明
qi::int_ 整数が書かれた文字列をintへパース
qi::double_ 浮動小数点値が書かれた文字列をdoubleへパース
qi::char_ 任意の文字をパース
qi::alpha アルファベット文字をパース(std::isalpha()に基づく)
qi::space 空白文字をパース(std::isspace()に基づく)

その他多数(公式ドキュメント)。これらを組み合わせることで複雑な文法を解析することができるようになります。組み合わせる方法は次回。

空白などを無視する

空白文字などを無視してパースする場合は、qi::parse()関数の代わりにqi::phrase_parse()関数を使います。qi::parse()関数との違いは、文法を指定した後に、読み飛ばす文字(列)の文法を指定することだけです。

std::string str("     3.14  ");
auto itr = str.begin(), end = str.end();
double result;
bool success = qi::phrase_parse(itr, end, qi::double_, qi::space, result);
if (success && itr == end) {
    std::cout << result << std::endl;
}

このコードの実行結果は以下の通りとなります。

3.14

次回

このままだとstd::stoi()とかstd::stod()とやっていることがほぼ変わらないので、次回からは複雑な文法の定義の方法を紹介します。