- 目次ページに戻る / 前 / 次
- 前回からの差分をまとめて見る
まずはしょぼいミスを修正……。
--- a/vgparser.rb +++ b/vgparser.rb @@ -478,7 +478,7 @@ if $PROGRAM_NAME == __FILE__ begin tree = parser.parse() - rescue ParseError => e + rescue Parser::ParseError => e parser.dump_state() raise e end
codegen_case の変更
今C言語に移植しているところなのですが、
生成したアセンブリコードを alines
に溜めて受け渡していくのをめんどくさがって
printf()
でその都度出力するスタイルで書き進めていたら codegen_case()
の実装でひっかかりました。
Cのコーディングをがんばる方向も考えましたが、
そもそも then_bodies
が文字列の配列の配列となる時点で
若干複雑で、もうちょいどうにかならんかなという気がします。
それに、 then_bodies
なんてものが出てくる時点で
ここだけちょっと例外的なんですよね(気にはなっていた)。
というわけで、移植元の Ruby版の方から変えて、移植しやすくすることにしました。
case の動作としては等価で、 出力されるアセンブリコード(と機械語コード)の表現が変わる形です。
これはアセンブリコードで変更を見た方が分かりやすいと思います。
# 変更前: {条件式1の評価} compare jump_eq then_1 {条件式2の評価} compare jump_eq then_2 label then_1 {then_1 の場合の処理} jump end_case label then_2 {then_2 の場合の処理} jump end_case label end_case
これまでは、このように各条件式が真になった場合のコードを then_bodies
に溜めておいて、
後ろの方でまとめて出力していました。
まあ急いで作っていてその時パッと思いついたのをそのまま書いたというものです。 深い理由があってこのようになっているわけではありません。 変えてもいいでしょう。
で、どうするか。
jump_neq
(比較結果が偽の場合にジャンプする)という VM 命令を追加して、
次のようなアセンブリコードを出力するのはどうかと考えました
# 案1(没): {条件式1の評価} compare jump_neq end_when_1 {条件式1が真の場合の処理} jump end_case # 最後までスキップ label end_when_1 # 偽だった場合ここにジャンプ # → 次の式の評価へ進む {条件式2の評価} compare jump_neq end_when_2 {条件式2が真の場合の処理} jump end_case label end_when_2 label end_case
すっきりしてていい感じなのですが、
VM と アセンブラまで修正が必要になるのと、
今回のこれだけのために命令を追加するのも大げさ
(せっかく命令を追加してもここだけでしか使われない)かなと思って、
結局 jump_eq
でなんとかする次の方式にしました。
# 案2(採用): {条件式1の評価} compare jump_eq when_1 jump end_when_1 label when_1 # 真だった場合ここにジャンプ {条件式1が真の場合の処理} jump end_case # 最後までスキップ label end_when_1 # 偽だった場合ここにジャンプ # → 次の式の評価へ進む {条件式2の評価} compare jump_eq when_2 jump end_when_2 label when_2 {条件式2が真の場合の処理} jump end_case label end_when_2 label end_case
というわけで差分がこう。
then_bodies
に加えて then_alines
も不要になりました。
--- a/vgcg.rb +++ b/vgcg.rb @@ -36,10 +36,10 @@ def codegen_case(fn_arg_names, lvar_names, when_blocks) label_id = $label_id when_idx = -1 - then_bodies = [] label_end = "end_case_#{label_id}" label_when_head = "when_#{label_id}" + label_end_when_head = "end_when_#{label_id}" when_blocks.each do |when_block| when_idx += 1 @@ -56,24 +56,24 @@ def codegen_case(fn_arg_names, lvar_names, when_blocks) alines << " set_reg_b 1" alines << " compare" - alines << " jump_eq #{label_when_head}_#{when_idx}" + alines << " jump_eq #{label_when_head}_#{when_idx}" # 真の場合 + alines << " jump #{label_end_when_head}_#{when_idx}" # 偽の場合 + + # 真の場合ここにジャンプ + alines << "label #{label_when_head}_#{when_idx}" + + alines += codegen_stmts(fn_arg_names, lvar_names, rest) + + alines << " jump #{label_end}" + + # 偽の場合ここにジャンプ + alines << "label #{label_end_when_head}_#{when_idx}" - then_alines = ["label #{label_when_head}_#{when_idx}"] - then_alines += codegen_stmts(fn_arg_names, lvar_names, rest) - then_alines << " jump #{label_end}" - then_bodies << then_alines else raise not_yet_impl("cond_head", cond_head) end end - # すべての条件が偽だった場合 - alines << " jump #{label_end}" - - then_bodies.each {|then_alines| - alines += then_alines - } - alines << "label #{label_end}" alines
出力されるアセンブリコードがちょっとだけ複雑になり、 一方でコード生成器の複雑さは少し減りました。 悪くはないんじゃないかなと思います。 少なくとも移植はしやすくなりました。