- 目次ページに戻る / 前 / 次
- 前回からの差分をまとめて見る
パーサが結構いいかげんなので、直します。
引数のパースの厳密化
一番適当なのが引数のパースです。
現状だと my_func(1 2 a)
のように区切りのカンマがなくても文法エラーになりません。
というわけで、 1つ目の引数と2個目以降の引数をパースする下請けメソッドをそれぞれ用意し、 次のように書き直しました。
def _parse_arg t = peek() if t.type == :ident @pos += 1 t.value elsif t.type == :int @pos += 1 t.value else raise ParseError end end def _parse_args_first return nil if peek().value == ")" _parse_arg() end def _parse_args_rest return nil if peek().value == ")" consume(",") _parse_arg() end def parse_args args = [] first_arg = _parse_args_first() if first_arg.nil? return args else args << first_arg end loop do rest_arg = _parse_args_rest() if rest_arg.nil? break else args << rest_arg end end args end
2個目以降の引数は カンマとセットにして見ていく形です。 BNF っぽく書くと下記のような感じでしょうか。
args: 引数なし | arg (',' arg)*
これでカンマがない場合にエラーにできるようになりました。
※ 追記: 細かくメソッドを分けすぎたので ステップ58 であらためて整理しました。
他に似たように反復になっている構文をチェックしてみると……
stmts
(文の連なり)と
case文の when句の部分が似ているのですが、
これらの場合は引数リストのカンマに当たる区切りトークンが必要ないため修正不要です。
peek()
上記のコードにも含まれてしまっていますが、
現在のトークンを取得するためにこれまで @tokens[@pos]
としていたところを、
peek()
というメソッドに置き換えました。
出現頻度が高めなのでメソッド化したい気持ちはありつつも
current_token
とかだとちょっと野暮ったいな……と思っていたところ、
こういう場合は peek という名前が使われるらしいと分かったので今回採用しました。
この peek という単語は「(一部だけを)ちらっと見る・覗き見る」のような意味だそうです。 「流行のピーク」などという場合は peak で、これは別の単語。
その他
- パース中に例外が発生した場合、その場で
dump_state()
を呼ぶのをやめて 大元に集約 - when句のないcase文を文法エラーとみなすようにした (parse_case)
- refactor: when句の処理を _parse_when_clause() に抽出 (Parser)
- vgcg.rb
vram[...]
のマッチ処理をメソッドに抽出して共通化