Scheme Compiler の勉強(0)

  • "Compilers: Backend to Frontend and Back to Front Again, Abdulaziz Ghuloum" http://www.cs.indiana.edu/~aghuloum/compilers-tutorial-2006-09-16.pdf を入手する。
  • まずは真面目にこの簡単なテキストを使って scheme の勉強がてら scheme コンパイラのおもちゃを作成する。このドキュメントは scheme の知識は入門レベルで大丈夫(と書いてある)。
  • せっかくなので llvm を使ってみる。
  • 最初は Common Lisp でやろうかと思ったけど、あまりに違いが多すぎるので最後まで終えてから考える。

まず準備。※以下は全然まとめずに、かつ llvmgauche も良くわかっていない状態で書いているので、信用しないこと。


  • 上記ドキュメント
  • compilers-tutorial-tests-2006-10-11.tar.gz テストスイート。
    • tests-driver.scm テストスイートのドライバー。コンパイラが出力したアセンブラを実行ファイルにして実行して結果を比較してくれる。
    • tests-n.n-req.scm このドキュメントだとインクリメンタルに実装する。その途中途中で満たすべきテストデータ。
  • compiler.scm これから書くコンパイラー本体。ここでは tests-driver.svm と同じ場所に置いた。
  • runtime.c テストするためのランタイム。これも少しずつ実装する。
  • scheme 実装。とりあえず gauche を利用。
  • llvm-gcc tests-driver.svm では gcc を使っているけど、llvm でトライするために準備。

第1段階、まずは tests-driver.scm を動かすまで

tests-driver.scm は実は ChezScheme 用に書かれている。「Scheme はちゃんとした仕様書があるからどの処理系でも気軽に動く」、とか思っていたけど、全然そんなことはない。そのままだと大量にエラーがでる。そんなときに使うのが、 http://practical-scheme.net/wiliki/schemexref.cgi の SchemeCrossReference。(使ったことはなかったけど、かなり前からこのクロスリファレンスの存在だけは知っていた。使ってみて素晴らしさが分かった。)

例えば素朴にロードするとこんな感じになるので、

gosh> (load "./tests-driver.scm")
*** ERROR: unbound variable: make-parameter
Stack Trace:
_______________________________________
  0  (make-parameter (lambda (x) x) (lambda (x) (unless (procedure? x)  ...
        At line 78 of "./tests-driver.scm"
gosh> 
  • クロスリファレンスで make-parameter を探す。

http://practical-scheme.net/wiliki/schemexref.cgi?make-parameter

という具合で少しずつ修正。そもそも自動化するためだけのものなので、自分で書き直してもいいとおもう。以下、scheme のど素人が、かなり適当に書いているので、間違いが有ったら教えて下さい。(個人的にはなんでこんな基本そうな手続きが標準化されていないのか、かなり疑問ですが・・・)

;; tests-driver.scm の修正
(use gauche.parameter)
;;(define printf format) ;; 修正。これだとちゃんと出力されない
(define (printf . x)
  (apply format #t x))
(define fprintf format)
(define flush-output-port flush)
(define system sys-system)
(define (fxzero? x) (and (fixnum? x) (zero? x)))
(define void (lambda () (values)))
(define add1 (lambda (x) (+ x 1)))
(define sub1 (lambda (x) (- x 1)))
(define (run-compile expr)
;;  (let ([p (open-output-file "stst.s" 'replace)])
  (let ([p (open-output-file "stst.s" :if-exists :supersede)]) ;; gauche と open-output-file のオプションが違う。
    (compile-program expr p)
    (close-output-port p)))

(define (build)
;; 多分 startup.c は runtime.c の間違い
;;  (unless (zero? (system "gcc -o stst startup.c stst.s"))
  (unless (zero? (system "llvm-gcc -o stst runtime.c stst.s"))
    (error 'make "could not build target")))
gosh> (load "./compiler.scm")
#t
gosh> (test-all)
"passed all 7 tests\n"
gosh> (load "./compiler.scm")
#t

とりあえず tests-1.1-req.scm の最後の二つ以外が通るようになった。(仕事がこれ以上忙しくならないかぎり)続く。