🕌

Zig で簡単な Lisp インタプリタを書いてみた。

2022/07/14に公開

はじめに

https://zenn.dev/mattn/articles/3b01651b7a42b3

先日 Zig で JSON パーサを書いて Zig がどういう言語なのか、だいたい理解できたので Lisp インタプリタを実装してみた。

簡単な実装なので四則演算と変数定義、関数呼び出しくらいしか実装してない。

https://github.com/mattn/zig-lisp

ちゃんと文法チェックなどをやってないので、変な Lisp コードを渡すと落ちる。これはいずれ直す予定。

実装中に知った Zig の記法

blk

名前付きブロックで値が返せる。

const std = @import("std");

pub fn main() anyerror!void {
    std.log.warn("{}", .{
        blk: {
            const d: u32 = 5;
            const e: u32 = 100;
            break :blk d + e;
        }, 
    });
}

便利かどうかは置いておいてカッコいい。

error handling if

関数の戻り値が error と union されている場合は if で処理ができる。

if (std.fmt.parseInt(i64, bytes.items, 10)) |num| {
    // 正常時の処理
} else |err| {
    // 異常時の処理
}

最初これを知らなくて遠回りな書き方をしてしまっていた。

for に else が書ける

var i: i32 = 0
for (i < 100): (i+=1) {
  // なにか
} else {
  // 回り切った時に実行される
}

賛否両論あるけど使ったら意外と便利だった。

今後気を付けたいところ

Zig はアロケータが全て。例えば struct に可変文字列 []const u8 を持つ場合、後で同じアロケータを使ってメモリを解放しないとリークする。これはこれでC言語ぽさがあって好きだけど、便利さは下がる。例えば Zig のハッシュマップに動的に項目を足す場合、キーも動的に生成する事になるが、このキーも解放時に同じアロケータで解放しなければならない。

var key = try parseString(a, br);
defer key.deinit();
try m.put(key.toOwnedSlice(), value);
for (m.keys()) |key| {
    allocator.free(key);
}
m.deinit();

ちなみにメモリリークしているかどうかは、アロケータとして std.testing.allocator を渡してテストすると勝手にリーク検査をやってくれる。とても便利。

若干の不満

ベンチマーカーが標準に無い

Zig には標準でベンチマーカーが付属していない。Go に慣れてしまっているので標準でベンチマークが付いていて当然になってしまった。

インタフェースが無い

Zig は全て struct なので、メソッドをシグネチャのチェックとして使える Go や Java の様なインタフェースが存在しない。代わりに anytype を渡してコンパイル時にチェックされる。これはこれでいいのかもしれないが、安心感が無い。どうしても型チェックしたい場合には型を書くしかないが、標準ライブラリに同梱されている型は異常に長い。

例えばバイト列を読み込めるピーク可能なストリームの型は以下の様に宣言しないといけない。

const ByteReader = std.io.PeekStream(std.fifo.LinearFifoBufferType{ .Static = 2 }, std.io.Reader(std.fs.File, std.os.ReadError, std.fs.File.read));

まぁ別名が付けられるだけマシではある。

今回実装した物の紹介

今回実装した Lisp は整数の四則演算ができる。

(print (+ 1 2))

変数が設定できる。

(setq a (+ 2 3))
(print (- 2 (+ 3 a)))

関数が作れる。

(defun foo (a b) (+ a b))
(print (foo 1 2))

雑な実装なので参照するスコープなどはおかしいかもしれない。

おわりに

数日前に書いた JSON パーサの時よりも、だいぶ Zig の理解が出来た気がする。

ところで新しいプログラミング言語を学ぶ時は Lisp を書いたりしているが、その実装をリストアップしておくので、見たい人は見て下さい。

https://github.com/mattn/cisp

https://github.com/mattn/golisp

https://github.com/mattn/lisper-vim

Zig 完全に理解した。

Discussion