ルール

ポリモーフィックなパーサである rule は、代入された EBNF 式の振る舞いをキャプチャする名前付きのプレースホルダとして働く。 EBNF 式に名前を付けることで、それを後から参照することができる。 rule はテンプレートクラスであり、スキャナの型( ScannerT )、ルールのコンテキスト、およびそのタグによってパラメータ化されている。 ルールを簡単に使えるようにデフォルトのテンプレートパラメータが与えられている。

    template<
        typename ScannerT = scanner<>,
        typename ContextT = parser_context>
    class rule;

デフォルトのテンプレートパラメータは最も一般的なケースを扱うように与えられている。 ScannerT のデフォルトは scanner<> で、 char const* イテレータを用いるごく普通のスキャナである。 これはヌル終端を持つ入力中の文字を一度に一文字ずつ繰り返す以外、特別なことは何もしない。 普通は、ルールとして rule<> を宣言するだけで十分である。 ルールの低レベルな振る舞いに手を加えたいと思わない限り、 ContextT テンプレートパラメータについて気にする必要はまったく無い。 ContextT テンプレートパラメータの詳細な情報は他の場所で提供されている。

上記は公開された API である。 ContextT の後には実際にはより多くのテンプレートパラメータが存在しうる。 ContextT パラメータの後ろのすべてのものは利用者に考慮されるべきではなく、厳格に内部でのみ使用すべきものである。

ルールは EBNF の生成規則をモデルとしている。例えば:

    rule<> a_rule = *(a | b) & +(c | d | e);

EBNF 式の右辺( rhs )の型と機能は、任意に複雑なものになり得るが、これは a_rule という名前のルールにエンコードされる。 a_rule は今や文法のあらゆるところで参照されることができる:

    rule<> another_rule = f >> g >> h >> a_rule;
ルールの参照

どこであれ EBNF 式の右辺でルールが参照されたときは、そのルールはその式に参照で保持される。 参照されたルールをスコープ内にとどめ、参照されている間は破壊されてしまわないよう保証するのはクライアントの責任である。
    a = int_p;
    b = a;
    c = int_p >> b;

先行宣言

BNF宣言でよく見られるような循環構造を許可するために、ruleは定義より先に宣言されることがある。例:

    rule<> a, b, c;

    a = b | a;
    b = c | a;

再帰

ルールの右辺は、それ自身を含む他のルールから参照されることがある。 制限は、直接または関節の左再帰は許されないということである(これはチェックされない実行時エラーであり、無限ループを引き起こす)。これはトップダウン型のパーサであることによる。例:

    a = a | b; // 無限ループ!
左再帰とは?

左再帰は、他の何かの前にそれ自体を呼び出すルールがあるとき起こる。 これが起きると、トップダウン型のパーサは無限ループに陥る。 左再帰をどのように予測するかについての詳細は FAQ を参照のこと。

未定義のルール

未定義のルールは何にもマッチせず、意味的には nothing_p と等価である。

再宣言

C++ の他のあらゆる代入と同様、ルールへの二度目の代入は破壊的であり、そのルールを再定義する。 古い定義は失われる。 ルールは動的であり、いつでも定義を変更できる:

    r = a_definition;
    r = another_definition;

二つ目の代入が行われた際に、ルール r は古い定義を失う。 すでに述べたとおり、未定義のルールは何にもマッチせず nothing_p と意味的に等価である。

動的なパーサ

宣言的な EBNF を命令法(imperative)の C++ に宿すことは、興味深い混合物を生み出す。 我々は両方の世界の最良の部分を持っている。 ifelse ステートメントのような命令法の構造物を用いて、 実行時に簡便に文法を修正することができる。例えば:

    if (feature_is_available)
        r = add_this_feature;

ルールは根本的に動的なパーサである。動的パーサの特徴は、その振る舞いを実行時に修正できることにある。 初期状態ではルールは未定義でいずれにもマッチしない。あらゆる時点で、そのルールを定義および再定義することで、動的に振る舞いを切り替えられる。

開始ルールなし

一般に、構文解析器は開始記号と呼ばれるものを持っている。これは構文解析の始端となる文法の根として選ばれたものである。 Spirit パーサフレームワークは開始記号という概念を持たない。 あらゆるルールが開始記号に成りうる。 この機能はパーサのステップ単位での作成を促す。 それぞれのレベルやモジュール単位で完全にテストされたパーサを、 最上位のレベルに至るまでボトムアップから組み立てることができる。

parser_id

各ルールは parser_id 型の ID を持っている。 それぞれのルールのデフォルトの ID はそのルールのアドレスが(整数に変換されて)セットされている。 比較のためにルールのアドレスを得る事が常に可能である訳ではないので、 これは常に最も使いやすいというわけではない。 そこで、ルールに set_id(parser_id) を呼び出して、各ルールで使われているデフォルトの ID を上書きできる。 例:

    a_rule.set_id(123); //  a_rule の ID を 12 に設定する


このドキュメントの対象: Boost Version 1.30.0
最新版ドキュメント(英語)