コンパイラ作成(8) 複数の文

前回の続きで組み込み関数printのチェック。まずはコンパイル結果のアセンブリコードを見てみるよ。

.intel_syntax noprefix
.global main
main:
  mov rdi, 42
  call print
  ret

うんうん、ちゃんと思った通りのコードが出力されてる。次はもうちょっと複雑なプログラムのコンパイル行ってみるよ。

main()
{
    print(123);
    print(456);
}

複雑って言ってもprintが二つあるだけだけどね。

~/myc$ ./myc.rb c2.myc 
~/myc$ clang-5.0 c2.s print.o -o c2
~/myc$ ./c2
123
~/myc$ 

あれ、一個しか表示されないよ。

.intel_syntax noprefix
.global main
main:
  mov rdi, 123
  call print
  ret

うーむ、一個分しか出力されてない。複数の文に対応したはずなのになあ。あ、statementメソッドでtrueを返すところで間違えてfalseを返してる。これが原因か。

  def statement()
    kind, str = @lex.gettoken
    if kind == TK::SYMBOL && str == "}" then return false end
    if kind == TK::ID && str == "return" then
      # return文の処理
      kind, str = @lex.gettoken
      if kind == TK::NUMBER then
        codegen "  mov rax, "+str
        codegen "  ret"
      end
      kind, str = @lex.gettoken
      if kind == TK::SYMBOL && str == ";" then return true end
      perror "expected ';' after return statement"
      return true;
    elsif kind == TK::ID && str == "print" then
      # 組み込み関数printの処理
      kind, str = @lex.gettoken
      if kind != TK::SYMBOL || str != "(" then perror end
      kind, str = @lex.gettoken
      if kind == TK::NUMBER then
        codegen "  mov rdi, "+str
        codegen "  call print"
      end
      kind, str = @lex.gettoken
      if kind != TK::SYMBOL || str != ")" then perror end
      kind, str = @lex.gettoken
      if kind == TK::SYMBOL && str == ";" then return true end
      perror "expected ';' after builtin function"
      return true;
    end
    perror
    return true;
  end

二か所修正した。

~/myc$ ./myc.rb c2.myc 
~/myc$ clang-5.0 c2.s print.o -o c2
~/myc$ ./c2
123
456
~/myc$ 

おお、print二個分ちゃんと表示された。
やっぱりちょっと機能拡張したら十分にテストをしないと駄目だね。どんどこ先へ進めたいんだけどさ。