yuni: Cyclone 0.3xに対応する (未遂)
壊れ方
普通に実行すると、ライブラリ内のグローバル変数が参照できない:
("/opt/yunibase/current/cyclone/bin/icyc" "-A" "/opt/yunibase/current/cyclone/share/cyclone" "-s" "/home/oku/repos/yuni/lib-runtime/selfboot/cyclone/selfboot-entry.scm" "core0.sps")
"/home/oku/repos/yuni/lib-runtime/selfboot/cyclone/selfboot-entry.scm"
(LOADLIB "/home/oku/repos/yuni/lib/yunitest/mini.sls")
-> EXPAND-LIBRARY: (yunitest mini)
-> AUTOPROMOTE: (yunitest mini)
-> REALIZE-HOOK: (yuni/realize-library-hook (yuni scheme) #t)
-> SKIP: (yuni scheme)
-> REGLIB! (yunitest mini)
(LOADPROG "core0.sps")
(import: ((yuni scheme) (yunitest mini)))
-> REALIZE-HOOK: (yuni/realize-library-hook$869 (yuni scheme) #t)
-> SKIP: (yuni scheme)
-> REALIZE-HOOK: (yuni/realize-library-hook$869 (yunitest mini) #t)
-> LOOKUP (yunitest mini)
-> (yunitest mini) => #((yunitest mini) #t #t #f #f ())
(eval: (check-equal #t (boolean=? #t #t)))
Error: Unbound variable: %%yunitest-mini-test-counter++
Call history, most recent first:
[1] scheme/base.sld:raise
[2] scheme/base.sld:error
[3] scheme/cyclone/util.sld:env:_lookup-variable-value
[4] scheme/cyclone/util.sld:env:lookup-variable-value
[5] scheme/cyclone/util.sld:env:extend-environment
[6] scheme/cyclone/util.sld:pack-lambda-arguments
[7] scheme/cyclone/util.sld:formals->list
[8] scheme/cyclone/util.sld:tagged-list?
[9] scheme/eval.sld:execute-application
%%yunitest-mini-test-counter++
はエクスポートされない、ライブラリ (yunitest mini)
内のシンボル 。マクロ check-equal
が eval
できなかったのかな。
yuniがR5RS形式に展開したライブラリ
(begin
(begin
(yuni/realize-library-hook (yuni scheme) #t))
(begin
(define %%yunitest-mini-test-counter 0)
(define %%yunitest-mini-success-counter 0)
(define %%yunitest-mini-failed-forms (quote ()))
(define (check-finish)
(define (print-failed v)
(let ((expected (vector-ref v 0))
(actual (vector-ref v 1))
(frm (vector-ref v 2)))
(display "[") (write frm) (display "]")
(display " Expected: ") (write expected)
(display " Actual: ") (write actual)))
(display "Test: ")
(display %%yunitest-mini-success-counter)
(display "/")
(display %%yunitest-mini-test-counter)
(display " passed.\n")
(unless (null? %%yunitest-mini-failed-forms)
(display "\nFailed: \n")
(for-each (lambda (x)
(display " ") (print-failed x) (display "\n") #t)
(reverse %%yunitest-mini-failed-forms)))
(exit (if (null? %%yunitest-mini-failed-forms) 0 1)))
(define (%%yunitest-mini-test-counter++)
(set! %%yunitest-mini-test-counter
(+ 1 %%yunitest-mini-test-counter)))
(define (%%yunitest-mini-success-counter++)
(set! %%yunitest-mini-success-counter
(+ 1 %%yunitest-mini-success-counter)))
(define (%%yunitest-mini-proc-fail! expected actual frm)
(let ((v (vector expected actual frm)))
(set! %%yunitest-mini-failed-forms
(cons v %%yunitest-mini-failed-forms))))
(define-syntax check-equal
(syntax-rules ()
((_ obj form) (begin (%%yunitest-mini-test-counter++)
(let ((e form))
(cond ((equal? obj e)
(%%yunitest-mini-success-counter++))
(else
(%%yunitest-mini-proc-fail!
obj e (quote form))))))))))
(yuni/register-library! (quote (yunitest mini)) #f))
REPLでは問題なく実行できる
REPLでは問題なく実行できるので、ライブラリ自体の問題ではないようだ。プログラムの load
手法に問題があるのかな。
単純ケース
cyclone> (begin
(begin
(define (a) (display "a!\n"))
(define-syntax do-a
(syntax-rules ()
((_ "a") (a)))))
(write "done.\n"))
"done.\n"
cyclone> (do-a "a")
a!
実際のライブラリケース
cyclone> (define-syntax yuni/realize-library-hook (syntax-rules () ((_ . x) 'ok)))
ok
cyclone> (define (yuni/register-library! . x) 'ok)
ok
cyclone> (begin
(begin
(yuni/realize-library-hook (yuni scheme) #t))
(begin
(define %%yunitest-mini-test-counter 0)
(define %%yunitest-mini-success-counter 0)
(define %%yunitest-mini-failed-forms (quote ()))
(define (check-finish)
(define (print-failed v)
(let ((expected (vector-ref v 0))
(actual (vector-ref v 1))
(frm (vector-ref v 2)))
(display "[") (write frm) (display "]")
(display " Expected: ") (write expected)
(display " Actual: ") (write actual)))
(display "Test: ")
(display %%yunitest-mini-success-counter)
(display "/")
(display %%yunitest-mini-test-counter)
(display " passed.\n")
(unless (null? %%yunitest-mini-failed-forms)
(display "\nFailed: \n")
(for-each (lambda (x)
(display " ") (print-failed x) (display "\n") #t)
(reverse %%yunitest-mini-failed-forms)))
(exit (if (null? %%yunitest-mini-failed-forms) 0 1)))
(define (%%yunitest-mini-test-counter++)
(set! %%yunitest-mini-test-counter
(+ 1 %%yunitest-mini-test-counter)))
(define (%%yunitest-mini-success-counter++)
(set! %%yunitest-mini-success-counter
(+ 1 %%yunitest-mini-success-counter)))
(define (%%yunitest-mini-proc-fail! expected actual frm)
(let ((v (vector expected actual frm)))
(set! %%yunitest-mini-failed-forms
(cons v %%yunitest-mini-failed-forms))))
(define-syntax check-equal
(syntax-rules ()
((_ obj form) (begin (%%yunitest-mini-test-counter++)
(let ((e form))
(cond ((equal? obj e)
(%%yunitest-mini-success-counter++))
(else
(%%yunitest-mini-proc-fail!
obj e (quote form))))))))))
(yuni/register-library! (quote (yunitest mini)) #f))
ok
cyclone> (check-equal #t #t)
ok
cyclone> (check-finish)
Test: 1/1 passed.
意図せずrenameされている
どうも環境に正しく反映されていないようだ。REPLとプログラムの実行時の両方で expand
して比較してみる:
cyclone> (expand '(check-equal #t #t))
((lambda () (%%yunitest-mini-test-counter++) ((lambda (e$94$96) (if (equal? #t e$94$96) ((lambda () (%%yunitest-mini-success-counter++))) ((lambda () (%%yunitest-mini-proc-fail! #t e$94$96 (quote #t)))))) #t)))
REPLでは、 %%yunitest-mini-test-counter++
と正しく展開されるが、プログラムから呼ぶと、
((lambda () (%%yunitest-mini-test-counter++$929) ((lambda (e$930$934) (if (equal? #t e$930$934) ((lambda () (%%yunitest-mini-success-counter++$931))) ((lambda () (%%yunitest-mini-proc-fail!$933 #t e$930$934 (quote (boolean=? #t #t))))))) (boolean=? #t #t))))
のように、 %%yunitest-mini-test-counter++$929
とリネームされてしまう。
よって、プログラムを eval
する際のコンテキストが正しくないように見える。
(lambda () ...)
でwrapされている
そもそも ↑ の出力をよく見ると、 check-equal
の展開結果が (lambda () ...)
で wrapされている。これだとスコープを作ってしまうので define
で定義した変数が外部からアクセスできなくなってしまう。
というわけで、Cyclone側のexpanderを使わずに手動でライブラリを展開するしか無いようだ。。一応yuniは define-macro
だけで実装できる専用のランタイム(Generic Scheme)を持っているが、Cycloneの仕様だと define-macro
すら使えないのでかなり厳しい戦いになりそうだ。。
ただ、非常に単純な syntax-rules
をREPLで試す限りはwrapされないので、何か条件があるような気がする。 begin
が最適化されないケースが不味いのかな。。
cyclone> (define-syntax define10 (syntax-rules () ((_ nam) (define nam 10))))
ok
cyclone> (define10 a)
ok
cyclone> a
10
cyclone> (expand '(define10 a))
(define a 10)
どうせ後で必要になるし、 define-macro
なしのSchemeでも使える実装を用意するか。。