エラー処理

C++ の例外処理機構はこのフレームワークのエラー処理に完璧にマッチする。 完全なパーサを迷路だと想像して欲しい。 それぞれの分岐で、入力がどちらに曲がるか指示する。 エラーのある入力が与えられると、行き止まりに行き着く。 そこに行き当たると、来たところから引き返すための時間が無駄になる。 そうならないように、戦略的な地点に番兵(guard)を置く。 特定の地点の向こう側に、行くのを許可されない所々にパーサアサーションを置く。

アサーションは我々を番兵(guard)へと投げ飛ばすバネのようなものである。特定の入力パターンに相当する壁に行き当たると、 全ては素早く解きほぐされ、番兵(guard)のすぐ後ろに投げ飛ばされる。 賢く使えばこれは非常に効果的な最適化となり得る。 番兵のすぐ後ろで、可能なら、状況を修正する機会を得る。 次の図は、そのシナリオを描いている。

パーサエラー

parser_error クラスは Spirit で用いられる汎用のパーサ例外クラスである。これはすべてのパーサ例外の基底クラスである。

    template <typename ErrorDescrT, typename IteratorT = char const*>
    class parser_error 
    {
    public:
                            parser_error(IteratorT where, ErrorDescrT descriptor);
        IteratorT const     where;
        ErrorDescrT const   descriptor;
    };

この例外はエラーが見つかった場所のイテレータポジションをメンバ変数 where に保持する。イテレータに加え、 parser_error はそのエラー(エラー記述子)についての情報をメンバ変数 descriptor に保持する。

セマンティックアクションは必要に応じて自由にパーサ例外を投げることができる。 ユーティリティ関数 throw_ が呼ばれるだろう。 この関数はイテレータとエラー記述子を与えると parser_error を作成し、投げる:

    template <typename ErrorDescrT, typename IteratorT>
    void throw_(IteratorT where, ErrorDescrT descriptor);

パーサアサーション

アサーションは構文解析に成功すること以外の選択肢を持たないような場所に置かれる。 もし構文解析に失敗すれば、特定の型の例外が投げられる。

アサーションオブジェクトは文法より先に宣言される。 assertion は、そのアサーションに失敗した時に投げられるエラーの型によってパラメータ化されたテンプレートクラスである。 次のアサーションはユーザが定義した列挙子 Error によってパラメータ化されている。

    enum Errors
    {
        program_expected,
        begin_expected,
        end_expected
    };

    assertion<Errors> expect_program(program_expected);
    assertion<Errors> expect_begin(begin_expected);
    assertion<Errors> expect_end(end_expected);

この例ではエラーに関する情報を保持するために列挙子を使っているが、整数や文字列など他の型も自由に使うことが出来る。例えば assertion<string> はその情報として文字列を受け取る。 しかし軽量なオブジェクトを用いるのが賢明だろう。どのみちエラー記述子はふつう静的なものだからだ。 列挙子はエラーハンドラが検出するのに便利で、 C++ は enum を独自の型として扱うので容易に catch で捕まえることが出来る。

assertive_parser

実際には、式 expect_end(str_p("end")) は assertive_parser オブジェクトを作成する。 assertive_parser は構文解析処理に失敗した際の応答として例外を投げる。 assertive_parser はパーサが入力の一致判定に失敗したことを知らせるために、 マッチに成功しなかったことを返す代わりに parser_error 例外を投げる。 構文解析処理の間、パーサは IteratorT 型のイテレータを与えられる。 これは、そのアサーションのエラー記述子型 ErrorDescrT (この場合は列挙子 Errors )と組み合わせられる。 両者は parser_error<Errors, IteratorT> を作成するのに使われ、その後、例外を通知するために投げられる。

宣言済みアサーションオブジェクトである expect_end は、いまやパーサのラッパとして文法で使われることができる。例えば:

    expect_end(str_p("end"))

これは入力から "end" を読むことに失敗すると例外を投げる。

番兵(guard)

guard は特定の型の parser_error を捕らえるために用いられる。 番兵は典型的にはちょうどアサーションと同じようにあらかじめ宣言されている。 前述の例を拡張すると:

    guard<Errors> my_guard;

この例の Errors は検出しようとしているエラー記述子型である。 これは上記の enum と同じものである。 my_guard はいまや文法の宣言で使うことが出来る:

    my_guard(p)[error_handler]

ここで p はパーサを評価する式である。 p の内部のどこかで、パーサはパーサ例外を投げることがある。 error_handler はエラーハンドラであり、以下のインタフェースと互換性のある関数またはファンクタである:

    error_status<T>
    f(ScannerT const& scan, ErrorT error);

scan は構文解析前のスキャナの状態を指しており、 error が発生したエラーである。ハンドラは適切と思われる位置にスキャナを移動させることができ、可能ならばエラーの訂正を試みる。 ハンドラはその後 error_status<T> オブジェクトを返さなければならない。

fallback_parser

my_guard(expr, error_handler) は fallback_parser オブジェクトを作成する。 fallback_parser は特定の型の parser_error 例外を処理する。 my_guardguard<Errors> と宣言されているので、 fallback_parser は Errors が指定したパーサエラー parser_error<Errors, IteratorT> を捕らえる。 このクラスは try ブロックを準備する。例外が捕らえられると、 catch ブロックが error_handler を呼び出す。

error_status<T>

    template <typename T = nil_t>
    struct error_status
    {
        enum result_t { fail, retry, accept, rethrow };

        error_status(
            result_t result = fail, 
            int length      = -1, 
            T const& value  = T());
        
        result_t    result;
        int         length;
        T           value;
    };

ここで Tfallback_parser の対象のマッチ属性と互換性がある属性型である(デフォルトは nil_t )。 クラス error_status はエラーハンドラの結果を報告する。 この結果は次のいずれかである:

error_status result
fail 失敗し、中断した。 no_match を返す
retry エラーからの復帰を試み、場合によってはスキャナを移動させた
accept 強制的に成功させた。一致した長さを返し、スキャナを適切な位置に移動させ、属性値を返す
rethrow エラーを再送する




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