詳細:パーサ

Spirit を動かしているものは何か。その詳細を見ていこう。 パーサクラスはフレームワークで最も基礎的な存在である。 基本的に、パーサは first-last イテレータの組からなるスキャナを受け取り、 一致判定オブジェクトを結果として返す。 イテレータは、その時構文解析されているデータを切り出す。 一致判定オブジェクトは、構文解析が成功すれば真と評価され、 その場合、入力はそれに応じて前方に進められる。 各々のパーサは特定のパターンやアルゴリズムを表現できるパーサであるか、 あるいは他のパーサのコンポジションで形成されたより複雑なパーサである。

あらゆるパーサは基底テンプレートクラス parser から派生する:

template <typename DerivedT>
struct parser
{
    /*...*/

    DerivedT& derived();
    DerivedT const& derived() const;
};

このクラスはあらゆるパーサのプロトコル基底クラスである。 これは基本的にインタフェース規約である。 パーサクラスはどのように何かを構文解析するか、実際には知らず、 実際に構文解析を行うテンプレートパラメータ DerivedT に依存する。 メタプログラミング界隈では、このテクニックは"奇妙に再帰するテンプレートパラメータ(Curiously Recurring Template Pattern)"として知られている。 この継承戦略は仮想関数のオーバーヘッドなしにポリモーフィズムの威力をもたらす。 要するに、これはコンパイル時ポリモーフィズムを実装する方法の一つである。

parser_category_t

それぞれのパーサはそのカテゴリを定義する parser_category_t 型を持っている。 デフォルトでは、もし一つも指定されなければ、 その parser_category_t を plain_parser_category として typedef する基底パーサクラスから継承する。 異なる型のパーサを区別するため、いくつかのテンプレートクラスが定義されている。 以下に挙げるのはもっとも一般的なカテゴリである。 より限定的な型はこれらから継承される。

Parser categories
plain_parser_category 素のパーサ
binary_parser_category 対象 a および b を持つパーサ(例.選択)
unary_parser_category 単一の対象を持つパーサ(例. Kleene Star)
action_parser_category セマンティックアクションを取り付けられたパーサ
    struct plain_parser_category {};
    struct binary_parser_category       : plain_parser_category {};
    struct unary_parser_category        : plain_parser_category {};
    struct action_parser_category       : unary_parser_category {};

embed_t

各パーサは embed_t を typedef している。 この typedef は、パーサがコンポジットパーサの中にどのように埋め込まれるかを定義する。 デフォルトでは、もし一つも定義されなければ、パーサは値によって埋め込まれる。 つまり、パーサの複製がコンポジットパーサのメンバ変数として置かれる。 ほとんどのパーサは値で埋め込まれる。 しかしある特定の状況では、これは望むものではなかったり、不可能であったりする。 一つの適切な例はルールである。 ルールは他のパーサと異なり、参照で埋め込まれている。

マッチ

マッチは構文解析の結果を保持する。 一致判定オブジェクトは、一致した箇所が見つかった時に真と評価され、それ以外では偽となる。 一致部分の長さは、一致した部分の文字(あるいは語句)の数である。 これは length() メンバ関数によって問い合わせることが出来る。 負の値は一致判定に失敗したことを意味する。

各々のパーサは関連づけられた属性を持つことがある。 構文解析に成功した場合、この属性も一致判定オブジェクトを通じてクライアントに返される。 一致判定オブジェクトの value() メンバ関数はこの値への参照を返す。

match クラス:

    template <typename T>
    class match
    {
    public:

        /*...*/

        typedef T attr_t;
operator bool() const; int length() const; T& value() const; T const& value(); };

match_result

パーサがその結果として一致判定オブジェクトを返すと繰り返し述べた。 これは単純化した話である。 実際には、ジェネリックプログラミングの力によって、 実はパーサは一致判定オブジェクトを返すように直書きされているわけではない。 より正確に言えば、パーサは概念的なインタフェースを順守したオブジェクトを返す。 match はその一例である。 いずれにせよ、 実際には match オブジェクトやその派生型やあるいは総合的に無関係な型であるとしても、 パーサの結果の型を一致判定オブジェクトと呼ぶことにする。

メタ関数

メタ関数とは何か。 誰もが関数がどのように見えるかは知っている。 最も簡潔に言えば、関数は引数を受け取り、結果を返すものである。 誰もが心から愛する関数は次のようなものだ:

int identity_func(int arg)
{ return arg; } // 引数 arg を返す

