👌

Zigでかんたんな自作言語のコンパイラを書いた

2022/07/17に公開


ブログに書いていたものを引っ越してきて一部手直ししました。元の記事公開日は 2021-01-07 です。


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

ライフゲームのコンパイルが通ったのでヨシ、という程度の雑なものです。
Zig言語(ziglang)を触り始めて1週間くらいの人が、理解は後回しにしてとにかく動くものを作るぞ、という方向性で書いたもの(その程度のノリでかんたんに書けるコンパイラです)。

Zig のバージョンは 0.6.0。
(追記 2022-07-17: 0.9.1 に上げました)

できたもの

https://github.com/sonota88/vm2gol-v2-zig

サイズはこんな感じ:

$ wc -l {lexer,parser,codegen}.zig lib/{types,utils,json}.zig
  189 lexer.zig
  630 parser.zig
  552 codegen.zig
  209 lib/types.zig
  210 lib/utils.zig
  144 lib/json.zig
 1934 合計

動かし方の例

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

  func main() {
    call add(1, 2);
  }
' | zig run lexer.zig | zig run parser.zig | zig run codegen.zig

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

  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 ...

移植元

https://github.com/sonota88/vm2gol-v2

<自作言語処理系(Ruby版)の説明用テンプレ>
自分がコンパイラ実装に入門するために作った素朴なトイ言語とその処理系です。簡単に概要を書くと下記のような感じ。

<説明用テンプレおわり>

ベースになっているバージョン: tag:50 のあたり
(追記 2022-07-17: ステップ62 の修正まで適用しました)

メモ

  • C と Go の中間、GC のない Go みたいな印象
  • sentinel-terminated な配列もなるほど便利そうと思って使ってみたが、通常の配列とは別の型になって煩雑なのと、サイズ固定な配列の出番が実はほとんどなかったので、後から普通の配列+スライスで書き直していった
  • 例外の扱いも Go とは違ったアプローチでおもしろい。最初は試しに使っていたけど、お遊びプログラムなのでまじめにやる必要ないかと思い直し、これも後から使わないように(Java でいえば検査例外をその場で RuntimeException に包んで投げ直す感じに)書き変えた。単に煩雑さを避けるのを優先した形なのでほんとはちゃんとハンドリングすべきと思います。
    • エラーになったらその場で異常終了させるだけ、という作りです
  • 型まわりの理解がまだいまいち(配列や const が絡むあたりとか)
  • いろいろ用意するのが面倒なので Emacs + javascript-mode で済ませた
  • あと細かい話がいろいろあった気がするけど、作った後このエントリを書くまでしばらく他のことをしていたら忘れてしまった……思い出したら追記します
    • その間に Zig のバージョンが 0.6 から 0.8 に上がっていた
  • 今回はあんまり余裕なかったので実験的な要素はなし

逆引き的なメモ

標準エラー出力に出力 (v0.9.1)

const std = @import("std");

pub fn main() !void {
    const x = 123;
    const file: std.fs.File = std.io.getStdErr();

    try file.writer().print("{}\n", .{x});

    // または catch で処理する
    // file.outStream().print("{}", .{x}) catch |err| {
    //     std.debug.panic("error ({})", .{err});
    // };
}

デバッグ用なら std.debug.print が手軽っぽい。
https://github.com/ziglang/zig/blob/0.9.1/lib/std/debug.zig#L63

標準入力からの入力をすべて読む

Zig: 1バイトごとに読み書きするだけのcatコマンドを書いてみた

panic (v0.9.1)

https://github.com/ziglang/zig/blob/0.9.1/lib/std/debug.zig#L228

const panic = @import("std").debug.panic;

pub fn main() !void {
    panic("PANIC {} {s}", .{ -123, "foo" });
}

C の sprintf 相当 (v0.9.1)

https://github.com/ziglang/zig/blob/0.9.1/lib/std/fmt.zig#L1835

const std = @import("std");

pub fn main() !void {
    const s = "foo";
    const n = -123;
    var buf: [16]u8 = undefined;

    const slice: []u8 = try std.fmt.bufPrint(
        &buf,
        "{s} bar {}",
        .{ s, n }
    );

    std.debug.print("{s}\n", .{ slice });
}

整数 → 文字列 などの変換にも使えそう。

文字列を整数に変換 (v0.9.1)

https://github.com/ziglang/zig/blob/0.9.1/lib/std/fmt.zig#L1639

const std = @import("std");

pub fn main() !void {
    const s = "-123";
    const n = try std.fmt.parseInt(i32, s, 10);
    std.debug.print("{}\n", .{ n });
}

Zig 0.6.0 → 0.7.1 にバージョンアップした際の変更点

(2021-03-17 に追記)

Zig 0.7.1 → 0.8.0 にバージョンアップした際の変更点

(2022-07-17 に追記)
https://github.com/sonota88/vm2gol-v2-zig/commit/3635ba1eeb0a06fa

  • フォーマット文字列の書式が変わった
    • 文字列の場合は {s} と指定しないとコンパイルエラーになる
- panic("Failed to parse ({})", .{str});
+ panic("Failed to parse ({s})", .{str});
- file.outStream().print(...
+ file.writer().print(...

Zig 0.8.0 → 0.9.0 にバージョンアップした際の変更点

(2022-07-17 に追記)
https://github.com/sonota88/vm2gol-v2-zig/commit/71d70661e38d6ded

  • 未使用変数があるとコンパイルエラーになるようになった

↓この err なんかも使ってないと怒られるようになりました

-        var obj = allocator.create(Self) catch |err| {
+        var obj = allocator.create(Self) catch {
             panic("Failed to allocate", .{});
         };

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


https://memo88.hatenablog.com/entry/2020/09/06/043607


https://memo88.hatenablog.com/entry/2020/09/25/073200

https://memo88.hatenablog.com/entry/20210407_vm2gol_v2_rust


https://qiita.com/sonota88/items/275c2b5407986b100cc8

Discussion