- 目次ページに戻る / 前 / 次
- 前回からの差分をまとめて見る
vgparser.rb
で行っていた字句解析処理を vglexer.rb
に分離します。
そうしなければいけない強い理由はあまりなくて、その方が他言語への移植がスムーズだった経験から、Ruby版にもフィードバックしておくか……というくらいのノリです。 あとは、個々のモジュールが小さくなっている方が威圧感がなくていいかなとか。
ファイル分割にあわせて、字句解析の結果であるトークン列をシリアライズして一度ファイルに出力する形にします。
整数値については、これまで字句解析の際に文字列から整数に変換して Token.new
に渡していましたが、最終的に文字列にシリアライズするのならわざわざ 文字列 → 整数 → 文字列 のように変換しなくても、文字列のまま受け渡してしまえばいいかなと。
*1
というわけで、まずはメインの修正の準備として文字列のまま渡すように修正。
--- a/vgparser.rb +++ b/vgparser.rb @@ -49,7 +49,7 @@ def tokenize(src) when /\A(-?[0-9]+)/ str = $1 - tokens << Token.new(:int, str.to_i) + tokens << Token.new(:int, str) pos += str.size when /\A(==|!=|[(){}=;+*,])/
これにより、Token#value
の型が String に統一され、静的型の言語への移植も(ちょっとだけ)やりやすくなります。
文字列から整数への変換はパーサで行うことになります。 微妙に DRY じゃないですが、まあいいか。
--- a/vgparser.rb +++ b/vgparser.rb @@ -131,7 +131,7 @@ class Parser t.value elsif t.type == :int @pos += 1 - t.value + t.value.to_i else raise ParseError end @@ -289,7 +289,14 @@ class Parser if t_left.type == :int || t_left.type == :ident @pos += 1 - expr_l = t_left.value + expr_l = + case t_left.type + when :int + t_left.value.to_i + else + t_left.value + end + parse_expr_right(expr_l) else
これで準備ができました。字句解析処理を vglexer.rb
に移動します……が、diff は大きいので略。
vglexer.rb
だけを動かして出力(シリアライズされたトークン列)を見るとこんな感じ:
$ ruby vglexer.rb gol.vg.txt | head kw:func ident:to_vi sym:( ident:w sym:, ident:x sym:, ident:y sym:, ident:offset
行ごとに記号と値をコロンで区切って並べるだけ。
あとは、 Parser クラスを解消して、トップレベルにメソッドが並ぶだけのスタイルにしました。簡単で分かりやすくする方向で考えるとクラスなくてもいいかなと。
テストコードの方もあわせて修正。すでに他のテストでやっているように ruby 〜.rb
の形でコマンドを実行して結果を読む方式に。
# test_vgparser.rb def _parse(src) File.open(VG_FILE, "wb") { |f| f.print src } _system %( ruby #{PROJECT_DIR}/vglexer.rb #{VG_FILE} > #{TOKENS_FILE} ) _system %( ruby #{PROJECT_DIR}/vgparser.rb #{TOKENS_FILE} > #{TREE_FILE} ) json = File.read(TREE_FILE) JSON.parse(json) end
その他の修正
- 変数名をより適切なものに変更
- words => insn, insns
- op, operator => opcode
- typo の修正
あと master
から main
にブランチ名を変えました。