メタ関数も根本的には同じものである。 これらの獣も引数を受け取り結果を返す。 しかし、関数が実行時に値に対して動作する一方で、 メタ関数はコンパイル時に型に対して動作する(あるいは定数に対しても。だがここでは型だけを扱うことにする)。 メタ関数はテンプレートクラス(あるいは構造体)である。 テンプレートパラメータはメタ関数の引数であり、 そのクラスの中の typedef はそのメタ関数の戻り値型である。 先程の関数に対応するメタ関数は次のようになる:

template <typename ArgT>
struct identity_meta_func
{ typedef ArgT type; } // 引数 ArgT を返す

上記のメタ関数は次のように呼び出される:

typename identity_meta_func<ArgT>::type

慣例的に、メタ関数はその結果を typedef type で返す。 typename はテンプレートの中でのみ必要とされる点に注意して欲しい。

実際にパーサに使用される一致判定型は二つの型に依存する: パーサの属性型とスキャナ型である。 match_result は、与えられた属性型とスキャナ型が要求する一致判定型を返すメタ関数である。

使い方:

    typename match_result<ScannerT, T>::type

基本的には、このメタ関数は"スキャナ型 ScannerT と属性型 T が与えられたとき、 要求される一致判定型は何か?"という問いに答えるものである [ typename はテンプレートの中でのみ必要]。

メンバ関数 parse

parser を継承した具象サブクラスは、対応するメンバ関数 parse(...) を持たなければならない。 このメンバ関数は次の概念的なインタフェースと互換性がある:

    template <typename ScannerT>
    RT
    parse(ScannerT const& scan) const;

ここで RT は要求されたパーサの戻り値型である。

パーサの結果

parser を継承した具象サブクラスは、多くの場合、ネストされたメタ関数 result を持つ必要がある。 このメタ関数は、スキャナ型が与えられると、パーサの parse メンバ関数の結果の型 type を返す。 このメタ関数は次の形式である:

    template <typename ScannerT>
    struct result
    {
        typedef RT type;
    };

ここで RT は要求されたパーサの戻り値型である。 これはたいていの場合、常にではないが、テンプレートパラメータ ScannerT に依存する。 例えば、属性型 int が与えられた時、 match_result メタ関数を次のように使うことができる:

    template <typename ScannerT>
    struct result
    {
        typedef typename match_result<ScannerT, int>::type type;
    };

もしパーサがメタ関数 result を供給しないなら、 parser 基底クラスがデフォルトのものを提供する。 デフォルトは次のように定義されている:

    template <typename ScannerT>
    struct result
    {
        typedef typename match_result<ScannerT, nil_t>::type type;
    };

result メタ関数なしでやるなら、パーサのデフォルト属性が nil_t であること(つまり、そのパーサは属性を持たないということ)に注意すること。

parser_result

スキャナ型 ScannerT とパーサ型 ParserT が与えられると、 パーサの実際の結果は何になるのだろうか? この質問への答えは parser_result メタ関数によって与えられる。

使い方:

    typename parser_result<ParserT, ScannerT>::type

一般には、このメタ関数は、ただ parser の result メタ関数を呼び出しに送るだけである:

    template <typename ParserT, typename ScannerT>
    struct parser_result
    {
        typedef typename ParserT::template result<ScannerT>::type type;
    };

これはメンバ関数を呼び出すグローバル関数と同じである。 多くの場合、上記の使い方は以下と等価である:

    typename ParserT::template result<ScannerT>::type

ただし、これがあらゆる場合に当てはまると信頼するべきではない。 特殊なパーサおよび/あるいはスキャナ型に対しては parser_result メタ関数が特殊化されることがあるからである。

parser_result メタ関数は、必要とされる parse メンバ関数のシグネチャを概ね標準化している:

    template <typename ScannerT>
    typename parser_result<self_t, ScannerT>::type
parse(ScannerT const& scan) const;

ここで self_t はそのパーサへの typedef である。

parser クラスの宣言

    template <typename DerivedT>
    struct parser
    {
        typedef DerivedT                embed_t;
        typedef DerivedT                derived_t;
        typedef plain_parser_category   parser_category_t;

        template <typename ScannerT>
        struct result
        {
            typedef typename match_result<ScannerT, nil_t>::type type;
        };

        DerivedT& derived();
        DerivedT const& derived() const;

        template <typename ActionT>
        action<DerivedT, ActionT>
        operator[](ActionT const& actor) const;
    };


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