vm2gol v2 (60) stmt をそのまま渡す / (61) リファクタリング



step 60

stmt_rest ではなく stmt を渡す

前々から気にはなっていたけど後回しにしていたものを片付けるシリーズです。

例として gen_call を見てみます。

def gen_call(fn_arg_names, lvar_names, stmt_rest)
  fn_name, *fn_args = stmt_rest
  # ...
end

# 呼び出し側
def gen_stmt(fn_arg_names, lvar_names, stmt)
  stmt_head, *stmt_rest = stmt

  case stmt_head
  when "call"
    gen_call(fn_arg_names, lvar_names, stmt_rest)
  # ...

stmt (文に対応するデータ)の先頭の要素は文の種類を表しているわけですが、 「これは○○文だな」と判断するためにその先頭要素を使っているのは呼び出し側であって、 呼び出した先では不要です。 不要なものは渡さなくていいんじゃないか、という(ぼんやりした)理由により このような作りになっていました。

しかし、だんだん不都合の方が気になってきました。

  • stmt_rest ってなんやねん問題
    • 「先頭の要素を除いた残りです」と説明すれば済むので、大して難しい話ではない。ないんだけど、その説明すらも不要にできれば、より簡素にできて良い。
  • 移植性

というわけで、先頭を除いた残りだけ渡すのをやめ、 文のデータ全体をそのまま渡すようにしました。 渡された側では単純に先頭の要素を無視することに。

def gen_call(fn_arg_names, lvar_names, stmt)
  _, *funcall = stmt
  # ...
end

def gen_stmt(fn_arg_names, lvar_names, stmt)
  case stmt[0]
  when "call"
    gen_call(fn_arg_names, lvar_names, stmt)
  # ...

○○文を処理するメソッドに○○文のデータをそのまま渡すようになりました。やっぱりこの方が単純で分かりやすいように思います。

トップレベルでのVMコメントの廃止

トップレベルで _cmt を使えるようになっていましたが、そうする必要がなかったので廃止。

==, != をそのまま使う

パース時にそれぞれ eq, neq に変換していましたが、 あらためて考えると不要な変換と思えたので +, * と同様に ==, != とするように。

さらにリファクタリングすれば parse_expr_right をコンパクトにできる状態になっていますが、今回の差分が分かりにくくなるので次回に先送りします。

Token#kind にリネーム

Token#type から Token#kind に変更しました。

他の処理系や解説などを見ていると、これは kind という名前にしているものが多いようなんですよね。 いわゆる型と混同しないように type という名前を避けているのか、単に慣習的なものなのか、はたまた英語的に kind の方が適切なのか……みたいな推測をしてますが、はっきりした理由は把握していません。

step 61

気になっていた細かい部分をまとめて修正しました。 リファクタリングのみなのでページを分けずに続けて書きます。

step 61 の差分はこちら


変更後の行数はこんな感じ。

$ LANG=C wc -l vg*.rb
   66 vgasm.rb
  381 vgcodegen.rb
   58 vglexer.rb
  380 vgparser.rb
  447 vgvm.rb
 1332 total

二項演算子まわりの整理

step 60 の変更により変換処理が不要になったので case を削除。

修正前:

def parse_expr
  expr = _parse_expr_factor()

  while binary_op?(peek())
    op =
      case peek().value
      when "+"  then "+"
      when "*"  then "*"
      when "==" then "=="
      when "!=" then "!="
      else
        raise ParseError, "must not happen"
      end
    $pos += 1

    expr_r = _parse_expr_factor()
    expr = [op.to_sym, expr, expr_r]
  end

  expr
end

修正後:

def parse_expr
  expr = _parse_expr_factor()

  while binary_op?(peek())
    op = peek().value
    $pos += 1

    expr_r = _parse_expr_factor()
    expr = [op.to_sym, expr, expr_r]
  end

  expr
end

すっきりしました。

vgcodegen.rb にリネーム

cg という名前を見て code generator の略だとすぐ分かる人はいなさそう、このように略している例を見ない、ということで、一般的ですぐ分かる名前に変更。

変数名・メソッド名などのリネーム、コメントの整理

気になっていた箇所をまとめて修正。