ループ

ここまでに、ループを扱ういくつかの EBNF 演算子を紹介してきた。 正符号演算子 + は先行するシンボルの1回かそれ以上の繰り返しにマッチし、 Kleene star * は先行するシンボルの0回かそれ以上の繰り返しにマッチする。

これをさらに進めて、一般化されたループ演算子が欲しくなるところだろう。 ものによってはやりすぎに見えるかも知れない。 だが、中には基本的な EBNF の繰り返し構文で定義するには、もしそれが可能だとしても、実用的でなく煩わしくなってしまう文法もある。 例えば:

最大で255文字までのファイル名。
指定されたビットマップファイルフォーマットは きっちり4096個のRGBカラー情報を持つ。
32ビットバイナリ列(1〜32個の1か0)。

フレームワークは、Kleene star * 、正符号クロージャ + 、および 省略可能 ! の他に、ループのためのより柔軟な機構を提供する。

ループの構築
repeat_p (n) [p] p を正確に n 回繰り返す
repeat_p (n1, n2) [p] p を少なくとも n1 回かつ最大で n2 回繰り返す
repeat_p (n, more) [p] p を少なくとも n 回、pが失敗するか入力が尽きるまで継続して繰り返す

repeat_pパーサを用いることで、今や上記の例を次のように書くことが出来る:

最大で255文字のファイル名:

    valid_fname_chars = /*..*/;
    filename = repeat_p(1, 255)[valid_fname_chars];

指定したビットマップファイルフォーマットは4096個のRGBカラー情報を持つ:

    uint_parser<unsigned, 16, 6, 6> rgb_p;
    bitmap = repeat_p(4096)[rgb_p];

32ビットバイナリ列(1〜32個の1または0)については、勿論 bin_p 数値パーサを替わりに用いて容易に構築できる。しかしここでは説明のためにループパーサを用いて次のようにする:

    bin32 = lexeme_d[repeat_p(1, 32)[ch_p('1') | '0']];
ループパーサは実行時にパラメータ化される。

ループパーサは動的なものにできる。 長さが前置された Pascal 形式の文字列、すなわち最初の1バイトがそれに続く文字列の長さを指定するような文字列のバイナリファイルを構文解析することを考えてみよう。 以下は入力の例である:

11
h
e
l
l
o
_
w
o
r
l
d

この単純明快な例は伝統的な EBNF では事実上定義できない。 いくつかの EBNF 構文は Kleene star より強力な繰り返し構造を許すにもかかわらず、 我々は未だに固定された文字列の構文解析処理に制限されている。 EBNF の性質は繰り返し因子が一定であるように強要する。 一方で、 Spirit は実行時に繰り返し因子が可変であることを許している。 上記の入力文字列を受け付ける文法を次のように書くことが出来る:

    int c;
    r = anychar_p[assign(c)] >> repeat_p(boost::ref(c))[anychar_p];

次の式

    anychar_p[assign(c)]

は、入力の最初の文字を取り出し、それを c に入れる。 興味深いことに、 repeat_p のパラメータとして、次のように定数だけでなく変数を用いることが出来る。

    repeat_p(boost::ref(c))[anychar_p]

整数 c を参照するために boost::ref を用いている点に注目して欲しい。 この repeat_p の使い方はパーサが繰り返し因子の評価を実際に必要になるまで遅らせる。 例を続けると、値11は既に入力から取り出されているので、この repeat_p は今やきっちり11回繰り返すと期待される。



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