minrubyコンパイラ / 変数を導入する
#MinRuby
from MinRubyコンパイラ
minrubyコンパイラに変数を導入した。
tree 上の var_assign を走査して以下を算出する。
変数の定義される順番に応じて、スタックポインタ(spレジスタ)からのオフセット位置を計算する(1番目に定義される変数のオフセット = 16、2番目に定義される変数のオフセット = 24、みたいな感じ)
スタックフレームのサイズ算出のため、変数の個数を数える
var_assign でスタックフレーム上へローカル変数を格納し、var_ref でスタックフレーム上のローカル変数を取得する。
code:diff
diff --git a/minrubyc.rb b/minrubyc.rb
index aa7a93c..06269df 100644
--- a/minrubyc.rb
+++ b/minrubyc.rb
@@ -1,6 +1,32 @@
require "minruby"
-def gen(tree)
+# tree 内の変数名一覧
+def var_names(arr, tree)
+ if tree0 == "var_assign"
+ arr.include?(tree1) ? arr : arr + [tree1]
+ elsif tree0 == "stmts"
+ tmp_arr = arr
+ tree1...each do |statement|
+ tmp_arr = tmp_arr + var_names(tmp_arr, statement)
+ end
+ tmp_arr
+ else
+ arr
+ end
+end
+
+# スタックフレーム上の変数のアドレスをスタックポインタ(sp)からのオフセットとして返す
+# 例:
+# ひとつ目の変数のアドレス = スタックポインタ + 16
+# ふたつ目の変数のアドレス = スタックポインタ + 24
+# ふたつ目の変数のアドレス = スタックポインタ + 32
+# ...
+def var_offset(var, env)
+ # 変数1つにつき8バイトの領域が必要
+ env.index(var) * 8 + 16
+end
+
+def gen(tree, env)
if tree0 == "lit"
puts "\tmov x0, ##{tree1}"
elsif %w(+ - * /).include?(tree0)
@@ -12,11 +38,11 @@ def gen(tree)
puts "\tsub sp, sp, #16"
# 左辺を評価した結果をスタックへ積む
- gen(expr1)
+ gen(expr1, env)
puts "\tstr x0, sp, #0"
# 右辺を評価した結果をスタックへ積む
- gen(expr2)
+ gen(expr2, env)
puts "\tstr x0, sp, #8"
# 演算
@@ -40,29 +66,41 @@ def gen(tree)
puts "\tadd sp, sp, #16"
elsif tree0 == "func_call" && tree1 == "p"
expr = tree2
- gen(expr)
+ gen(expr, env)
puts "\tbl _p"
elsif tree0 == "stmts"
tree1...each do |stmt|
- gen(stmt)
+ gen(stmt, env)
end
+ elsif tree0 == "var_assign"
+ name, expr = tree1, tree2
+
+ # 評価した値をスタック上のローカル変数領域へ格納
+ gen(expr, env)
+ puts "\tstr x0, sp, ##{var_offset(name, env)}"
+ elsif tree0 == "var_ref"
+ name = tree1
+
+ # ローカル変数領域からx0へ値をロード
+ puts "\tldr x0, sp, ##{var_offset(name, env)}"
else
raise "invalid AST: #{tree}"
end
end
tree = minruby_parse(ARGF.read)
+env = var_names([], tree)
puts "\t.text"
puts "\t.align 2"
puts "\t.globl _main"
puts "_main:"
-puts "\tsub sp, sp, #16"
+puts "\tsub sp, sp, ##{16 + env.size * 8}"
puts "\tstp fp, lr, sp, #0"
-gen(tree)
+gen(tree, env)
puts "\tmov w0, #0"
puts "\tldp fp, lr, sp, #0"
-puts "\tadd sp, sp, #16"
+puts "\tadd sp, sp, ##{16 + env.size * 8}"
puts "\tret"
diff --git a/test.sh b/test.sh
index 204047b..084c486 100755
--- a/test.sh
+++ b/test.sh
@@ -1,7 +1,5 @@
#!/bin/bash
-#!/bin/bash
-
assert() {
# expected の "\n" を改行コードをとして解釈させる
expected=echo -e "$1"
@@ -20,12 +18,20 @@ assert() {
fi
}
+# 変数
+assert "10\n20\n30\n" "a = 10; b = 20; c = 30; p a; p b; p c"
+
+# 複文
assert "10\n20\n" "p 10; p 20"
+
+# 四則演算
assert "305" "p((10+20*30)/2)"
assert "5" "p 30/6"
assert "72" "p 8*9"
assert "20" "p 30-10"
assert "30" "p 10+20"
+
+# 整数リテラル
assert "-10" "p(-10)"
assert "4649" "p 4649"
いい感じに動いてそう。
https://gyazo.com/4081d1ac190d8937ab0bd83587c3e1f6