コンパイラ作成(7) 組み込み関数print

return文一個のプログラムのコンパイルはできるようになったんで、もうちょっと複雑なのに挑戦するよ。さて何にするかな。式の評価?よくあるコンパイラの解説とかだと大体式の評価をやってるけど、式の構文解析とか難しそうだから違うことにしよ。

main()
{
    print(42);
}

ちょっと考えて今回はこれをコンパイルすることにしたよ。本当はprintfを呼べばいいんだけどハードル高いんで諦めた。

main()
{
    printf("%d\n",42);
}

これだと文字列リテラルとか出てきちゃうし、いきなり引数2個だし、そもそもprintfは不定個の引数受け付ける関数だもん。これを一気にやるのは厳しいよね。ってことで謎の組み込み関数printの登場。

#include <stdio.h>

void print(int n)
{
    printf("%d\n",n);
}

組み込み関数の実態はcで作った。で、前回つくったParserクラスのstatementメソッドに組み込み関数の処理を追加。

  # 文の構文解析
  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 false 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 false end
      perror "expected ';' after builtin function"
      return true;
    end
    perror
    return true;
  end

さてどうだ?

~/myc$ cat c.myc
main()
{
    print(42);
}
~/myc$ ./myc.rb c.myc 
~/myc$ clang-5.0 c.s print.o -o c
~/myc$ ./c
42
~/myc$

お、うまくできた。