vm2gol v2 (45) リファクタリング(主にVM)



今回は(主に VM の)リファクタリングです。 テストが用意できたので、安心してサクサクと進められます。

Rubocop 関連

Rubocop の設定が古くなってると言われたので設定を修正して、新たに指摘された箇所を修正。

--- a/vgparser.rb
+++ b/vgparser.rb
@@ -403,7 +403,7 @@ def tokenize(src)
       tokens << Token.new(:int, str.to_i)
       pos += str.size
 
-    when /\A(==|!=|[\(\)\{\}=;\+\*,])/
+    when /\A(==|!=|[(){}=;+*,])/
       str = $1
       tokens << Token.new(:symbol, str)
       pos += str.size

Rubocop の指摘(Style/RedundantRegexpEscape)に従って修正した箇所の例です。 こういうのは調べるのがめんどくさくて「とりあえず怪しいのはエスケープしとくか」 となりがちなので、指摘してもらえるのはありがたいですね。

Vm#num_args_for を改良

(10) ステップ実行 / ダンプ表示の改善 の追記で書いていた件です。

diff を見やすくしたいというのが主な目的だったので今さらな感じはしますが。

class Vm
  # ...

  NUM_ARGS_MAP = {
    "cp"       => 2,
    "set_vram" => 2,
    # ...
    "compare" => 0,
    "mult_ab" => 0
  }

  # ...

  def self.num_args_for(operator)
    NUM_ARGS_MAP.fetch(operator)
  end

reg_c を削除

なんとなく残していましたが、使ってないのでやっぱり消しました。 併せて add_ac 命令も削除。

Vm#execute を整理

この長い case 式の部分です。

  def execute
    # ...

    case op
    when "exit"
      return true

    # ...

    when "cp"
      copy(
        @mem.main[@pc + 1],
        @mem.main[@pc + 2]
      )
      @pc += pc_delta
    when "add_ab"
      add_ab()
      @pc += pc_delta

    # ...

    when "call"
      set_sp(@sp - 1) # スタックポインタを1減らす
      @mem.stack[@sp] = @pc + 2 # 戻り先を記憶
      next_addr = @mem.main[@pc + 1] # ジャンプ先
      @pc = next_addr

    # ...
  • (1) メモリから値を取得して引数でメソッドに渡す
  • (2) 引数なしでメソッドを呼び出し、メソッド内でメモリの値を使う
  • (3) メソッド呼び出ししない

のように各命令の処理がバラバラになっていました。 これはどれに揃えるか、あるいは無理に揃えなくてもいいか……などと考えつつ後回しにしていたところです。

ちょっと悩みましたが、 スタイルが統一されて case 式がスッキリするメリットを取って (2) 引数なしでメソッドを呼び出す方式に統一しました。

# 修正後

    case op
    when "exit"
      return true
    when "set_reg_a"
      set_reg_a()
      @pc += pc_delta
    when "set_reg_b"
      set_reg_b()
      @pc += pc_delta
    when "cp"
      copy()
      @pc += pc_delta

    # ...

    when "label"
      @pc += pc_delta
    when "jump"
      jump()
    when "jump_eq"
      jump_eq()

その他