言語ゲーム

とあるエンジニアが嘘ばかり書く日記

Twitter: @propella

逆転パーサー 4

何となく何が足りてないかはっきりして来ました。まず言葉の定義から。

  • 読み込みとは、バイナリ列 (例 [0 1 0 1 1 ...]) を入力しオブジェクト(例: "Hello") を出力する事。
  • 書き込みとはオブジェクトを入力し、バイナリ列を出力する事。
  • ルールから、読み込み関数と書き込み関数の二つが生成される。

まず、パターンとルールを次のように定義します。出来るだけシンタックスシュガーを使わないで prolog っぽい文法を使います。

  • パターンとは、パターン名(引き数, 引き数, ...) の形をしている。パターン名は小文字、引き数は大文字で書く。
  • 入力時、パターンは入力がマッチすると引き数に値を束縛する。
  • 出力時、パターンは引き数に応じてデータを構築して出力する。
  • パターンがリストの時、パターン パターン ... のように空白で区切ったやつはパターンの連接になる(append)。
  • パターン | パターンリスト のように縦棒で区切った奴はパターンリストの先頭にパターンを追加する(cons)
  • e は空リスト
  • ! はルール全体を失敗させる。
  • ルールとは、パターン := パターン の形をしている。左辺から右辺へ書き込む、もしくは右辺から左辺へ読み込むという意味。
  • ルール / ルールのように / で区切った奴もルール。最初のルールが失敗すると次のルールが試行される。改行があるとき / は省略可能。
  • 読み込みとは、ルールの右辺を入力して左辺を出力する事。
  • 書き込みとは、ルールの左辺を入力して右辺を出力する事。

例えば組み込みパターン bit(Value) はこんな性質を持っています。

  • 入力時: bit(Value) は入力が 0 なら 0 を、入力が 1 なら 1 を Value に束縛。
  • 入力時: bit(0) は入力が 0 なら成功、1 なら失敗。bit(1) は入力が 0 なら失敗、入力が 1 なら成功。
  • 出力時: bit(Value) は Value を出力。

ただ、入出力の先は最終的にはビット列なので、普通このままでは使わず、リストにします。リストにするには、| や many(後述) を使います。

  • 入力時、bit(B0) | bit(B1) | bit(B2) | bit(B3) | e は四ビット読み込んでそれぞれ B0 .. B3 に束縛する。
  • 出力時、bit(B0) | bit(B1) | bit(B2) | bit(B3) | e は B0 .. B3 の値に応じて 4 ビット出力する。

組み込みパターン many(Size, Type/1, Element) はこんな性質を持っています。

  • 入力時、Size が束縛されていれば many(Size, Type/1, Value) は Type を Size 回読んで Value に束縛。
  • 入力時、Size が束縛されていなければ many(Size, Type/1, Value) は Type を0回以上出来るだけ読んで Size と Value に束縛
  • Type/1 は引き数が一つのルール

これを使ってビット列 bits、バイト byte、可変長バイト列 bytes を定義出来ます。

bits(Size, Bits)   := many(Size, bit, Bits)
byte(Bits)         := bits(8, Bits)
bytes(Size, Bytes) := byte(Size) many(Size, byte, Bytes)

次に、C 文字列を考えます。

char(_)           := byte(0) !
char(Value)       := byte(Value)
cstr(Size, Chars) := many(Size, char, Chars)

swf 長方形オブジェクトはこんな感じ。

rectangle(Nbits, Xmin, Xmax, Ymin, Ymin) = bits(5, Nibts)
		       	     	   	   bits(Nbits, Xmin)
		       	     	   	   bits(Nbits, Xmax)
		       	     	   	   bits(Nbits, Ymin)
		       	     	   	   bits(Nbits, Ymax)

ここで、前回書き込み時に Nbits の値をどうやって決めたら良いか分からないという事を書きましたが、今のアイデアは、パターンの実装をカスタマイズ出来るようにするという物です。例えば、何も言わないとパターン rectangle の実装はただのタプルになるので整合性が崩れてエラーが起こる可能性がありますが、セッターとゲッターを適切に作って適切な Nbits を他の引き数から計算で出すようにすればカプセル化が守られます。

またここで、逆パーサーにおけるシンタックスとセマンティクスの違いをはっきり出来ます。つまり、逆パーサー文法で定義出来るのはシンタックスだけで、ようするにビット列を並び替えてタプルを作るだけです。その並び替えたビット列に意味を持たせる。のはホスト言語の役目です。例えばパターン many は引き数を解釈して解析の流れを変えるので、逆パーサーの文法の範囲で定義する事は出来ません。一方で byte の定義は 8 ビット分まとめるだけなので逆パーサで定義出来ます。

ホスト言語によって、many のように組み込みの動作を追加するか、rectangle のように追加の制約を作れるようにしたいと思います。

週末をこんなわけのわからん事に費やしてしまった。。。