コンフィクスパーサ

コンフィクスパーサ

コンフィクスパーサは三つの独立した要素:開き(opening)、式、 閉じ(closing)からなるシーケンス(ネストすることもある)を認識する。 このユーティリティパーサが構文解析に利用される構造では、 開き(opening)が式に含まれていてもよく、 またシーケンス全体は最初の開き(opening)サブシーケンスにマッチする閉じ(closing)部分を見た後でのみマッチする。 簡単な例はネストした PASCAL のコメントである:

    { これは { ネストした } PASCAL のコメントである }

これは次のルール定義によって構文解析されることができる:

    rule<> pascal_comment_rule
        =   confix_nest_p('{', *anychar_p, '}')
        ;

ネストしないコンフィクスパーサが必要なら、要求されたコンフィクスパーサを生成するために confix_p パーサジェネレータが使われる。 もし式部分が有効なコンフィクスシーケンスを含んでいる (コンフィクスシーケンスがネストを許す)なら、 confix_nest_p パーサジェネレータテンプレートはそのパーサの生成に用いられる。 confix_pconfix_nest_p へ渡される三つのパラメータは単一の文字(上述)か文字列、あるいはより複雑な構文解析ロジックが必要なら任意のパーサを用いることが出来る。このいずれも構文解析に必要な対応するパーサ型へと自動的に変換される。

生成されたパーサは以下のルールと等価である:

    open >> (expr - close) >> close

もし expr パーサが action_parser_category 型パーサ(セマンティックアクションが接続されたパーサ)であれば、 特別なことをしなければならない。 これが起こると、もしユーザが何か次のように書くと:

    confix_p(open, expr[func], close)

ここで expr はコンフィクスシーケンスの expr にマッチするパーサである。 また funcexpr にマッチした後に呼び出されるファンクタである。 もし何もしなければ、その結果のコードは次のようなシーケンスを構文解析する:

    open >> (expr[func] - close) >> close

これは多くの場合、ユーザが期待するものではない(もしこれ期待するものであれば、 パーサのリファクタリングを禁止する confix_p または confix_nest_p ジェネレータ関数 direct() を使うこと)。 コンフィクスパーサが期待するように振る舞うようにするには以下のようにする:

    open >> (expr - close)[func] >> close

expr パーサに付与された actor は (expr - close) パーサに再び付与される。 これによって結果のコンフィクスパーサは '正しいことをする' 。 このリファクタリングはリファクタリングパーサの助けを受けて行われる。 もし expr パーサが次のような unary_parser_category 型パーサの場合であれば、

    confix_p(open, *anychar_p, close)

さらに特別な配慮が必要である。何のリファクタリングも施さなければ、その結果は

    open >> (*anychar_p - close) >> close

となって、期待した結果は得られない( *anychar_p は入力ストリームの終端に至るまですべての入力を消費し尽くしてしまうだろう)。 そこで以下のようにリファクタリングする:

    open >> *(anychar_p - close) >> close

得られるのは正しい結果である。

 expr パーサがこの注意すべき二つの問題の組み合わせである (すなわち、 expr パーサがアクションの付与された引数一つのパーサである)場合も同様に処理され、その為:

    confix_p(start, (*anychar_p)[func], close)

は期待通りに構文解析される:

    open >> (*(anychar_p - end))[func] >> close

必要なリファクタリングはここでもリファクタリングパーサの助けで実装されている。

コンフィクスパーサのリファクタリングのまとめ
記述したコード: リファクタリングされたコード:
confix_p(open, expr, close)

start >> (expr - close) >> close

confix_p(open, expr[func], close)

start >> (expr - close)[func] >> close

confix_p(open, *expr, close)

start >> *(expr - close) >> close

confix_p(open, (*expr)[func], close)

start >> (*(expr - close))[func] >> close

コメントパーサ

コメントパーサジェネレータテンプレート comment_pcomment_nest_p は、任意のパラメータから正しいコンフィクスパーサを生成するためのヘルパーである。生成されたパーサは以下のような構造のコメントを構文解析できる:

    コメント開始トークン >> コメント本文 >> コメント終了トークン

パラメータとして以下の型がサポートされている: パーサ、単独の文字および文字列( as_parser を参照)。 二つの異なる定義済みコメントパーサジェネレータ comment_pcomment_nest_p が存在する: これらは特殊なコメントパーサを二つの異なる方法で作成するために使われる。 これらが一つのパラメータで用いられると、与えられた最初のパーサパラメータで始まるコメントは その行の終端までマッチする。 その為、例えば以下のパーサは C++ 形式のコメントにマッチする:

    comment_p("//")

もし二つのパラメータで用いられた場合、最初のパーサパラメータで始まり二つ目のパーサパラメータまでのコメントがマッチする。例えば C 形式のコメントパーサは以下のように構築される:

    comment_p("/*", "*/")

comment_p パーサジェネレータは ネストしないコメント( C / C++ コメントのような)にマッチするパーサを生成できるようにする。 時々、例えば Pascal のようにネストしたコメントを構文解析する必要があることがある。 そのようなネストしたコメントは comment_nest_p ジェネレータテンプレートファンクタによって生成されたパーサを通じて構文解析することが出来る。 次の例は、二つの異なる(ネストできる) Pascal のコメント形式を構文解析するために用いられるパーサを示している:

    rule<> pascal_comment
        =   comment_nest_p("(*", "*)")
        |   comment_nest_p('{', '}')
        ;

コメントは暗黙のうちに、 comment_p(...) ステートメント全体が lexeme_d[] ディレクティブの中に埋め込まれているかのように 構文解析される、という点に注意して欲しい。 つまり、 もし構文解析プロセス全体に対してスキップパーサを定義していたとしても、 コメントを構文解析している間、トークンのスキップは起こらない。



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