Let's start Scheme

2013-02-11

マクロ展開のタイミング

R6RSポータブルなコードを書く際に、書く処理系の癖を熟知してなんてことよほど暇じゃないと出来ないだろうということに気付いた。現状で一番問題になっているのはマクロ展開フェーズ周りの処理である。

Sagittariusはマクロ展開フェーズなんてまどろっこしいものを持たないのだが、なんとかしてそれっぽくエミュレートできないかということちょっと知恵を搾り出し中(正直、搾取しすぎて搾りかすしか出ないが・・・)。とりあえず、library内のtoplevelなdefine-syntaxについては強引に先にやってしまうおう的に解決。(一度library内のtoplevelにある式を全部舐めて、define-syntaxだけ先にコンパイルするという割とお粗末な解決方法。なので、マクロ展開後に出てくるdefine-syntaxとかは対応してない。やろうと思えば出来る気もするが・・・)。頑張ってもう少し賢くした。R6RSより多少制限がゆるいけど。

問題になってくるのは局所マクロである。

R6RSでは以下のようなコードが動かないといけない。
(import (rnrs))
(let ()
  (define-syntax define-inline
    (syntax-rules ()
      ((_ (name . args) body ...)
       (define-syntax name
         (syntax-rules ()
           ((_ . args)
            (begin body ...)))))))

  (define (puts args) (display args) (newline))
  (define-inline (print args) (puts args))
  (print "abc"))
;; abcを表示

(let ()
  (define-syntax define-inline
    (syntax-rules ()
      ((_ (name . args) body ...)
       (define-syntax name
         (syntax-rules ()
           ((_ . args)
            (begin body ...)))))))
  (define-inline (print args) (puts args))
  (define (puts args) (display args) (newline))
  (print "abcde"))
;; abcdeを表示
最初のパターンは何とか(そこそこスマートに)解決できたんだけど、次のパターンのうまい方法が思いつかない。

起きていることそしては、define-inlineで展開されたprintはputsが内部defineとして保持される前にコンパイルされる。そうするとprintのマクロ展開器はputsが内部defineだと知らないので展開器は特に環境情報を付加することなくシンボルputsを識別子に変換する。っで、コンパイラは何も持ってない識別子を大域変数と解釈しコンパイルする。

コンパイラが上記のputs識別子から内部defineのputsを参照できればいいのだが、本当に何の情報も持っていない識別子の参照を許すとマクロのhygieneを壊してしまうのでうかつなことはできない。さて、どうしたものか・・・

No comments:

Post a Comment