| プリミティブ |
![]() |
![]() |
![]() |
フレームワークはいくつかの基本的なパーサをあらかじめ定義している。 これらはユーザがより複雑なパーサを組み立てる際の最も基本的な組み立て部品である。 これらプリミティブパーサはテンプレートクラスであり、非常に柔軟にできている。
これらすべてのプリミティブパーサは、直接またはテンプレート補助関数によって実体化されるクラスである。 通常、補助関数は打鍵数が少ない分、扱いがずっと単純である。
ジェネレータ関数 ch_p による文字リテラルパーサは既に見たが、これは実はパーサではなく、正確にはパーサジェネレータである。 この文字リテラルパーサの背後にある実際のテンプレートクラスは chlit<CharT> クラスである。 chlit オブジェクトをインスタンス化するには、文字の型を決定するテンプレートパラメータとして、文字型 CharT を明示的に与えなければならない。 この型は典型的には入力型に対応し、普通は char ないし wchar_t である。 次の式は一つの文字 'X' を認識するテンプレートパーサオブジェクトを生成する。
chlit<char>('X');
chlit のジェネレータ関数である ch_p を用いることで、 chlit<> の使い方が簡単になる。 (これは Spirit のパーサクラスのほとんどに当てはまることで、それを言うなら、ほとんどが対応するジェネレータ関数を持っている)。 関数を呼び出しの方が、引数を推定する際にテンプレート型を推定してくれるので、ずっと便利である。 上述の例は ch_p 補助関数を使うことで、より冗長にならない表現ができる。
ch_p('X') // chlit<char>('X') オブジェクトと等価
パーサジェネレータ関数の呼び出しは、パーサそのものと等価である。 そのため、技術的に言えば文字パーサを生成する関数であるにも関わらず、我々はよく ch_p を文字パーサと呼ぶことがある。 |
次の断片的な文法はこれらの方法の実例を示している:
// rule はパーサオブジェクトを”格納”できる。これらについては
// 後に述べるが、とりあえず rule はある種の型だと考えれば良い
rule<> r1, r2, r3;
chlit<char> x('X'); // x という名前のパーサを宣言
r1 = chlit<char>('X'); // 明示的な宣言
r2 = x; // x を用いたもの
r3 = ch_p('X') // ジェネレータを用いたもの
一つの文字リテラルに一致する。 chlit は一つのテンプレート型パラメータをもち、そのデフォルトは char である (つまり、 chlit<> は chlit<char> と等価である)。 この型パラメータは chlit が構文解析する際に扱う文字型である。 前述のように、関数ジェネレータ版は実際の関数引数からテンプレート型引数を推測する。 chlit クラスのコンストラクタは一つのパラメータ、入力に対してマッチする文字、を受け取る。 例えば:
r1 = chlit<>('X');
r2 = chlit<wchar_t>(L'X');
r3 = ch_p('X');
元の例に戻ると:
group = '(' >> expr >> ')';
expr1 = integer | group;
expr2 = expr1 >> *(('*' >> expr1) | ('/' >> expr1));
expr = expr2 >> *(('+' >> expr2) | ('-' >> expr2));
この文法宣言中の文字リテラル '('、')' 、 '+' 、 '-' 、 '*' および '/' は舞台裏でこっそりと作られた chlit オブジェクトである。
これが動作するのは、 operator>> の二つの特殊なテンプレート化されたオーバーロードのおかげである。これらは (char, ParserT) または (parserT, char) を取る。 これらの関数は文字を chlit オブジェクトに変換する。 |
明示的に宣言したければ:
chlit<> plus('+');
chlit<> minus('-');
chlit<> times('*');
chlit<> divide('/');
chlit<> oppar('(');
chlit<> clpar(')');
範囲内の文字を示す range は上下の文字の組から作られる。 このパーサは両端を含む range の範囲にある文字にマッチする。 chlit と同様、 range はデフォルトが char である一つのテンプレート型パラメータを持つ。 range クラスのコンストラクタは二つのパラメータ、入力に対してマッチする文字範囲(〜からと〜まで)を受け取る。 関数ジェネレータ版は range_p である。 例えば:
range<>('A','Z') // matches 'A'..'Z'
range_p('a','z') // matches 'a'..'z'
最初の文字は二つ目よりもその環境の文字エンコーディング的に"前"でなければならない。 range は chlit と同様、一文字用のパーサである。
文字のマッピングは本質的にプラットフォーム依存である。 標準は、例えば 'A' < 'Z' であることを保証しない。 たとえ多くの場合、 ASCII 、 IOS-8859-1 、あるいは Uniocde のような我々が用いている文字コードにおいてそうであると承知しているにも関わらず、である。 他のプラットフォームに移植する際には注意が必要である。 |
このパーサは文字列リテラルにマッチする。 strlit は一つのテンプレート型パラメータ、イテレータ型を持つ。 内部的には、 strlit は文字列ないし文字コンテナを指す始端/終端のイテレータを保持している。 strlit は現在の入力ストリームとこの文字列をマッチしようとする。 このテンプレート型パラメータのデフォルトは char const* である。 strlit は二つのコンストラクタを持つ。 一つは、ヌル終端を持つ文字列へのポインタを受け取る。 もう一つのコンストラクタは始端/終端イテレータの組を受け取る。 関数ジェネレータ版は str_p である。 例えば:
strlit<>("Hello World")
str_p("Hello World")
std::string msg("Hello World");
strlit<>(msg.begin(), msg.end());
典型的な構文解析器には、文字(単語や語彙の形をなすシンボル)を考慮するものと、語句(文の形をなす単語) を考慮するものという、異なるドメインのものがある。 ある文法の終端記号となる予約語、演算子、リテラル文字列、数値定数等々のエンティティは、 普通、分けられた字句解析ステージで抽出される。 これまでの例で明らかなように、 Spirit フレームワークは語句レベルと同じように文字レベルの構文解析タスクを処理する。 この点で標準的な構文解析の技法とまったく異なることに注目して欲しい。 Spirit フレームワークでは字句解析器はシームレスに統合されていると考えても良い。 Spirit パーサライブラリは別の字句解析器を必要としないが、字句解析器を持ってはいけない理由はない。 誰でも、いつでも、必要なだけ多くのパーサレイヤを持つことができる。 原理的には、プリプロセッサ、字句解析器、そして構文解析器のすべてを、同じフレームワークを用いてきちんと作ることができる。 |
文字シーケンスにマッチする。 chseq は strlit と同じテンプレート型パラメータとコンストラクタパラメータを持つ。 関数ジェネレータ版は chseq_p である。 例:
chseq<>("ABCDEFG")
chseq_p("ABCDEFG")
strlit では単語の区切りは絶対である。すなわち、文字レベルでのみ動作する。 一方で strlit の双子である chseq は、文字レベルと語句レベルのどちらでも動作することができる。 これは単に文字列の間の空白を無視できるという意味である。例えば:
chseq<>("ABCDEFG")
これは以下を構文解析できる:
ABCDEFG A B C D E F G AB CD EFG
フレームワークは、一文字を処理するパーサの完全なレパートリーを定義している。 ch_p やこれまでに紹介した他のジェネレータ関数とは異なり、 これらのパーサは実際にインスタンス化されたものである。
| 一文字パーサ | |
| anychar_p | あらゆる一文字に一致する(ヌル終端: '\0' を含む) |
| alnum_p | アルファベットと数字に一致する |
| alpha_p | アルファベットに一致する |
| blank_p | 空白とタブに一致する |
| cntrl_p | 制御文字に一致する |
| digit_p | 数字に一致する |
| graph_p | 空白以外の印字可能な文字に一致する |
| lower_p | 小文字に一致する |
| print_p | 印字可能な文字に一致する |
| punct_p | 句読点に一致する |
| space_p | 空白、タブ、復帰および改行に一致する |
| upper_p | 大文字に一致する |
| xdigit_p | 16 進数文字に一致する |
chlit 、 range 、 anychar_p 、 alnum_p などの一文字パーサの否定を表現できる。例えば:
~ch_p('x')
これは 'x' を除くあらゆる文字にマッチする。 文字パーサの二重否定は否定をキャンセルする。 ~~alpha_p は alpha_p と等価である。
行末( CR ないし LF 、あるいはその組み合わせ)にマッチする。
どれにもマッチせず、常にマッチに失敗する。
入力の終端にマッチする (入力が尽きたときに長さ 0 の文字列とマッチし、成功を返す)。
厳密にはプリミティブパーサではないが、 epsilon_p と eps_p はヌル文字列にマッチし、長さ 0 の文字列へのマッチを返す:
epsilon_p // 常に長さ 0 の文字列へのマッチを返す
イプシロンはパーサジェネレータとしても働く。この役割では引数として、引数なしの関数/ファンクタか、他のパーサを取る。 それらは空の(長さ 0 の)マッチか、あるいは失敗を報告するパーサを構築する。
関数/ファンクタが偽を評価するか、収容されたパーサが失敗を報告した場合、失敗が報告される。 そうでなければ空のマッチが報告される。
epsilon_p / eps_p によって構築されたパーサに対する ~ 演算子が定義されている。 これは、報告された結果を補完することで否定を行う。 ~~eps_p(x) は eps_p(x) と同一である。
例:
epsilon_p('0') >> oct_p // 実際には '0' は ch_p('0') であることに注意
ここでイプシロンは構文的な述語として使われている。 oct_p は先行する '0' が現れた場合のみ構文解析される。 イプシロンの内側に先行する '0' を包むことで、パーサが入力からなにも消費しないようにしている。 もし '0' が現れれば、 epsilon_p は長さ 0 のマッチが成功したと報告する。 oct_p についてより詳しくは数値セクションで学ぶ。 ここでは、 8 進数の数を構文解析するプリミティブパーサであると言えば十分だろう。
![]() |
![]() |
![]() |
Copyright © 1998-2003 Joel de Guzman
Copyright © 2003 Martin Wille
Permission to copy, use, modify, sell and distribute this document
is granted provided this copyright notice appears in all copies. This document
is provided "as is" without express or implied warranty, and with
no claim as to its suitability for any purpose.
Japanese Translation Copyright © 2003 Kent.N
オリジナルの、及びこの著作権表示が全ての複製の中に現れる限り、この文書の複製、利用、変更、販売そして配布を認める。このドキュメントは「あるがまま」に提供されており、いかなる明示的、暗黙的保証も行わない。また、いかなる目的に対しても、その利用が適していることを関知しない。
このドキュメントの対象: Boost Version 1.30.0
最新版ドキュメント(英語)