Gauche:Translation:Devlog:普遍的な変換器のサポート

Gauche:Translation:Devlog:普遍的な変換器のサポート

(原文: Supporting general transformers: Step 1)

Gauche の開発はストレッチドブートストラッププロセスの一種です。 最初は VM 全体とコンパイラ、それとほとんどの組込み手続きが C で (それと、スタブコード生成のために少しばかり STK の助けを借りて) 書かれました。 そのプロダクションで Gauche を使うには最初から相当な性能を必要としたからです。 当初の VM はよくチューニングされておらず、最初から Gauche でコンパイラを書いていたとしたら、未熟なバージョンは要求に対してはあまりにも遅過ぎたでしょう。

私は次第に VM (まだ C だがほとんどの部分はS式を使ったDSLのようなもので書かれている)、それからコンパイラ (ほとんど完全に今の Gauche で書かれた) を書き直しました。 コンパイラの初期の最適化のターゲットはコンパイラ自体であり、それはうまくいきました。 組込手続を Scheme で段階的に書換えるという工程で、いつでもそうすれば全体的なパフォーマンスには影響しませんでした。

そのようにして VM とコンパイラが書き直されました。 しかし、手つかずのままつのコンポーネントがあります。 syntax-rules 展開器です。 それは私があえて触れなかった C コードの嫌なスパゲッティです。 ポータブルな Scheme の展開器はありましたが、 Gauche 上で実行するには最適ではないことを私は恐れていました。 Gauche はオンザフライでコンパイルする必要があるので、高速なマクロエクスパンダを必要とするのです。 Gauche のランタイムの優位性を活かすためにチューニングされた展開器をスクラッチから書こうと計画しました。

私が今日コミットした変更はマクロのサブシステムを書き換える最も初期の段階です。 まだ古い syntax-rules 展開器を使用しますが、コンパイラから展開器を分離します。 以前、コンパイラは syntax-rules を define-syntax 等の一部として認識していました。 つまり、 syntax-rules フォームだけではコンパイラにとって何の意味もありませんでした。

gosh> (syntax-rules () [(_ x) 'x]) *** ERROR: unbound variable: x

今は syntax-rules 自体はマクロの変換器に評価するマクロです。

gosh> (syntax-rules () [(_ x) 'x]) #<macro #f>

define-syntax, let-syntax そして letrec-syntax のような構文的な束縛は、それがコンパイル時の環境 (マクロ変換子が生成されることが想定される) で右辺を評価してから、与えられた名前に構文的な束縛をするように変更されます。

★★★

この変更が公式に別名をもった構文/マクロのキーワードをサポートするのは興味深い結果です。

Gauche はコンパイル時のグローバルな束縛と実行時のグローバルな束縛を分けておらず、構文/マクロのキーワードを評価すると、第一級の値として構文ハンドラはマクロ変換子かを表示します。

gosh> if #<syntax if> gosh> let1 #<macro let1>

それらが実行時束縛であるかのように、別のグローバル変数に変換子を再束縛することができます。

gosh> (define xif if) ; don't do this!

Gauche はオペレーションの通常モードでそれを実行する前に各トップレベルフォームの右側をコンパイルするという事実に依存した意図せぬ産物です。

フェイズを混ぜてしまい、コンパイルと実行のタイミングが変更されたときに予期せず中断されるので、推奨されません。

実際、両方のフォームが単一のトップレベルフォームで囲まれている場合に機能しません。

gosh> (begin (define yif if) (yif #f (error "oops") 'ok)) *** ERROR: oops

これらのフォームを持つファイルがプリコンパイルされている場合にも動作しません。

(プリコンパイルは正式に文書化されていません。 これらはまだ一般的なケースで信頼性に欠ける動作があるためです。 しかし、かなりの数の組込手続やコンパイラ自体が VM の命令列にプリコンパイルされています)。

今日の変更によって define-syntax 等の右辺はマクロ変換子 (または構文ハンドラ; 私は現時点でふたつの違いの詳細に入り込みたくはありません) を生じる任意の Scheme 式とすることが出来ます。 今、これは構文/マクロのキーワードに別名を与える行儀の良いな方法です。

gosh> (define-syntax zif if)

私はまだマクロ変圧器のインタフェースを熟考しています。

内部的には、現在の実装はソース形式とコンパイル時環境を受け取る手続を使用し、可能な限りの構文アノテーションと共に展開形を返します。

しかし、コンパイル時環境はコンパイラへのプライベートな構造であり、プログラマにそのような内部の仕掛けを露出したくないので、良い抽象によって変換子を覆いたい。

可能な選択肢はR6RSで定義されているモデルです。 ---マクロの変換子は構文オブジェクトを受け取り、構文オブジェクトを返す手続きです。

これ自体は OK です (ソース形式とコンパイル環境を不透明なオブジェクトに一緒に押し込めることができる)。

しかし、 R6RS を読むにつれ、その仕様に従いたくなくなる。

変圧子が呼び出される様々な方法(フォーム全体または単にキーワードを受け取るかどうか、またはキーワードが set! の左辺に現れることができるかどうか) とそれが使用されるかを決定する規則はややアドホックに見えます。

私はそれらのバリエーションは識別子マクロや変数変換子をサポートするために必要であると理解しています。

ワンセンテンスで言うことができるような単純な公理の上にユーティリティ API として設計されているようただ願う次第です。

実際には、R6RS はこれらの変換器 API 唯一の API にしようとしているのか、使える複数の API のひとつにしようとしているのか私にはわかりません。 変換器手続の定義はメイン報告書には少しもなく、むしろライブラリ報告書にあります。 変換器のこの特定の定義がライブラリ (rnrs syntax-case (6)) に所属しているかのようです。 もし他のライブラリをインポートしたら、実装は異なる変換器インタフェイス実装が提供することが許されるのでしょうか。 例えば explicit renaming のような。 それは define-syntax などがライブラリがインポートされている変換器手続の依存を切り替える必要があることから、単純には見えません。 変換器ライブラリは構文上の束縛フォームを提供するか、またはどんなシステム内部変換器 API でも明示的変換器 API に変換するような中間マクロを提供するのが合理的です。

(define-syntax foo
  (sc-transformer
    (lambda (expr)
      (syntax-case expr
        ....))))

MIT-scheme や他のいくつかの実装がこれに似たものをもっていると思います。

おそらく私はこのアプローチを取るつもりです。 私の現在の計画では、基礎として explicit-renaming 変換器を提供し、その上に syntax-case を構築することです。 アレックスシンの Chibi-Scheme はそうしておりますし、私は信頼しています。 ただひとつの問題は十分に速いそれを得ることです。 それを見るためには私が実装する必要があります。


Last modified : 2013/03/20 21:21:27 UTC