プログラミング
Slisp: シンプルなLispコンパイラ (Linux/amd64)
Slisp: Simple Lisp compiler (Linux/amd64) (github.com)
要約
Slispは、LispプログラムをLinux/AMD64用のスタンドアロンアセンブリ表現にコンパイルするプロジェクトです。シンプルな構文のLisp言語を使用し、バインディング、関数、整数、文字列、ラムダ、リストなどの機能と、数学・比較演算、特殊形式をサポートしています。ガベージコレクションやマクロは含まれていませんが、テストとファズテストの機能を提供し、作者の学習目的で開発されました。
全文翻訳
slisp
このリポジトリには、入力としてLispプログラムを読み込み、Linux/AMD64用のスタンドアロンアセンブリ表現を生成するコンパイラであるSlispが含まれています。このプロジェクト名は「Simple Lisp」または「Steve's Lisp」のどちらでも構いません。Lispは伝統的に対話型でREPLを提供しますが、コンパイルされたバージョンも依然として有用であり、ほとんどの一般的なLispプログラムを実行できます。
クイックリンク:
INTRODUCTION.md 施設の簡潔な概要。
PRIMITIVES.md 利用可能なすべての関数と特殊形式の詳細なリスト。
例
```lisp
;; factorial. woo.
(defun fact (n)
(if (<= n 1)
1
(* n (fact (- n 1)))))
;; entry-point
(defun main ()
(println "Showing some factorials:")
(println (fact 4))
(println (fact 5))
(println (fact 9))
(println (fact 10))
;; exit code - use "(exit 3)" if you prefer
0)
```
`test/` ディレクトリには、`factorial.lisp`、`fibonacci.lisp`、`fizzbuzz.lisp` など、いくつかの例があります。`example.lisp` にはその他の雑多なスニペットが含まれており、最後に `brainfuck.lisp` には便利で動作するBrainfuckインタプリタが含まれています。コマンドラインに `-stdlib=false` が追加されない限り、すべてのユーザープログラムには標準ライブラリの関数が追加されることに注意してください。そのライブラリ自体は、機能の有用な参照/デモンストレーションです。
`stdlib.slisp` - Slisp自体で書かれた標準ライブラリ。既知の型を適切に処理する良い `print` 定義があります。`map`、`length`、および同様の汎用関数が含まれています。
機能
バインディング、関数、整数、文字列、ラムダ、リストなどのサポート。ラムダはクロージャをサポートします。`int?` や `cons?` などの関数による実行時型検出。ヒープに割り当てられたコンスセル用の、おおまかで簡潔なバンプアロケータ。数学演算: `+`、`-`、`*`、`/`、`%`。比較演算: `=`、`<`、`<=`、`>=`、`>`、および結果を反転する `!`。
特殊形式
* `(cond ..)`
* `(defun ..)`
* `(do ..)`
* `(if ..)`
* `(lambda ..)`
* `(let ..)`
* `(list ..)`
* `(set! ..)`
組み込みの特殊形式と、アセンブリまたはSlisp自体で実装されている標準ライブラリの部分の両方を文書化した、プリミティブの完全なリストと詳細については、`PRIMITIVES.md` を参照してください。
アンチ機能:
* ガベージコレクションなし。マクロなし。追加するのは不可能ではありませんが、`quote`、`quasiquote` などがないと、多くの作業が必要です。
* `quote` なし。コンパイラとして `eval` を呼び出せる場合にのみ本当に役立つでしょうか?それは簡単には起こりません。
使用方法
コンパイラをビルドします:
```bash
go build .
```
プログラムのコンパイルとリンクにそれを使用します:
```bash
./slisp example.lisp > example.s
nasm -f elf64 example.s
ld -o example example.o
```
最後にプログラムを実行します:
```bash
./example
```
プロのヒント
現在のディレクトリにある `*.lisp` ファイルは、次を実行するとすべてコンパイルされます。
```bash
make clean all
```
これにより、手動でのリダイレクト、アセンブル、またはリンクの必要がなくなります。また、`example.lisp` ファイルも実行されますが、「`make clean example`」だけでも同様に行われ、整理されます。
テスト
`test/` の下にはいくつかの機能テストプログラムがあり、固定プログラムをコンパイルし、その出力を既知の良好な結果と比較します。これらのテストは次を実行して実行できます。
```bash
cd test && make test
```
トップレベルで `make clean` を実行すると、テストの成果物とコンパイルされたプログラムが削除されます。機能テストに加えて、内部実装パッケージのGo言語テストも存在し、これらは通常の方法で実行できます。
```bash
$ go test ./...
ok github.com/skx/slisp 0.004s
ok github.com/skx/slisp/compiler 0.009s
ok github.com/skx/slisp/env (cached)
ok github.com/skx/slisp/lexer 0.008s
ok github.com/skx/slisp/parser 0.006s
```
Go言語が提供するファズテストのサポートも存在します。次を実行することで、5分間のファズテストを実行できます(`-fuzztime=300s` を削除すると永久に実行され、`-parallel=1` を削除すると複数のインスタンスを同時に実行できます)。
```bash
$ go test -fuzztime=300s -parallel=1 -fuzz=FuzzProject -v
```
動機
私は自家製言語s-langのコンパイラを数週間かけて作成しました。当初、この言語は整数のみを使用していましたが、後に値の下位ビットに適切な型マーカーを付けてフロート/文字列/ポインタを追加しました。タイピングと構文を扱うオーバーヘッドが少し複雑だと感じ、それによって自分を追い詰めてしまいました。ファイルI/O、getenv、その他の機能を備えたかなり完全な標準ライブラリを作成しました。しかし、さらに多くの型や動的なものを追加することは、これまでに行ったことの多くを根こそぎ変えることになるため、複雑すぎると感じました。コンパイラ、標準ライブラリ、そしてその2つの間のインターフェースです。そこでこのリポジトリが誕生しました。
* ゼロから適切な型付けを備えたコンパイラを実装する。
* 読みやすさのため、また間違いを犯す可能性を最小限に抑えるためにマクロを使用する。
* 自家製の代替案ではなく、よく知られたSysV ABIを使用する。
* 構文の解析が簡単なのでLispを使用する。そして、過去にLispのインタプリタを書いてきたので、ドラゴンはいますが、多少は友好的なものです。
すでにこのコンパイラは、s-langの品質、標準ライブラリ、テストケース、創造性には欠けるものの、より「現実的」で「使用可能」です。結局のところ、どちらもおもちゃであり、どちらも私自身の個人的な学習のためにここにあります。