素朴な自作言語のコンパイラをRustに移植した

かんたんな自作言語のコンパイラをいろんな言語で書いてみるシリーズ 15番目の言語は Rust です。

理解は後回しにしてとにかく動くものを作るぞ、という方向性で書いたもの。

できたもの

github.com

移植元

memo88.hatenablog.com

ベースになっているバージョン: tag:56 のあたり

動かし方の例

$ echo '
  func add(a, b) {
    return a + b;
  }

  func main() {
    call add(1, 2);
  }
' | cargo run lex | cargo run parse | cargo run codegen

# ↓アセンブリが出力される

  call main
  exit

label add
  push bp
  cp sp bp
  cp [bp:2] reg_a
  push reg_a
  cp [bp:3] reg_a
  push reg_a
  pop reg_b
  pop reg_a
  add_ab
  cp bp sp
  pop bp
  ret

label main
  push bp
  cp sp bp
  cp 2 reg_a
  push reg_a
  cp 1 reg_a
  push reg_a
  _cmt call~~add
  call add
  add_sp 2
  cp bp sp
  pop bp
  ret
(snip)

メモ

  • 理解は後回しにして、かっこ悪い書き方でもいいのでとにかく完成まで持って行く……といういつも通りの方針で、時間をかけすぎないように。理解は後でゆっくり。まずは手を動かして慣れる。
  • Tour of Rust
    • 情報量が多すぎずよく整理されていて、一番最初にこれを読めばよかった
  • 借用とかムーブとか
    • このくらいのプログラムを書いて動かせる程度には分かってきた、はず。 でもけっこう怪しい。
  • ライフタイム
    • まだよく分かっていなくて、コンパイルが通らなかったらライフタイムが絡まない書き方にして回避したりしている
  • 連結リストが難しそうだったので Vec<NodeId> で管理する方式にしてとりあえず回避
  • レキサの入力文字列は最初に Vec<char> にして使い回し
    • C版Zig版 のように単純なバイト列として扱ってもよかったが、 せっかくなので UTF-8 文字列として扱ってみた

TODO

気が向いたらあとで

  • List にノードID ではなくノードを直接持たせる
  • List のイテレータ対応

この記事を読んだ人はこちらも(ひょっとしたら)読んでいます

memo88.hatenablog.com

memo88.hatenablog.com

memo88.hatenablog.com