initial gauche
アケオメ コトヨロ
と、新年のご挨拶を 和文 で、お送りいたします。
去年のクリスマスのお菓子が賞味期限切れにならない内に味わっておく。
https://qiita.com/advent-calendar/2019 が、起点だ。選り取り見取りだぞ。
sudo がアレなので doas に乗り替えてみるの元原稿が公開されてた。これだけ書くとSDの人に眼を付けられるのか。 今年は、雑誌デビュー目指して頑張ってみるかな。
有馬記念を血統だけで予想する 結果の正統性について、どなたか評論をお願いします。お菓子を肴に談義もいいかも。
Rstanってのが有名みたいだけど、たたき台として
julia> using Stan [ Info: Precompiling Stan [682df890-35be-576f-97d0-3d8c8b33a550]
こんな事を始めてみた。そこまではいいんだけど、さっぱり準備が整わない。
Smalltalk-72で学ぶOOPの原点だとか。gaucheを見ていると、避けて通れない気がするので、原典に当たってみるのもいいかも。
git で昔へ行く
さて、去年の最後にGaucheのVMを見ようとした。コンパイラも3フェーズから5フェーズに拡張されてたりして、いささか荷が重い。そこで思い付いたのがgitは歴史博物館だと言う事。
タイムマシンに乗って過去に行ったら、簡単なVMが見られるんじゃなかろうかと言う発想。石の世界に置き換えてみれば、X64な石は複雑過ぎて手に負えないから、世界初の石 i4004 なる電卓もどきのCPUを調べてみましょうと言う訳ね。
gitで過去の物を取り出す方法は分かったけど、現代に帰る方法は(勉強不足のため)知らない。よくTVでJKなオネーちゃんが過去へタイムスリップして、ごちゃごちゃやってる番組を見るけど、最後は現代へ無事に帰って来るよね。
自信が無いので、Gaucheのgitを/tmpへ丸ごとコピー。そこで
(base) sakae@debian:Gauche$ git checkout initial : (base) sakae@debian:Gauche$ ls doc gc src
docには決意表明が、gcはガベコレ込みのmallocが、srcは極少な目になってます。このままでは、取り回しが大変なので(.gitを含めて60Mもある)、新たにsmallってdirに、上記3つのdirを丸ごとコピーしておきます。
(base) sakae@debian:Gauche$ cd gc (base) sakae@debian:gc$ make cc -O -DATOMIC_UNCOLLECTABLE -DNO_SIGNALS -DNO_EXECUTE_PERMISSION -DALL_INTERIOR_POINTERS -DSILENT -DCORD_MB -c -o alloc.o alloc.c In file included from gc_priv.h:48, from alloc.c:19: gcconfig.h:311:2: error: expected identifier or ‘(’ before ‘--’ token --> unknown machine type ^~ make: *** [<builtin>: alloc.o] Error 1
根幹を成すgcがコンパイル出来るか64Bit版のdebianで試したら、あえなく撃沈。この頃は まだ、Linuxでは64Bit版が動かなかったんですかね。以降は、32Bit版のdebianで試す事にします。
32Bit版で試してみると、C語のソース作成に謎のschemeであるsnowってのが使われていた。 これをクリア出来ない限り、復元出来ないってのが、去年最後の投稿であった。
尚、下記はリリース日の年表になります。これを見ながら任意の時代へ簡単にタイムスリップ出来るので、自由に楽しんでください。
(base) sakae@debian:Gauche$ for t in `git tag` > do > echo -n "$t " > git show $t | head -3 | tail -1 > done | grep release release0-2-11 Date: Mon Mar 26 10:28:23 2001 +0000 release0-2-12 Date: Tue Mar 27 10:52:36 2001 +0000 : release0_9_5 Date: Sat Oct 8 02:10:44 2016 -1000 release0_9_6 Date: Tue Jul 3 01:40:01 2018 -1000 release0_9_7 Date: Thu Dec 20 07:50:35 2018 -1000 release0_9_8 Date: Sat Jun 15 18:59:18 2019 -1000 release0_9_9 Date: Fri Dec 13 11:50:49 2019 -1000
STk
突然だけど、虫の知らせか前から気になってたOpenBSDのportsにある、 STkの配布物を見ていたんだ。そしたら配布物にsnowなんんてコマンドが用意されてた。これってgaucheの初期バージョンを作る時に必要なSCMなんかな? 入れてみた。
ob$ man -M. snow : NAME stk, snow - A Scheme interpreter using the Tk toolkit : DESCRIPTION Stk is a Scheme R4RS interpreter which provide a simple access to the X11 Tk toolkit. If the -no-tk option is provided to the interpreter, the Tk library is not initialized and no main window is created. If stk is invoked with no -f option then it reads Scheme expressions interactively from standard input. It will continue processing commands until all windows have been deleted or until end-of-file is reached on standard input. Snow is a light version of the stk interpreter which does not provide support for the Tk toolkit. This interpreter does not recognize the options -display, -geometry, -sync -colormap, -visual and -no-tk which are meaningless without Tk. This interpreter is called, rather than the standard one, when the shell DISPLAY variable is not initialized.
どうやら微妙だな。gitでgaucheの初期バージョンを作り試してみたよ。結果はあえなく失敗。モジュールが無かった。多分snow違いなんだろうね。悔しいので、コードを鑑賞しておく。
ob$ cat hello.stk #!/bin/sh :;exec /usr/local/bin/stk -f "$0" "$@" : (button '.hello :text "Hello, world" :command (lambda () (display "Hello, world\n") (destroy *root*))) (pack .hello)
素直にTkを使ってみましたってコード。むき出しのTkが出てきている。
ob$ cat hello.stklos #!/bin/sh :;exec /usr/local/bin/stk -f "$0" "$@" : (require "Tk-classes") (define b (make <Button> :text "Hello, world" :command (lambda () (format #t "Hello, world~%") (destroy *root*)))) (pack b)
こちらは、ほらgaucheのあれにそっくりじゃん。それもそのはず、gaucheが範を求めたと言うオブジェクトシステムだ。
この他にも、3次元の五目並べやら、洋式王様並べ(8-queens)やら亀の軌跡(turtle)が居たりして、プチ正月気分。
snow to gosh
表題を日本語に変換すると、北国から常夏のハワイ行きとなる。雪は北国でしょ。goshはハワイ生まれでしょって訳。雪国に住む人の憧れだ。
この時期、芸能人や裕福な連中は、皆ハワイへ行くんだよな。ゴルフをやりに、あるいはダイヤモンドヘッドで泳ぎに、あるいは危ない薬に手を出すためにとね。 治外法権なハワイ県を十分に楽しんでくれ。
そう言えば、甥っ子が新婚旅行でハワイへ行ったそうな。昔は新婚旅行と言えば熱海か宮崎県の青島が定番だったな。今はハワイかオーストラリアだものね。行きの飛行機に修学旅行生が乘っててうるさかったと言ってたな。
写真を見せてもらった。ベランダからダイアモンドヘッド方面を写したやつ。この景色、見覚えあるぞ、確かシェラトンホテルでしょって言ったらビンゴでした。フロントに大きなウミガメが展示してあるホテル、懐かしいな。(オイラーも昔行った事が有るのさ。オアフ島よりハワイ島の方が記憶に深く残ってる)
脱線しちゃったので、軌道修正します。 上で書いたようにSTkにそれらしいコマンドが用意されてたけど、どうも違うようだ。そこでsnowをgosh(Ver 0.9.9)に変えて動かしたい。謎のsnow、そしてどんな結果が欲しいかも分からない八方ふさがり状態。人間が、教師無し学習に挑戦します。
いわば、土の中から発掘してきたコード片を繋ぎ合わせて、過去の遺物の復元に挑戦です。 まずは素直にMakefileでsnowからgoshに変更。
debian:src$ make gosh genstub stdlib.stub *** ERROR: cannot find "textutils" in ("/usr/local/share/gauche-0.97/site/lib" "/usr/local/share/gauche-0.97/0.9.9/lib") While compiling "./genstub" at line 12: (require "textutils") While loading "./genstub" at line 12
そんなファイル無し攻撃。無い物はしょうがないので無視しましょ。
gosh genstub stdlib.stub *** ERROR: make-hash-table requires a comparator or one of the symbols in eq?, eqv?, equal? or string=?, but got: () While loading "./genstub" at line 329
次はhas-tableの作成部分でエラーです。比較関数にeq?とかが来るべきなのに空リストが来てるよってエラー。
そこで、gaucheのマニュアルを見てhash-tableの作り方を確認。
(define *type-table* (hash-table-r7 'eq? :pair '("ScmObj" "SCM_PAIRP" #f "pair") :list '("ScmObj" "SCM_LISTP" #f "list") :
keyとvalueが交互に並んでいる(交代リストとか言うらしい)形式になってたので、r7rs風な指定をしてあげたよ。昔は(key . value)ってのしか無かったらしいけどね。
gosh genstub stdlib.stub *** ERROR: unbound variable: strpad While loading "./genstub" at line 352 Stack Trace: _______________________________________ 0 (strpad (car *argv*) -5) at "./genstub":350 1 (strpad (car *argv*) -5) at "./genstub":350 2 (string=? (strpad (car *argv*) -5) ".stub") at "./genstub":350
ここら辺から泥臭い変換が始まります。入力ファイル名を受理して、元ファイル名から色々な名前を作り出している風。コードを眺めて、直に設定しちゃいました。これが後で矛盾してたらバックトラックの憂き目に会うと言う、ハラハラドキドキな賭けです。競馬並みに予想して賭ける作業ですよ。
まあ、うじうじしててもしょうがないので先に進めると、plintlnが無いとか言い出した。そんなの簡単、自分でplintlnを書いちゃえ。でも、良く使われ方を見たら、format風になってたぞ。素直に、数十ヶ所あるやつを書き換えた。勿論使ったのは、emacsの M-% ね。
そう言えば、emacsでこのgenstubを開いても、schemeのスクリプトとして認識してた。ファイルの最後に書いてある
;; Local variables: ;; mode: scheme ;; end:
これが効いているのね。知らなかったぞ。
gosh genstub stdlib.stub *** ERROR: string required, but got eqv? While loading "./genstub" at line 373 Stack Trace: _______________________________________ 0 (get-c-name *file-prefix* (scheme-name-of cproc)) at "./genstub":65 1 ((setter c-name-of) cproc (get-c-name *file-prefix* (scheme-n ... expanded from (set! (c-name-of cproc) (get-c-name *file-prefix* (scheme-na at "./genstub":64 2 (process-define-cproc form) at "./genstub":368 3 (with-input-from-file file (lambda () (let loop ((form (read) ... at "./genstub":363 make: *** [Makefile:20: stdlib.c] Error 70
悩んでしまったぞ。文字列が欲しいのに比較系が気ているとな。なんか、とんでもない齟齬が有るのだろうか? 冷静になってコードを眼トレースする。
そして値を知りたい所に #?= で、プリント文を入れる。どうやら、プログラムが走り始めて、最初のデータ eqv? を処理しようとして、エラーになってる事が分かった。 前にもこういうパターンが有ったね。人間goshに成り切れが大事だと思うぞ。
ここまで書いてきて、続きを書くと長くなるんで、Live debug は、これぐらいにするかな。 後は、修正したのを挙げておくんで、見てくれ。(genstub.gz)
stdlib.c
makeしてみる。色々と警告が出て来たけど、どうやらgoshが出来上がったようだ。
debian:src$ make : ar r libgauche.a core.o vm.o compile.o class.o boolean.o string.o list.o port.o hash.o write.o read.o vector.o symbol.o module.o proc.o number.o load.o stdlib.o ar: creating libgauche.a cc -g -I. -I../gc -c -o main.o main.c cc -g -I. -I../gc -o gosh main.o -L. -lgauche ../gc/gc.a -lm
散々迷惑をかけてくれたsnowの成果物を確認
debian:src$ less stdlib.c /* Generated by genstub. Do not edit. */ /* source: stdlib.stub */ #include <gauche.h> static void stdlib_eqvP(ScmObj *SCM_FP, int SCM_ARGCNT, void *data_){ ScmObj obj1_scm; ScmObj obj1; ScmObj obj2_scm; ScmObj obj2; SCM_ENTER_SUBR("eqv?"); obj1_scm = SCM_ARGREF(0); obj1 = obj1_scm; obj2_scm = SCM_ARGREF(1); obj2 = obj2_scm; { SCM_RETURN(Scm_EqvP(obj1, obj2)); }} static ScmString stdlib_eqvP_NAME = { SCM_CLASS_STRING, 4, 4, "eqv?" };static ScmSubr stdlib_eqvP_STUB = { SCM_CLASS_SUBR, 2, 0, SCM_OBJ(&stdlib_eqvP_NAME), stdlib_eqvP, NULL}; static void stdlib_eqP(ScmObj *SCM_FP, int SCM_ARGCNT, void *data_) ....
人様が読めるような物じゃない。そんな時にはindentで整形よ。
debian:src$ indent stdlib.c debian:src$ less stdlib.c /* Generated by genstub. Do not edit. */ /* source: stdlib.stub */ #include <gauche.h> static void stdlib_eqvP (ScmObj * SCM_FP, int SCM_ARGCNT, void *data_) { ScmObj obj1_scm; ScmObj obj1; ScmObj obj2_scm; ScmObj obj2; SCM_ENTER_SUBR ("eqv?"); obj1_scm = SCM_ARGREF (0); obj1 = obj1_scm; obj2_scm = SCM_ARGREF (1); obj2 = obj2_scm; { SCM_RETURN (Scm_EqvP (obj1, obj2)); }} static ScmString stdlib_eqvP_NAME = { SCM_CLASS_STRING, 4, 4, "eqv?" }; static ScmSubr stdlib_eqvP_STUB = { SCM_CLASS_SUBR, 2, 0, SCM_OBJ (&stdlib_eqvP_NAME), stdlib_eqvP, NULL }; static void stdlib_eqP (ScmObj * SCM_FP, int SCM_ARGCNT, void *data_) : void Scm_Init_stdlib () { ScmModule *module = Scm_VM ()->module; SCM_DEFINE (module, "eqv?", SCM_OBJ (&stdlib_eqvP_STUB)); SCM_DEFINE (module, "eq?", SCM_OBJ (&stdlib_eqP_STUB)); : SCM_DEFINE (module, "procedure-info", SCM_OBJ (&stdlib_procedure_info_STUB)); }
stdlib.stubの元ネタ。これに色々と肉付けしたんだな。
(define-cproc eqv? (obj1 obj2) " SCM_RETURN(Scm_EqvP(obj1, obj2));")
どうでもいいけど、機械的に作成されてる難読化されたやつは、整形すると6倍にも伸長してたよ。
debian:src$ wc stdlib.c* 3369 11471 100062 stdlib.c 520 8983 95009 stdlib.c~
run gosh
ソースを眺めていてもしょうが無いので、試走させてみます。
debian:src$ ./gosh Segmentation fault
見事にクラッシュしましたねぇ。
debian:src$ gdb -q gosh Reading symbols from gosh...done. (gdb) r Starting program: /tmp/small/src/gosh Program received signal SIGSEGV, Segmentation fault. 0x004283ee in GC_push_all_eager () (gdb) bt #0 0x004283ee in GC_push_all_eager () #1 0x0042841b in GC_push_all_stack_partially_eager () #2 0x00426f56 in GC_push_current_stack () #3 0x00426fc5 in GC_push_roots () #4 0x00428b8d in GC_mark_some () #5 0x00422f34 in GC_stopped_mark () #6 0x00423226 in GC_try_to_collect_inner () #7 0x00425f28 in GC_init_inner () #8 0x00421fa1 in GC_generic_malloc_inner () #9 0x004220b8 in GC_generic_malloc () #10 0x004221ab in GC_malloc () #11 0x00403c52 in Scm_Malloc (size=16) at core.c:26 #12 0x004097ca in Scm_MakeStringConst (str=0x42cf37 "scheme", size=6, len=6) at string.c:144 #13 0x004167c9 in Scm__InitModule () at module.c:152 #14 0x00403d18 in Scm_Init () at core.c:58 #15 0x00403c1c in main (argc=1, argv=0xbffff544) at main.c:64
ざっと見で、GC側に問題が有りそうな雰囲気だな。
64Bit機では失敗してたgcを外部の正式なやつに置き換えて試してみるか。欲しいのはgc.aと言う奴なんだから、何とかなるっしょ。
boehm-gc
A garbage collector for C and C++
ガベコレ込みのMallocライブラリィーである。このおかげでメモリー使い放題。使い終わったらfreeを使って解放しろって言うC語の教科書を捨て去る目的で開発されている。
主なユーザーにはw3mとかbiglooなんてのが居る。gaucheが列挙されていないのは宣伝不足かな。 作者のアンテナに引っかかってもいいと思うんだけどね。
(base) [sakae@c8 gc-8.0.4]$ ./configure --enable-static --prefix=/tmp/small/gc --disable-threads
こんな内容でconfigしたよ。設置先が /tmp/small/gc になってるのは、/tmp/smallにgaucheの初期版を置いているからだ。事前に提供されてるgc/を削除しておき、すっかり入れ替える作戦。threadsをoffにしてるのは、下記で使うリンクフェーズでthreads関係があれも足りない、これも足りないと言われたから。すっかりとthread無しのやつを作るんだ。
(base) [sakae@c8 src]$ cat Makefile CFLAGS = -g -I. -I../gc/include gosh : libgauche.a main.o $(CC) $(CFLAGS) -o gosh main.o -L. -lgauche ../gc/lib/libgc.a $(LDFLAGS)
そして、新しいgc用にMakefileを少々調整。これでgoshが無事に作成された。
gosh> (cdr '(a b c)) (b c) gosh> (print-code '(cdr '(a b c))) cdr quote a b c #<undef>
基本が動くか確認。それからsrc/p.scmと言う簡単アプリが有ったので、gosh画面に張り付けて評価。それから、クッダーらないコードを評価。どうやら動いている風。
gosh> (procedure-info print-code) (lambda (code) (letrec ((do-indent (lambda (count) (if (> count 0) (begin (display " ") (do-indent (- count 1)))))) (print-code-int (lambda (code indent) (for-each (lambda (insn) (if (pair? insn) (if (memq (car insn) (quote (let let* letrec lambda))) (begin (do-indent indent) (write-limited insn 60) (newline)) (print-code-int insn (+ indent 1))) (begin (do-indent indent) (write-limited insn 60) (newline)))) code)))) (print-code-int code 0)))
それから、組み込みでprocedure-infoってのが有ったので、使ってみた。最密充填のコードが 表示されたよ。まずは、動いてる。遺跡の復元が成功したって事ですかね。
(base) [sakae@c8 src]$ ls -l gosh -rwxrwxr-x 1 sakae sakae 1020864 Dec 29 07:42 gosh (base) [sakae@c8 src]$ file gosh gosh: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=ad06f4575b67b69332f8451a10e8f9c985afabfb, with debug_info, not stripped (base) [sakae@c8 src]$ ldd ./gosh linux-vdso.so.1 (0x00007ffc6e7b8000) libm.so.6 => /lib64/libm.so.6 (0x00007fd35e600000) libc.so.6 => /lib64/libc.so.6 (0x00007fd35e23c000) /lib64/ld-linux-x86-64.so.2 (0x00007fd35e982000)
図体が大きいのはlibgc.aのせいです。
気を良くしたオイラーは、OpenBSDでもやってみた。本来OpenBSDでは普通のgaucheの作成に失敗する。一時期有志がパッチを投稿してたけど、その後最新のパッチは投稿されていない。
今回は作成済みのstdlib.cを持ち込む事にして、goshの使用を回避したのさ。気になるgcの作成は問題無く成功した。しかもmakeで行けた。gmakeを使わないと動かないってのはGNUに毒されている証拠だからね。
ob$ ./gosh -g gosh> (cdr '(a b c)) == ((a b c) #<GREF> cdr #<CALL 1,-1> #<source-info (cdr (quote (a b c)))>) (b c) gosh> (print-code '(car '(a b c))) == ((car (quote (a b c))) #<GREF> print-code #<CALL 1,-1> #<source-info (print-code (quote (car (quote (a b c)))))>) car quote a b c #<undef>
gフラグを付けて、コンパイル結果を見ながらの実行。詳細を覗いてみようね。折角苦労して復元したんだから。
gosh> (define (aa n) (hogefuga n)) aa gosh> (+ 1 (aa 5)) *** ERROR: unbound variable: hogefuga Stack Trace: _______________________________________ 0 (lambda (n) (hogefuga n)) 1 (aa 5) 2 "[toplevel]"
エラーが発生するかの確認。昔からスタックトレースを実装してたんだね。
bigloo
FreeBSDのportsからも消えていたけど、ひょんな事から蘇ってきたな。記念に入れてみた。
(base) [sakae@c8 BIG]$ bin/bigloo ------------------------------------------------------------------------------ Bigloo (4.3f) ,--^, `a practical Scheme compiler' _ ___/ /|/ Thu 04 Jul 2019 08:10:18 AM CEST ,;'( )__, ) ' Inria -- Sophia Antipolis ;; // L__. email: bigloo@lists-sop.inria.fr ' \ / ' url: http://www-sop.inria.fr/indes/fp/Bigloo ^ ^ ------------------------------------------------------------------------------ 1:=>
新年なので、これぐらいにしておきます。今年も楽しくHackingだ。