vm2gol v2 (47) 引数のパースの厳密化など



パーサが結構いいかげんなので、直します。

引数のパースの厳密化

一番適当なのが引数のパースです。 現状だと 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[...] のマッチ処理をメソッドに抽出して共通化