- 目次ページに戻る / 前 / 次
- 前回からの差分をまとめて見る
変数宣言周りの修正(パーサ編)
第48回 変数宣言のコード生成処理の改善など で変数宣言まわりを変更したときは特に考えてなかったんですが、これはパーサも合わせておいた方が良さそうに思えます。
まずは var文が書ける場所を関数の直下のみに限定する変更。
--- a/vgparser.rb +++ b/vgparser.rb @@ -185,7 +185,20 @@ class Parser consume ")" consume "{" - stmts = parse_stmts() + + stmts = [] + loop do + t = peek() + break if t.value == "}" + + stmts << + if t.value == "var" + parse_var() + else + parse_stmt() + end + end + consume "}" [:func, func_name, args, stmts] @@ -434,7 +447,6 @@ class Parser case t.value when "func" then parse_func() - when "var" then parse_var() # parse_stmt() からは削除 when "set" then parse_set() when "call" then parse_call() when "call_set" then parse_call_set()
これにより while文やcase文の中で var文が使えなくなってテストが壊れるので適宜 set文に変えるなどしました。
--- a/test/test_vgparser.rb +++ b/test/test_vgparser.rb @@ -286,14 +286,16 @@ class ParserTest < Minitest::Test def test_while_2 src = <<-EOS + var a; while (a == 1) { - var b; + set a = 2; } EOS tree_exp = [ + [:var, "a"], [:while, [:eq, "a", 1], [ - [:var, "b"]]]] + [:set, "a", 2]]]]
次いで top_stmt(s) 関連の修正。
parse_top_stmt()
,parse_top_stmts()
を追加parse()
からparse_top_stmts()
を呼び出すように切り替えparse_stmt()
から関数定義の分岐を削除
--- a/vgparser.rb +++ b/vgparser.rb @@ -446,7 +446,6 @@ class Parser return nil if t.value == "}" case t.value - when "func" then parse_func() when "set" then parse_set() when "call" then parse_call() when "call_set" then parse_call_set() @@ -474,8 +473,31 @@ class Parser stmts end + def parse_top_stmt + t = peek() + + case t.value + when "func" then parse_func() + else + raise ParseError, "Unexpected token (#{t.inspect})" + end + end + + def parse_top_stmts + stmts = [] + + loop do + break if end?() + + stmts << parse_top_stmt() + end + + stmts + end + def parse - stmts = parse_stmts() + stmts = parse_top_stmts() [:stmts, *stmts] end end
これで、 関数定義の中に関数定義を書くのはダメ、などのルールがパーサのコードで表現できている状態になりました。 今まで結構いい加減にやっていたところが多少はマシになった気がします。
vgtコードのルート要素の名前を変更
いい機会なのでこれも今回あわせて変更しました。 地味に気になっていた。
// before [ "stmts", ... ] // after [ "top_stmts", ... ]
ちなみに Ruby でも top_stmts
という名前になっています。
https://github.com/ruby/ruby/blob/v2_7_1/parse.y#L1208
codegen_return で出力する命令を変更
整数の即値を return で返却する場合は
set_reg_a {値}
というアセンブリコードを出力していましたが、他との統一性のため
cp {値} reg_a
のように cp を使うように変えました。 こうしておいた方が都合が良さそうなのです。 出力されるアセンブリコード、機械語コードが変わりますが動作は何も変わりません。
Perl に移植 していたときに気付いて Ruby版にフィードバック。
peek
parse_var()
では先読みのため @tokens[@pos + 1]
としていて、
ここだけ peek に置き換えられていなかったんですが、
offset という引数を追加して peek(1)
とすると
現在位置の1個先を覗き見できるようにしました。
--- a/vgparser.rb +++ b/vgparser.rb @@ -87,8 +87,8 @@ class Parser .map { |t| format("%s<%s>", t.type, t.value) } end - def peek - @tokens[@pos] + def peek(offset = 0) + @tokens[@pos + offset] end def dump_state(msg = nil)
これも移植版で先行して試していたものをそろそろいいかなと思って取り込んだ形。
その他
- メソッドの位置の変更
- ParseError のメッセージを改善
- テストまわりの改善