kiyokaのブログアーカイブ

Archive of old blog posts

syntax-rulesが動いた日

オレ処理系の[Nendo]についての開発メモ。 img chibi-scheme 0.3のer-macro-transformerとsyntax-rulesをそのままNendoに移植を試みていたのだが、ついに成功した。

make-syntactic-closureの実装方法は別段難しくなかった。 トップレベルにbindされた変数なら、その識別子をシンボルで返し、そうでなければ(gensym)したシンボルを返せばいいだけだった。 Rubyで書いた関数なので、これだけでは意味がわからんだろうけど、雰囲気だけでも伝わるかと思うので、コードを貼っておく。

    def _make_MIMARKsyntactic_MIMARKclosure( mac_env, use_env, identifier )
      if _pair_QUMARK( identifier )
        raise RuntimeError, "Error: make-syntactic-closure requires symbol only..."
      else
        if mac_env.to_arr.include?( identifier )
          identifier
        else
          sym = toRubySymbol( identifier ) + _gensym( ).to_s
          sym.intern
        end
      end
    end

苦労した主な敗因は、chibi-schemeのsyntax-rulesの定義を自分がよく理解しないままポーティングしようとしていたことだ。 こんな複雑な手続きが、一発で動くほど甘い世界ではない。 結局、デバッグコード挿入しつつ少しづつ原因を追いこんでいった。

Nendoにポーティングする時に考慮する必要があったのは以下の点。

* … というシンボルをあらかじめグローバルに定義しておく必要があった

syntax-rulesを評価する直前に、次のような定義を置いた。

(define ... '...)

* NendoのScheme非互換仕様に対しての手当が必要だった

マクロ展開時 v.1 や v.2 のようなローカル変数を抑制する必要があった。 Nendoには instance.method の様な表記をRubyのメソッド呼び出しとして特別扱いしている。 (これは、Schemeとは非互換な部分である)

Nendoでは syntax-rulesで展開したコード中の v.1 などの形式が vというインスタンスの1というメソッド呼び出しになってしまう。 まあ、数値リテラルのメソッドなんてものは無いので、v.1のような表記は特別に:”v.1”というシンボルとして扱えばいいのだけど、そういう例外も気持ち悪いので特例は入れないつもり。 結局 v__1 や v__2 のような変数名を生成するように変更した。

 (rename (string->symbol (string-append "v." (number->string count)))))

 (rename (string->symbol (string-append "v__" (number->string count)))))

* chibi-scheme 0.3のany関数にバグがあった。

chibi-scheme 0.3のsyntax-rulesの定義中で何回か any が使用されているが、実はchibi-schemeのanyにはバグがある。(これは、chibi-schemeのMLでバグ報告しないといけない) これを見つけるのに苦労したのだよ。 次のような場面で使われている。

(define-syntax syntax-rules
  (er-macro-transformer
      .
      .
             (cond
              ((any (lambda (v) (compare t (car v))) vars)
               => (lambda (cell)
                    (if (<= (cdr cell) dim)
                        t
                        (error "too few ...'s"))))
              (else
               (list _rename (list _quote t)))))
      .
      .

anyで見つかったcellを使って、(cdr cell)とかしちゃっているが、これは間違い。 本来のanyは条件にマッチした値を返すわけではなく、リスト中に条件を満たすものが1件でもあれば #t を返す。 要はchibi-schemeのanyはfindの動作をしちゃっているようだ。

証拠を示そう。 SRFI 1: List Libraryのanyの定義 Note the difference between find and any – find returns the element that satisfied the predicate; any returns the true value that the predicate produced.

    (any integer? '(a 3 b 2.7))   => #t
    (any integer? '(a 3.1 b 2.7)) => #f

とうわけで、このように修正した。 誤り:

              ((any (lambda (v) (compare t (car v))) vars)

正解:

              ((find (lambda (v) (compare t (car v))) vars)

参考:

 $ chibi-scheme
> (any even? '(1 2 3 4))
2

 $ gosh
gosh> (any even? '(1 2 3 4))
#t

 $ nendo
nendo> (any even? '(1 2 3 4))
#t
nendo> (use srfi-1)
20000
nendo> (any even? '(1 2 3 4))
#t

さて、これでsrfi-2とかGaucheのutil.listがポーティングできるぞー。 Nendoでできる世界がさらに広がること間違いナシ。 高い壁をひとつ越えた気がする。すごい充実感。