R6RS-Lite → R5RS へのlowerをしてみる
R6RS-liteはSchemeの1世代前の標準であるR6RSのライブラリ形式をサブセットしたもので、R5RSやそれ以前のSchemeを想定して機能を削っている。
現状では、これをSchemeのプログラムにlowerするマクロ群を実装して "selfboot" フロントエンドとしている。これを使うと、yuniがサポートしているScheme処理系の殆ど( https://github.com/okuoku/yuni/blob/9335f8b968a19d48f93c860338902ffe9843f064/CMakeLists.txt#L114-L138 )で共通のスクリプトを起動できる。
...が、一部の処理系というかCycloneだけはこの方法で実装できない。
変換の概要
Lowerとは、プログラムをより抽象度の低い方向に変換することを指す。最近のScheme標準、つまり R6RSやR7RSはプログラムを Nつのライブラリと1つのプログラム によって構成する。それ以前のSchemeにはライブラリの標準仕様が存在しなかったため、 1つのプログラム を実行することだけが共通集合であった。
で、R6RS(-lite)プログラムをR5RSにlowerするとは、プログラムを実行するために必要なライブラリと実際のプログラム本体を合成して1本のプログラムに変換してしまうことを指す。
変換は以下のようになる: ライブラリ (lib-a)
と (lib-b)
に依存しているプログラムがあったとして、
(library (lib-a)
(export do-a)
(import (yuni scheme))
(define-syntax do-a ...))
(library (lib-b)
(export do-b)
(import (yuni scheme))
(define (do-b) ...))
;; プログラム
(import (lib-a) (lib-b))
(do-a 10)
(do-b 20)
これらの3ファイルを一つにして、
;; ★ マクロ(syntax)をexportするライブラリはそのまま出力
(define-syntax do-a ...)
;; ★ マクロをexportしないライブラリは何らかの方法でexportされていない
;; シンボルが見えないように努力する
(let ((do-b #f))
(define (__do-b) ...)
(set! do-b __do-b))
;; プログラムはそのまま出力
(do-a 10)
(do-b 20)
出力されたものをそのまま実行する。Schemeの標準ライブラリは (yuni scheme)
ライブラリに纏めているので、このライブラリは省略できる。(実際には (yuni scheme)
ライブラリはR7RSの語彙を実装しているため、R5RSやR6RSな処理系で実行するには多少のpolyfillを出力する必要がある)
やってみた(手抜きで)
... そもそも (library ...)
から中身を抜いてロード順に並べるだけで 99.99%のユースケースは満たせるのでそれで良いや。。逆に言うとそれでダメなケースのテストを書かないといけないんだけど。