なんだかライフゲーム詐欺みたいになってきましたが
今度こそほんとにライフゲームに突入できる……はず……。
というか突入しましょう!
やるぞぉ……!
はい、やります。
vgtコードです。
最終的にはグライダーを動かしたいのですが、
まずは確認が簡単そうなブリンカーから始めましょうか。
["stmts"
, ["func", "vram_set", ["w", "x", "y", "val"]
, [
["var", "yw"]
, ["set", "yw", ["*", "y", "w"]]
, ["var", "vi"]
, ["set", "vi", ["+", "yw", "x"]]
, ["set", "vram[vi]", "val"]
]
]
, ["func", "main", []
, [
["var", "w"]
, ["set", "w", 5]
, ["call", "vram_set", "w", 2, 1, 1]
, ["call", "vram_set", "w", 2, 2, 1]
, ["call", "vram_set", "w", 2, 3, 1]
]
]
]
まずは初期化の部分ということで、
main
関数とは別に、座標を指定して状態をセットする vram_set
関数を用意し、
座標
(2, 1)
(2, 2)
(2, 3)
の 3つのセルを生存状態にします。
VM の初期化時に VRAM の内容を 0 で初期化しているので、
これ以外のセルはすべて死亡状態です。
こういう状態に初期化したい:
.....
..@..
..@..
..@..
.....
さて、適当に書きましたが、とりあえず動かしてみましょうか。
どうなるでしょうか?
$ ./run.sh 27_blinker.vgt.json
vgcg.rb:106:in `codegen_exp': Not yet implemented ("left") ("y") (RuntimeError)
from vgcg.rb:154:in `codegen_set'
from vgcg.rb:221:in `block in codegen_func_def'
from vgcg.rb:196:in `each'
from vgcg.rb:196:in `codegen_func_def'
from vgcg.rb:251:in `block in codegen_stmts'
from vgcg.rb:247:in `each'
from vgcg.rb:247:in `codegen_stmts'
from vgcg.rb:270:in `codegen'
from vgcg.rb:282:in `<main>'
vgcg.rb
の該当箇所を見てみると下記のようになっています。
def codegen_exp(lvar_names, exp)
left =
case args[0]
when Integer
args[0]
when String
case
when lvar_names.include?(args[0])
lvar_pos = lvar_names.index(args[0]) + 1
"[bp-#{lvar_pos}]"
else
raise not_yet_impl("left", args[0])
end
else
ローカル変数には対応済ですが、関数の引数には未対応だったようです。
対応しましょう。
--- a/vgcg.rb
+++ b/vgcg.rb
@@ -65,7 +65,7 @@ def codegen_while(fn_arg_names, lvar_names, rest)
alines << "label while_#{label_id}"
# 条件の評価 ... 結果が reg_a に入る
- alines += codegen_exp(lvar_names, cond_exp)
+ alines += codegen_exp(fn_arg_names, lvar_names, cond_exp)
# 比較対象の値(真)をセット
alines << " set_reg_b 1"
alines << " compare"
@@ -89,7 +89,7 @@ def codegen_while(fn_arg_names, lvar_names, rest)
alines
end
-def codegen_exp(lvar_names, exp)
+def codegen_exp(fn_arg_names, lvar_names, exp)
alines = []
operator, *args = exp
@@ -102,6 +102,9 @@ def codegen_exp(lvar_names, exp)
when lvar_names.include?(args[0])
lvar_pos = lvar_names.index(args[0]) + 1
"[bp-#{lvar_pos}]"
+ when fn_arg_names.include?(args[0])
+ fn_arg_pos = fn_arg_names.index(args[0]) + 2
+ "[bp+#{fn_arg_pos}]"
else
raise not_yet_impl("left", args[0])
end
@@ -151,7 +154,7 @@ def codegen_set(fn_arg_names, lvar_names, rest)
rest[1]
when rest[1].is_a?(Array)
exp = rest[1]
- alines += codegen_exp(lvar_names, exp)
+ alines += codegen_exp(fn_arg_names, lvar_names, exp)
"reg_a"
when fn_arg_names.include?(rest[1])
fn_arg_pos = fn_arg_names.index(rest[1]) + 2
@@ -220,7 +223,7 @@ def codegen_func_def(rest)
when "set"
alines += codegen_set(fn_arg_names, lvar_names, stmt_rest)
when "eq"
- alines += codegen_exp(lvar_names, stmt)
+ alines += codegen_exp(fn_arg_names, lvar_names, stmt)
when "return"
val = stmt_rest[0]
alines << " set_reg_a #{val}"
再度実行。
$ ./run.sh 27_blinker.vgt.json
vgcg.rb:141:in `codegen_exp': Not yet implemented ("operator") ("*") (RuntimeError)
from vgcg.rb:157:in `codegen_set'
from vgcg.rb:224:in `block in codegen_func_def'
from vgcg.rb:199:in `each'
from vgcg.rb:199:in `codegen_func_def'
from vgcg.rb:254:in `block in codegen_stmts'
from vgcg.rb:250:in `each'
from vgcg.rb:250:in `codegen_stmts'
from vgcg.rb:273:in `codegen'
from vgcg.rb:285:in `<main>'
おっと、掛け算がまだなかったですね。
足し算と同じ感じで追加しましょう。
まず VM に命令を追加してから、それに合わせてコード生成器を修正する、
という順番が正攻法な気がしますが、
そんなに難しいことやってないのでコード生成器から先にやってしまっても大丈夫でしょう。
エラーメッセージにしたがってコード生成器を掛け算に対応させます。
--- a/vgcg.rb
+++ b/vgcg.rb
@@ -119,6 +119,10 @@ def codegen_exp(fn_arg_names, lvar_names, exp)
alines << " set_reg_a #{left}"
alines << " set_reg_b #{right}"
alines << " add_ab"
+ when "*"
+ alines << " set_reg_a #{left}"
+ alines << " set_reg_b #{right}"
+ alines << " mult_ab"
when "eq"
$label_id += 1
label_id = $label_id
足し算とほぼ同じですね。
reg_a
、 reg_b
に値をセットし、
mult_ab
命令を実行すると結果が reg_a
に入るようにします。
"mult" は "multiply" の略です。
実行するとこんどは VM に mult_ab
命令などない!
と怒られるので、 命令を追加します。
--- a/vgvm.rb
+++ b/vgvm.rb
@@ -173,6 +173,9 @@ class Vm
when "add_ac"
add_ac()
@pc += pc_delta
+ when "mult_ab"
+ mult_ab()
+ @pc += pc_delta
when "add_sp"
set_sp(@sp + @mem.main[@pc + 1])
@pc += pc_delta
@@ -294,7 +297,7 @@ class Vm
2
when "set_reg_a", "set_reg_b", "label", "call", "push", "pop", "add_sp", "sub_sp", "jump_eq", "jump"
1
- when "ret", "exit", "add_ab", "compare"
+ when "ret", "exit", "add_ab", "compare", "mult_ab"
0
else
raise "Invalid operator (#{operator})"
@@ -346,6 +349,10 @@ class Vm
@reg_a = @reg_a + @reg_c
end
+ def mult_ab
+ @reg_a = @reg_a * @reg_b
+ end
+
def set_reg_a(val)
@reg_a =
case val
こちらも足し算のコードをコピペしてちょこっと変えるだけですね。
動かしてみます。
$ ./run.sh 27_blinker.vgt.json
(略)
================================
reg_a(0) reg_b(0) reg_c(0) zf(0)
---- memory (main) ----
00 ["call", 41]
02 ["exit"]
03 ["label", "vram_set"]
05 ["push", "bp"]
07 ["cp", "sp", "bp"]
10 ["sub_sp", 1]
12 ["set_reg_a", "[bp+4]"]
14 ["set_reg_b", "w"]
16 ["mult_ab"]
17 ["cp", "reg_a", "[bp-1]"]
20 ["sub_sp", 1]
22 ["set_reg_a", "[bp-1]"]
24 ["set_reg_b", "x"]
26 ["add_ab"]
27 ["cp", "reg_a", "[bp-2]"]
30 ["set_vram", "vi", "[bp+5]"]
33 ["cp", "bp", "sp"]
36 ["pop", "bp"]
38 ["ret"]
39 ["label", "main"]
41 ["push", "bp"]
43 ["cp", "sp", "bp"]
46 ["sub_sp", 1]
48 ["cp", 5, "[bp-1]"]
51 ["push", 1]
53 ["push", 1]
55 ["push", 2]
pc => 57 ["push", "w"]
59 ["call", 5]
61 ["add_sp", 4]
63 ["push", 1]
65 ["push", 2]
67 ["push", 2]
69 ["push", "w"]
71 ["call", 5]
73 ["add_sp", 4]
75 ["push", 1]
77 ["push", 3]
79 ["push", 2]
81 ["push", "w"]
83 ["call", 5]
85 ["add_sp", 4]
87 ["cp", "bp", "sp"]
90 ["pop", "bp"]
92 ["ret"]
---- memory (stack) ----
35 0
36 0
37 0
38 0
39 0
40 0
41 0
42 0
sp => 43 2
44 1
45 1
46 5
bp => 47 49
48 2
49 0
---- memory (vram) ----
..... .....
..... .....
..... .....
..... .....
..... .....
vgvm.rb:214:in `block in start': Not yet implemented ("push") ("w") (RuntimeError)
from vgvm.rb:142:in `loop'
from vgvm.rb:142:in `start'
from vgvm.rb:389:in `<main>'
機械語コードに w
が出てくるのはおかしいので、
コード生成器がおかしいはず。
w
の参照が解決できてないのでは。
39 ["label", "main"]
41 ["push", "bp"]
43 ["cp", "sp", "bp"]
46 ["sub_sp", 1]
48 ["cp", 5, "[bp-1]"]
51 ["push", 1]
53 ["push", 1]
55 ["push", 2]
pc => 57 ["push", "w"]
59 ["call", 5]
main()
が始まって、 vram_set()
の呼び出しの前に引数を
スタックに push するとこがおかしいようだ、と当たりを付けて調べてみると……。
ここですね。
やはり、参照の解決をしていないようです。
def codegen_func_def(rest)
case stmt_head
when "call"
fn_name, *fn_args = stmt_rest
fn_args.reverse.each {|fn_arg|
alines << " push #{fn_arg}"
}
alines << " call #{fn_name}"
alines << " add_sp #{fn_args.size}"
when "call_set"
修正します。
--- a/vgcg.rb
+++ b/vgcg.rb
@@ -206,7 +206,20 @@ def codegen_func_def(rest)
when "call"
fn_name, *fn_args = stmt_rest
fn_args.reverse.each {|fn_arg|
- alines << " push #{fn_arg}"
+ case fn_arg
+ when Integer
+ alines << " push #{fn_arg}"
+ when String
+ case
+ when lvar_names.include?(fn_arg)
+ lvar_pos = lvar_names.index(fn_arg) + 1
+ alines << " push [bp-#{lvar_pos}]"
+ else
+ raise not_yet_impl(fn_arg)
+ end
+ else
+ raise not_yet_impl(fn_arg)
+ end
}
alines << " call #{fn_name}"
alines << " add_sp #{fn_args.size}"
動かします。
$ ./run.sh 27_blinker.vgt.json
(略)
================================
reg_a(0) reg_b(0) reg_c(0) zf(0)
---- memory (main) ----
00 ["call", 41]
02 ["exit"]
03 ["label", "vram_set"]
05 ["push", "bp"]
07 ["cp", "sp", "bp"]
10 ["sub_sp", 1]
12 ["set_reg_a", "[bp+4]"]
14 ["set_reg_b", "w"]
16 ["mult_ab"]
17 ["cp", "reg_a", "[bp-1]"]
20 ["sub_sp", 1]
22 ["set_reg_a", "[bp-1]"]
24 ["set_reg_b", "x"]
26 ["add_ab"]
27 ["cp", "reg_a", "[bp-2]"]
30 ["set_vram", "vi", "[bp+5]"]
33 ["cp", "bp", "sp"]
36 ["pop", "bp"]
38 ["ret"]
39 ["label", "main"]
41 ["push", "bp"]
43 ["cp", "sp", "bp"]
46 ["sub_sp", 1]
48 ["cp", 5, "[bp-1]"]
51 ["push", 1]
53 ["push", 1]
55 ["push", 2]
pc => 57 ["push", "[bp-1]"]
59 ["call", 5]
61 ["add_sp", 4]
63 ["push", 1]
65 ["push", 2]
67 ["push", 2]
69 ["push", "[bp-1]"]
71 ["call", 5]
73 ["add_sp", 4]
75 ["push", 1]
77 ["push", 3]
79 ["push", 2]
81 ["push", "[bp-1]"]
83 ["call", 5]
85 ["add_sp", 4]
87 ["cp", "bp", "sp"]
90 ["pop", "bp"]
92 ["ret"]
---- memory (stack) ----
35 0
36 0
37 0
38 0
39 0
40 0
41 0
42 0
sp => 43 2
44 1
45 1
46 5
bp => 47 49
48 2
49 0
---- memory (vram) ----
..... .....
..... .....
..... .....
..... .....
..... .....
vgvm.rb:214:in `block in start': Not yet implemented ("push") ("[bp-1]") (RuntimeError)
from vgvm.rb:142:in `loop'
from vgvm.rb:142:in `start'
from vgvm.rb:389:in `<main>'
たぶんこれも push
命令で参照の解決が未実装ですねー。
今までサボッていたツケが回ってきた、と見ることもできますが、
YAGNI でやってるので今が実装のタイミングなのだ、と見ることもできるので、
大丈夫だと思います。
たぶん。
--- a/vgvm.rb
+++ b/vgvm.rb
@@ -208,8 +208,16 @@ class Vm
case arg
when Integer
arg
- when "bp"
- @bp
+ when String
+ case arg
+ when "bp"
+ @bp
+ when /^\[bp\-(\d+)\]$/
+ stack_addr = @bp - $1.to_i
+ @mem.stack[stack_addr]
+ else
+ raise not_yet_impl("push", arg)
+ end
else
raise not_yet_impl("push", arg)
end
お次はこう:
vgvm.rb:373:in `set_reg_a': Not yet implemented ("val") ("[bp+4]") (RuntimeError)
from vgvm.rb:154:in `block in start'
from vgvm.rb:142:in `loop'
from vgvm.rb:142:in `start'
from vgvm.rb:397:in `<main>'
修正しまーす。
--- a/vgvm.rb
+++ b/vgvm.rb
@@ -369,6 +369,9 @@ class Vm
when /^\[bp-(\d+)\]$/
stack_addr = @bp - $1.to_i
@mem.stack[stack_addr]
+ when /^\[bp\+(\d+)\]$/
+ stack_addr = @bp + $1.to_i
+ @mem.stack[stack_addr]
else
raise not_yet_impl("val", val)
end
またこれ:
03 ["label", "vram_set"]
05 ["push", "bp"]
07 ["cp", "sp", "bp"]
10 ["sub_sp", 1]
12 ["set_reg_a", "[bp+4]"]
14 ["set_reg_b", "w"] ← まだ "w" が残ってる
pc => 16 ["mult_ab"]
vgvm.rb:361:in `*': String can't be coerced into Fixnum (TypeError)
from vgvm.rb:361:in `mult_ab'
from vgvm.rb:177:in `block in start'
from vgvm.rb:142:in `loop'
from vgvm.rb:142:in `start'
from vgvm.rb:400:in `<main>'
次から次に出てきますね……ガンガンやっつけていきましょう。
codegen_exp()
の右項で参照の解決が未実装でした。
--- a/vgcg.rb
+++ b/vgcg.rb
@@ -112,7 +112,21 @@ def codegen_exp(fn_arg_names, lvar_names, exp)
raise not_yet_impl("left", args[0])
end
- right = args[1]
+ right =
+ case args[1]
+ when Integer
+ args[1]
+ when String
+ case
+ when fn_arg_names.include?(args[1])
+ fn_arg_pos = fn_arg_names.index(args[1]) + 2
+ "[bp+#{fn_arg_pos}]"
+ else
+ raise not_yet_impl("right", args[1])
+ end
+ else
+ raise not_yet_impl("right", args[1])
+ end
case operator
when "+"
またなんか似た感じの:
================================
reg_a(1) reg_b("[bp+2]") reg_c(0) zf(0)
---- memory (main) ----
00 ["call", 41]
02 ["exit"]
03 ["label", "vram_set"]
05 ["push", "bp"]
07 ["cp", "sp", "bp"]
10 ["sub_sp", 1]
12 ["set_reg_a", "[bp+4]"]
14 ["set_reg_b", "[bp+2]"]
pc => 16 ["mult_ab"]
vgvm.rb:361:in `*': String can't be coerced into Fixnum (TypeError)
from vgvm.rb:361:in `mult_ab'
from vgvm.rb:177:in `block in start'
from vgvm.rb:142:in `loop'
from vgvm.rb:142:in `start'
from vgvm.rb:400:in `<main>'
w
が [bp+2]
になりましたが、
こんどは reg_b
に [bp+2]
という文字列がそのまま入っていてダメです。
見てみるとこうなっていて、
def start
case op
when "set_reg_a"
val = @mem.main[@pc + 1]
set_reg_a(val)
@pc += pc_delta
when "set_reg_b"
n = @mem.main[@pc + 1]
@reg_b = n
@pc += pc_delta
def set_reg_a(val)
@reg_a =
case val
when Integer
val
when /^\[bp-(\d+)\]$/
stack_addr = @bp - $1.to_i
@mem.stack[stack_addr]
when /^\[bp\+(\d+)\]$/
stack_addr = @bp + $1.to_i
@mem.stack[stack_addr]
else
raise not_yet_impl("val", val)
end
end
set_reg_a
命令では対応済なので、
set_reg_b
でも同じようにすればいいでしょう。
--- a/vgvm.rb
+++ b/vgvm.rb
@@ -154,8 +154,8 @@ class Vm
set_reg_a(val)
@pc += pc_delta
when "set_reg_b"
- n = @mem.main[@pc + 1]
- @reg_b = n
+ val = @mem.main[@pc + 1]
+ set_reg_b(val)
@pc += pc_delta
when "set_reg_c"
n = @mem.main[@pc + 1]
@@ -377,6 +377,22 @@ class Vm
end
end
+ def set_reg_b(val)
+ @reg_b =
+ case val
+ when Integer
+ val
+ when /^\[bp-(\d+)\]$/
+ stack_addr = @bp - $1.to_i
+ @mem.stack[stack_addr]
+ when /^\[bp\+(\d+)\]$/
+ stack_addr = @bp + $1.to_i
+ @mem.stack[stack_addr]
+ else
+ raise not_yet_impl("val", val)
+ end
+ end
+
def compare
@zf = (@reg_a == @reg_b) ? 1 : 0
end
お次は?
---- memory (main) ----
00 ["call", 41]
02 ["exit"]
03 ["label", "vram_set"]
05 ["push", "bp"]
07 ["cp", "sp", "bp"]
10 ["sub_sp", 1]
12 ["set_reg_a", "[bp+4]"]
14 ["set_reg_b", "[bp+2]"]
16 ["mult_ab"]
17 ["cp", "reg_a", "[bp-1]"]
20 ["sub_sp", 1]
22 ["set_reg_a", "[bp-1]"]
24 ["set_reg_b", "[bp+3]"]
26 ["add_ab"]
27 ["cp", "reg_a", "[bp-2]"]
pc => 30 ["set_vram", "vi", "[bp+5]"]
33 ["cp", "bp", "sp"]
36 ["pop", "bp"]
38 ["ret"]
vgvm.rb:241:in `[]=': no implicit conversion of String into Integer (TypeError)
from vgvm.rb:241:in `block in start'
from vgvm.rb:142:in `loop'
from vgvm.rb:142:in `start'
from vgvm.rb:416:in `<main>'
vi
は変数名なので機械語コードに出現しているのがおかしい、
というパターンですね。
つまり参照が未解決……。
codegen_set()
を修正します。
--- a/vgcg.rb
+++ b/vgcg.rb
@@ -188,7 +188,15 @@ def codegen_set(fn_arg_names, lvar_names, rest)
case dest
when /^vram\[(.+)\]$/
vram_addr = $1
- alines << " set_vram #{vram_addr} #{src_val}"
+ case
+ when /^\d+$/ =~ vram_addr
+ alines << " set_vram #{vram_addr} #{src_val}"
+ when lvar_names.include?(vram_addr)
+ lvar_pos = lvar_names.index(vram_addr) + 1
+ alines << " set_vram [bp-#{lvar_pos}] #{src_val}"
+ else
+ raise not_yet_impl("vram_addr", vram_addr)
+ end
else
lvar_pos = lvar_names.index(dest) + 1
alines << " cp #{src_val} [bp-#{lvar_pos}]"
どうでしょうか?
---- memory (main) ----
00 ["call", 41]
02 ["exit"]
03 ["label", "vram_set"]
05 ["push", "bp"]
07 ["cp", "sp", "bp"]
10 ["sub_sp", 1]
12 ["set_reg_a", "[bp+4]"]
14 ["set_reg_b", "[bp+2]"]
16 ["mult_ab"]
17 ["cp", "reg_a", "[bp-1]"]
20 ["sub_sp", 1]
22 ["set_reg_a", "[bp-1]"]
24 ["set_reg_b", "[bp+3]"]
26 ["add_ab"]
27 ["cp", "reg_a", "[bp-2]"]
pc => 30 ["set_vram", "[bp-2]", "[bp+5]"]
vgvm.rb:241:in `[]=': no implicit conversion of String into Integer (TypeError)
from vgvm.rb:241:in `block in start'
from vgvm.rb:142:in `loop'
from vgvm.rb:142:in `start'
from vgvm.rb:416:in `<main>'
vi
は [bp-2]
に置き換わりましたが、まだダメか〜。
修正するぞ〜。
--- a/vgvm.rb
+++ b/vgvm.rb
@@ -238,7 +238,27 @@ class Vm
arg1 = @mem.main[@pc + 1]
arg2 = @mem.main[@pc + 2]
- @mem.vram[arg1] = arg2
+ src_val =
+ case arg2
+ when Integer
+ arg2
+ when /^\[bp\+(\d+)\]$/
+ stack_addr = @bp + $1.to_i
+ @mem.stack[stack_addr]
+ else
+ raise not_yet_impl("set_vram", arg2)
+ end
+
+ case arg1
+ when Integer
+ @mem.vram[arg1] = src_val
+ when /^\[bp-(\d+)\]$/
+ stack_addr = @bp - $1.to_i
+ vram_addr = @mem.stack[stack_addr]
+ @mem.vram[vram_addr] = src_val
+ else
+ raise not_yet_impl("set_vram", arg1)
+ end
@pc += pc_delta
when "get_vram"
どうだ!?
$ ./run.sh 27_blinker.vgt.json
(略)
================================
reg_a(17) reg_b(2) reg_c(0) zf(0)
---- memory (main) ----
00 ["call", 41]
pc => 02 ["exit"]
03 ["label", "vram_set"]
05 ["push", "bp"]
07 ["cp", "sp", "bp"]
10 ["sub_sp", 1]
12 ["set_reg_a", "[bp+4]"]
14 ["set_reg_b", "[bp+2]"]
16 ["mult_ab"]
17 ["cp", "reg_a", "[bp-1]"]
20 ["sub_sp", 1]
22 ["set_reg_a", "[bp-1]"]
24 ["set_reg_b", "[bp+3]"]
26 ["add_ab"]
27 ["cp", "reg_a", "[bp-2]"]
30 ["set_vram", "[bp-2]", "[bp+5]"]
33 ["cp", "bp", "sp"]
36 ["pop", "bp"]
38 ["ret"]
39 ["label", "main"]
41 ["push", "bp"]
43 ["cp", "sp", "bp"]
46 ["sub_sp", 1]
48 ["cp", 5, "[bp-1]"]
51 ["push", 1]
53 ["push", 1]
55 ["push", 2]
57 ["push", "[bp-1]"]
59 ["call", 5]
61 ["add_sp", 4]
63 ["push", 1]
65 ["push", 2]
67 ["push", 2]
69 ["push", "[bp-1]"]
71 ["call", 5]
73 ["add_sp", 4]
75 ["push", 1]
77 ["push", 3]
79 ["push", 2]
81 ["push", "[bp-1]"]
83 ["call", 5]
85 ["add_sp", 4]
87 ["cp", "bp", "sp"]
90 ["pop", "bp"]
92 ["ret"]
---- memory (stack) ----
41 85
42 5
43 2
44 3
45 1
46 5
47 49
48 2
sp bp => 49 0
---- memory (vram) ----
..... .....
..@.. .....
..@.. .....
..@.. .....
..... .....
exit
やった〜動いた〜!!
(上に貼ったのは終了時の状態です)
Enter キーを連打してプログラムの実行を進めると、
3つのセルが順番に生存状態になっていくのが
VRAM のダンプ表示で確認できました!!!!
グラフィックが動くと、
これまでとは違う種類の手応えがあっていいですね……!!
楽しくなってきた!!
とりあえず動くところまで持って行きたかったので
ほぼコピペみたいな感じで参照を解決する部分のコードを書き散らかしてしまいました。
似たような処理がこれだけ多く出現すると
さすがにリファクタリングしたくなってきますので、
次回はリファクタリングにしましょう。
あと、ダンプ表示が長くなってきて1画面に収まらなくなってきたので、
そこもどうにかしましょう。