コンパイラ作成(18) 式の解析の改良

今回の目標

今回は演算子の優先順位を正しく評価するよう頑張るよ。

main()
{
    print(2 * 3 + 5);
    print(2 + 3 * 5);
}

問題なのは二番目の方で、電卓式計算になっちゃってたやつね。

バグ発見

本題に行く前にもう一点なんだけど、式の解析を組み込んだ時からバグってたのを見つけたよ。

main()
{
    print(42);
}

一番簡単な式。これのチェックが漏れててさあ、mycに掛けるとSyntax errorになっちゃってた。今回これも併せて修正するよ。

expr

式の評価部のexprメソッドを修正。

  # 式の構文解析
  def expr(fkind,fstr)
    kind, str = @lex.gettoken
    return expr2 fkind,fstr,kind,str
  end

  # 式の構文解析
  def expr2(fkind,fstr,skind,sstr)
    if fkind == TK::NUMBER then
      codegen "  mov eax, " + fstr
      loop do
        if skind == TK::SYMBOL && sstr == "+" then
          kind, str = @lex.gettoken
          nkind, nstr = @lex.gettoken
          if nkind == TK::SYMBOL && nstr == "*" then
            codegen "  push rax"
            skind, sstr = expr2 kind, str, nkind, nstr
            codegen "  pop  rbx"
            codegen "  add  eax, ebx"
          elsif kind == TK::NUMBER then
            codegen "  add eax, " + str
            skind, sstr = nkind, nstr
          end
        elsif skind == TK::SYMBOL && sstr == "*" then
          kind, str = @lex.gettoken
          if kind == TK::NUMBER then
            codegen "  mov ebx, " + str
            codegen "  mul ebx"
            skind, sstr = @lex.gettoken
          end
        else
          return skind, sstr
        end
      end
    end
  end

もう一個先のトークンを読み込んで、それが*演算子だったらexpr2を再帰で呼ぶようにした。注意点は、既にeaxに途中までの計算結果が入ってること。これを保存しなきゃいけないんで、push/popのコードを生成するようにしたよ。

動作チェック

さてどうかな。

~/myc$ myc i7.myc
~/myc$ ./i7
11
17
~/myc$

お、正しい結果になったよ。二番目の式は2+3*5だから17であってるよね。今回の修正だけど、手を付ける前は難しそうだなと思ってたんだけど、やってみたら割と簡単にできたよ。案ずるより産むが易しだね。
今回の修正と同じようにすれば他の演算子の場合も対応できるかな。えーと、C言語演算子って全部でいくつあったっけ?かなり多いよね。全部サポートするのは大変だよなあ。うーむ、何とか四則演算ぐらいは早めにサポートしたいよ。

テスト用スクリプト

テスト漏れをなくすために、簡単なスクリプトを作ってみたよ。

#!/bin/bash

echo c.myc -------------------------
myc c.myc
./c
echo f.myc -------------------------
myc f.myc
./f
echo g.myc -------------------------
myc g.myc
./g

こんな感じでずらずらっと並べただけだけど、それでもテストが楽になったよ。今は13個並んでるけど今後どんどん増えてくんだろうなあ。

気になる点

前から気になってるんだけど掛算にmul使ってるけど、これimulにした方が良いんだろうか。そもそもmycはマイナスの数は扱えないよなあ。当面、mulで行ってマイナスの数を扱えるようになった時にimulに修正するか。