TinySCHEME Version 1.38 "マニュアル通りに使われる限りは安全だ" -- Philip K. Dick, "Ubik" これはBSDライセンスで保護されたオープンソースソフトウェアです。 添付されているCOPYINGファイルをご確認ください。 ------------------------------------------------------------------------------- このSchemeインタプリタは MiniSCHEME バージョン 0.85k4 を基にしています。 (Scheme リポジトリ内の miniscm.tar.gzを見てみて下さい) MiniSCHEMETribute.txt ファイル内に元々のクレジットがあります。 D. Souflis (dsouflis@acm.org) ------------------------------------------------------------------------------- TinySchemeとは? ------------------- TinyScheme は軽量な Scheme インタプリタです。巨大で複雑になりすぎないように しながら、R5RS のできるだけ大きなサブセットとして実装しています。軽量である ことは他のプログラムへの組込み用インタプリタとして使えることを意味しています。 そのため、IDE や重量級のツールキットは提供されませんが、小さなトップレベル ループは条件的に含まれます。TinySCHEME の持つたくさんの機能は、条件的に含める ことができます。これは開発者が機能の多さとフットプリントのバランスを自由に 取れるようにするためです。 組込み用インタプリタとして、同じプログラムの中で複数のインタプリタの状態を 共存させることができ、それらはお互い独立して動作します。プログラム的には、 C言語で書かれた外部関数を追加することができますし、 Scheme 環境内に値を定義 することもできます。かなり小さいプログラムなので、容易に理解し、学習し、使用 することができます。 既知のバグ ---------- TinySchemeはメモリの空き容量が無い時、不正な動作を行うことがわかっています。 間違いがほったらかしにされ改修を必要としている場合 ---------------------------------------------------- 健全なマクロは存在しません。有理数と複素数もありません。unwind-protect関数と call-with-values関数もありません。 おそらく、SLIB(またはその一部)はTinySCHEME上で動作すると思います...。 優秀なデバッキング機能はありません。tracing のみがネイティブにサポートされて います。 Schemeリファレンス ------------------ もし何か欠けているのではないかと思ったら、作成したコードや利用している ライブラリ関数の中にある"init.scm"を参照してください。MiniSCHEMEのreadmeを 参照するのは最後の手段です。 環境 (interaction-environment) R5RSを確認してください。 TinySCHEMEでは、不変な連想リストのリストです。 (current-environment) 実質的な関数が呼び出されたときの環境となります。これを使い、また有用な例と なるコードが"init.scm"内のパッケージに実装されているのを見ることができます。 以下にそれを示します。 (macro (package form) `(apply (lambda () ,@(cdr form) (current-environment)))) クロージャ内部の(local)定義を含む環境は不変な値として処理されます。 (defined? ) または (defined? ) 与えられたシンボルが現在の(もしくは与えられた)環境で定義されているのかどうか チェックします。 シンボル (gensym) 毎回 intern されたシンボルを返します。これはstring->symbolが実装されたら ライブラリに移動されるでしょう。 指示詞 (gc) ガーベッジコレクションを即座に行います。 (gcverbose) (gcverbose ) 引数によって(デフォルトでは#t)、GCが視覚的な出力を行うかどうかの制御を 行います。 (quit) (quit ) インタプリタを停止し、"リターンコード"を内部変数へ(デフォルトは0)セット します。スタンドアローンで動作している場合、"リターンコード"はOSへの終了 コード(exit code)として返されます。 (tracing ) 1をセットするとトレース出力を返すようになり、0をセットすると返さなくなり ます。(この関数はUSE_TRACINGが1の時のみ動作します。) 数学的関数 有理数と複素数が存在しないので、それぞれの関数もありません。 サポートしている数学的関数郡: exp, log, sin, cos, tan, asin, acos, atan, floor, ceiling, trunc, round USE_MATH=1時に使える関数群: sqrt, expt 整数論的な商、余り、モジュロ(法)、最大公約数、最小公倍数。 ライブラリ関数: exact?, inexact?, odd?, even?, zero?, positive?, negative?, exact->inexact。 inexact->exactは中心となる関数です。 Number-theoretical quotient(整数論的な商), remainder(余り)、 modulo(モジュロ(法))、gcd(最大公約数)、lcm(最小公倍数) はサポートされて います。 型述語 - 引数の変数の型が何かをチェックする関数郡 boolean?,eof-object?,symbol?,number?,string?,integer?,real?,list?,null?, char?,port?,input-port?,output-port?,procedure?,pair?,environment?',vector? といった関数があります。 closure?, macro? もあります。 型 以下の型をサポートしています。 Numbers (整数と実数) Symbols Pairs Strings Characters Ports Eof object Environments Vectors リテラル 文字列リテラルはエスケープされた引用符(\")を通常含むことができます。それ だけでなく、\n, \r, \t, \xDD (16進数表現)や \DDD (8進数表現) も含めることが できます。注記しておきたいことがあります。文字列リテラル内の改行リテラルは、 以下のように文字列に組み込まれます。 (define s "String with newline here and here that can function like a HERE-string") 文字リテラルは #\space や #\newlineを含み、#\returnや#\tabといった分かり易い 意味を持つものも補完されています。16進数による表現が用いられています(例えば、 #\20 は #\space です)。USE_ASCII_NAMES が定義されているとき、各種制御文字が それらのASCIIコード名によって参照できます。 0 #\nul 17 #\dc1 1 #\soh 18 #\dc2 2 #\stx 19 #\dc3 3 #\etx 20 #\dc4 4 #\eot 21 #\nak 5 #\enq 22 #\syn 6 #\ack 23 #\etv 7 #\bel 24 #\can 8 #\bs 25 #\em 9 #\ht 26 #\sub 10 #\lf 27 #\esc 11 #\vt 28 #\fs 12 #\ff 29 #\gs 13 #\cr 30 #\rs 14 #\so 31 #\us 15 #\si 16 #\dle 127 #\del 数値リテラルは #x, #o, #b と #d を使うことができます。浮動小数点は現在、 10進数表記での読み込みのみできます。全ての文法はもうじきサポートされます。 クォート・準クォート 普通に使えます。 固定値 不変な値(固定値)のペアは set-car!関数や、set-cdr!関数で変更することは できません。不変な文字列(固定文字列)は string-set!関数で変更することは できません。 I/O R5RSによるものと、以下に記述するString Portsを追加したものになります。 current-input-port, current-output-port, close-input-port, close-output-port, input-port?, output-port?, open-input-file, open-output-file. read, write, display, newline, write-char, read-char, peek-char. char-ready? 関数は string ports 利用時のみ #t を返します。これは、標準入出力に 対してキャラクタが利用可能かどうか決定する移植可能な方法がないためです。 open-input-output-file, set-input-port, set-output-port (R5RSにはありません) Library: call-with-input-file, call-with-output-file, with-input-from-file, with-output-from-file and with-input-output-from-to-files, close-port, input-output-port? (R5RSにはありません). String Ports: open-input-string, open-output-string, open-input-output-string. 文字列はI/Oルーチンで利用可能です。 ベクター make-vector, vector, vector-length, vector-ref, vector-set!, list->vector, vector-fill!, vector->list。 vector-equal? (これは補助関数で、R5RSにはありません) 文字列 string, make-string, list->string, string-length, string-ref, string-set!, substring, string->list, string-fill!, string-append, string-copy。 string=?, string?, string>?, string<=?, string>=?。 (string-ci*? はまだありません). string->number, number->string. atom->string, string->atom (R5RSにはありません)。 シンボル symbol->string, string->symbol 文字 integer->char, char->integer.char=?, char?, char<=?, char>=?. (char-ci*? はありません) ペア & リスト cons, car, cdr, list, length, map, for-each, foldr, list-tail,list-ref, last-pair, reverse, append。 generic-memberを基にした member, memq, memv。 generic-assocを基にした assoc, assq, assv。 ストリーム head, tail, cons-stream 制御文字 procedure?、macro?、closure?はさておき、 map, for-each, force, delay, call-with-current-continuation (or call/cc), eval, apply があります。promiseではない値を'force'するとその値を返します。 call-with-values, values, dynamic-wind は存在しません。継続がある場合の dynamic-wind は、抽象マシン自身によるサポートを必要とします。 (若干意味がわからない) プロパティリスト TinySchemeはMiniSchemeのシンボルを使うプロパティリストを継承しています。 put, get 関数が存在します。 動的拡張ロード (load-extension <拡張子無しのファイル名>) 外部プロシージャを定義しているDLLをロードします。 上級者向けプロシージャ (oblist) oblistを返します。これは、全てのシンボルの不変なリストです。 (macro-expand <フォーム>) 引数に渡されたマクロ呼出しの展開された形式を返します。 (define-with-return ( ...) ) 通常の"define"と似ています、が プロシージャ内で"return"として利用できる 継続を生成します。命令型プログラムに便利です。 (new-segment <数値>) より多くのメモリ領域を割り当てます。 defined? 「環境」の項を参照してください。 (get-closure-code <クロージャ>) schemeのデータとしてプログラムコードを取得します。 (make-closure <コード> <環境>) 与えられた環境に新しいクロージャを生成します。 古い形式の関数 (print-width <オブジェクト>) 開発者用リファレンス -------------------- インタプリタの状態は、"scheme_init"関数で初期化されます。手を入れたメモリ 割り当てルーチンをデフォルトの代替として初期化関数にしたいときは、 "scheme_init_custom_alooc"関数を使います。"scheme_load_file"関数でファイルの ロードを行います。schemeのプログラムコードを含む文字列を読み込むときは、 "scheme_load_string"を使います。"scheme_load"関数でinit.scmをまず最初に 読み込むのは、良い考えです。 外部の状態を保持するための外部データ(外部関数を使うための)は "scheme_set_external_data"関数を使うことで利用できるようになります。 外部関数は、"assign_foreign"関数によって使えるようになります。後から追加 したい定義は、"scheme_define"関数を使って、現在のインタプリタへ追加することが できます(これは、Altera SQL サーバ上でSchemeスクリプトにHTTPヘッダやHTML フォームの情報を渡す方法として使われています)。もし、特定の環境内で外部関数の 定義をしたいならば(モジュールの独立性を高めるために)、"assign_foreign_env"関数 を利用します。 "scheme_apply0"関数は、永続的なスクリプトと共に用いられることを考慮して追加され ました。永続的なスクリプトは1度だけロードされ、HTTP出力を生成する必要がある ときに毎回必要とされます。適切なデータはグローバル定義を通して渡され、"main" 関数が仕事をするために呼ばれます。"scheme_apply1"などを追加することは簡単でしょう。 インタプリタの状態は"scheme_deinit"関数によって初期化されなければいけない。 DLLに含まれている外部関数は「init_ベース名」と名付けられた関数を定義しないと いけません。例えば、foo.dllは"init_foo"という関数を定義しないといけません、 そしてbar.soは"init_bar"という名前の関数を定義しないといけません。この関数は DLL内に含まれた外部関数をassign_foreignしないといけません。 最初に動的に読み込まれ、TinyScheme上で利用可能になる拡張は正規表現の ライブラリです。このライブラリは決して標準というわけではありませんが、 (FIXME: under the TinyScheme location が謎。TinySchemeが入ってるパスに ライブラリをコピーするの? - TinySchemeの資料を探して確認) 外部関数 -------- ユーザはC言語により外部関数を追加することができます。例えば、以下の例は引数の 二乗を返す関数です。 pointer square(scheme *sc, pointer args) { if(args!=sc->NIL) { if(sc->isnumber(sc->pair_car(args))) { double v=sc->rvalue(sc->pair_car(args)); return sc->mk_real(sc,v*v); } } return sc->NIL; } 外部関数はこのようにクロージャとして定義されています: sc->interface->scheme_define( sc, sc->global_env, sc->interface->mk_symbol(sc,"square"), sc->interface->mk_foreign_func(sc, square) ); 外部関数は"scheme"構造体内の外部データを利用することができます。この構造体は あらゆる外部状態を格納できます(FIXME: implementの訳画怪しい)。外部データは 以下の関数で設定することができます: void scheme_set_external_data(scheme *sc, void *p); v.1.17において、DLL内の外部関数がSchemeのデータを操作する標準的な方法は "sc->interface"で関数ポインタを使う方法になっています。 スタンドアローン ---------------- ( MEMO: tiny-fuではほとんど使わないので後回し ) エラーハンドリング ---------- エラーは損害無しに回復させられます。ユーザは、 *error-hook* で定義することで、 自分で作ったシステムのエラーハンドラを入れることができます。'()を定義する ことでデフォルトの挙動を与えることができます、これは"error"と等価です (FIXME: 訳が怪しい)。USE_ERROR_HOOKを必ず定義してください。 単純な例外処理機構は"init.scm"内で見ることができます。新しい統合形式は以下の ように記述できます(FIXME: introduce ): (catch <例外を返す表現(FIXME: 実装を確認)> ... ) "Catch"は次の"catch"が登場するまで、複数のcall-frames(FIXME: 用語確認)に渡って スコープの範囲内とします。例外は以下のようにスローされます: (throw "message") もし、(catch ...)の外で使うと、 (error "message") へと回顧します。 以下に例を記述します: (define (foo x) (write x) (newline) (/ x 0)) (catch (begin (display "Error!\n") 0) (write "Before foo ... ") (foo 5) (write "After foo")) このコードの処理結果は以下の通りになる: >> "Before foo ... "5 >> Error! >> 0 例外機構は以下のように記述することで、システムのエラーに対しても 使うことができる。: (define *error-hook* throw) エラーをフックする方法は上記のとおりです(init.scmに記述されています)。 もし必要であれば、自分専用の例外機構を工夫して作ることができます、 例えばタグ付け例外とか。 リーダ拡張 ---------- '#'記号の後ろで未知の文字が現れた場合、ユーザが作成した*sharp-hock*プロ シージャがもしあれば、リーダー拡張の為に呼び出されます。これは、ユーザ 定義の定数やその他なんでも扱えるようにリーダーを拡張するものです。この 手続きは引数をとらず、カレントの入力ポート(それは load-port になるでしょ う)から読み込まなければなりません。 コロン限定子( Colon Qualifiers ) - パッケージ --------------------------------------------- USE_COLON_HOOK=1: 上記のようにした時、構文解析器はその時から、:: という 構造体を認識するようになり、この構造体を以下に示す文法で変形させます。 ( T は変換関数です): T(::) = (*colon-hook* 'T() ) には " は含まれません。 再帰的に定義がなされた場合、限定子はネストされます。 ユーザは、 *colon-hook* 関数を使って確定された名前達を制御することが できます。デフォルトでは、"init.scm"内で *colon-hook* はEVALとして定義 されています。従って、限定子は何らかの Scheme 環境を表さなければなりません。 例えば (interaction-environment) の戻り値などです。"Init.scm"では、 簡単な例としてPACKAGEという新しい構文形態を定義しています。これは以下の ようなものです: (define toto (package (define foo 1) (define bar +))) foo ==> Error, "foo" undefined (eval 'foo) ==> Error, "foo" undefined (eval 'foo toto) ==> 1 toto::foo ==> 1 ((eval 'bar toto) 2 (eval 'foo toto)) ==> 3 (toto::bar 2 toto::foo) ==> 3 (eval (bar 2 foo) toto) ==> 3 もしユーザがこれとは異なる packege機構を導入した場合、ユーザは新しく 'package'プロシージャもしくはマクロを定義しなければならない。なぜなら、 提供されているコードの互換性を保持する為である。 注記: 古いバージョンでは ":" を限定子として使っている。不幸なことに、 現存するコード(例えば SLIB)内で ":" は擬似確定詞として使われていることが 根本的に真の確定詞を使うことを不可能にしている。