エラーやシグナル(signal)に関する振る舞いを示すために、 あらかじめそれぞれ特別の変数*error-handler*と*signal-handler* に適当な関数を設定する。 修正あるいは続行できるエラーはない。 エラーを解析後、現在の実行をresetまたは上位レベルへの適当なthrow によって停止しなければならない。 Euslispの最上位レベルで0と名付けられたcatch frameを作成しているので、 resetは、(throw 0 NIL)と同等である。
エラーハンドラーは、code msg1 form &optional (msg2) という3つあるいは4つの引き数を持つ関数として定義しなければならない。 codeはエラーコードで、システムで定義されたエラーを示す。 例えば、14が'引き数が合わない'、13が'関数が定義されていない'となる。 これらの定義は、"c/eus.h"の中に定義されている。 msg1とmsg2は、ユーザーに示されるメッセージである。 formは、エラーによって生じたs表現である。
シグナルハンドラーは、sigとcodeの2つの引き数を受ける関数として 定義されなければならない。 sigは、1から30までのシグナル番号である。 codeは、シグナル番号の中に定義された補助番号である。
最上位レベルでの^
D(end-of-file)は、Euslispの活動を停止させる。
これは、Euslispをフィルターとしてプログラムされているとき
役に立つ。
eval-dynamicは、letやlambda変数として使用されるsymbolに結び付く 動的な変数を捜す関数である。 デバッグするときに役に立つ。
eval form &optional environment [関数]
evalhook hookfunc form &optional env [関数]
eval-when situation &rest forms [特殊]
declare &rest declarations [特殊]
warn format-string &rest args [関数]
error format-string &rest args [関数]
マルチスレッドEuslispにおいて、特殊変数はスレッド間で共有され、 同じ*error-handler*が異なったスレッドから参照される。 この不自由を避けるために、マルチスレッドEuslispはinstall-error-handler 関数を備えている。その関数は、それぞれのスレッドに対して 異なったエラーハンドラーをインストールする。
lisp::install-error-handler handler [関数]
EusLispの標準の最上位レベルの入力−評価−出力のループ(loop)は、eustop により制御されている。 euslispが呼び出されたとき、 eustopは、ホームディレクトリから".eusrc"というファイルを あるいはEUSRC環境変数で指定されたファイルをロードする。 それから、euslispは、引き数リストで指定されたファイルをロードする。 これらのロードが終了後、eustopは、普通の対話セッション(session)に入る。
*standard-input*にユーザーのTTYが接続されたとき、 eustopは、*prompt-string*(デフォルトとして"eus$"が 設定されている)に設定されたプロンプトを出力する。 そして、*terminal-io*ストリームから命令を入力する。 もし、その入力がカッコで括られた行ならば、 evalによってlisp書式として扱われる。 もし、入力行の最初のsymbolに関数定義があった場合、その行に自動的に カッコが入れられ、評価される。 もし、関数定義が見つからなかった場合、その特殊値(special value)が 調査され、その値が出力される。 もし、そのsymbolになにも定義されてないならば、 その行はUNIX命令とみなされ、sh(Bourn's shell)へ渡される。 もし、shが一致するUNIX命令を捜せなかったとき、 "command unrecognized"というメッセージを出力する。 このように、eustopはlispのインタープリタおよびUNIXのシェルとして動作する。 もし、入力をUNIX命令として実行したくないとき、 行の最初にコンマ','を付ければよい。 これは、対話実行(interpretive execution)でエラーが発生したとき、 動的な値を見るときに役に立つ。 Euslispはローカルなスコープ(lexical scope)を採用しているので ローカル変数の値をspecialとして宣言されていない限りスコープの外から 調査することは出来ない。
入力は、それぞれ行番号とともに*history*ベクトルに記憶される。 cshの上と同じ様に!関数を入力することにより入力の詳細を 呼び出すことができる。 cshの履歴との違いは、!が関数であるため!の次に最低1つの スペースが必要である。 また、コントロールキーを用いることによりemacsのように対話的に 行を編集することができる。
通常^
D (EOF)でEuslispは終了する。
上位レベル(普通はcsh)に異常終了コードを返すためには、適当な条件コードをつけた
exitを使用すること。
eustopは、SIGINTとSIGPIPEのためにシグナルハンドラーを設定する。 そして、他のシグナルはcatchしない。 このため、SIGTERMやSIGQUITのようなシグナルでEuslispを終了できる。 終了を避けたいとき、これらのシグナルをcatchするためには、 unix:signal関数でユーザーで定義したシグナルハンドラーを設定すること。
euserror code message &rest arg [関数]
exit &optional termination-code [関数]
Euslispコンパイラは、Lispプログラムの実行を高速化するために使用される。 実行時間の5〜30倍の高速化とマクロ展開によるガーベージコレクション 時間の大幅な減少が期待できる。
euscompは、計算処理とベクトル処理のための最適化を行う。 ときどきコンパイラが最適化を効率良く実行するために、固有の型宣言が必要となる。
compile-functionは、1つずつ関数をコンパイルする。 compile-fileは、すべてのソースファイルをコンパイルする。 compile-fileを実行している間、ファイル内のすべての書式が読み込まれ 評価される。 これは、現在のEuslispの環境を変化させる。 例えば、defparameterはsymbolに新しい値を設定するし、 defunはコンパイルされていない関数をコンパイルされた関数に 置き換える。 これらの予期しない影響を避けるためには、compile指定のないeval-whenを 使用したり、euscompを使用して別プロセスとしてコンパイラを実行したりする。
euscompはUNIXのコマンドで、普通eusにシンボリックリンクされている。 幾つかのオプションを持っている。 -OフラグはCコンパイラの最適化を指示し、 -O1,-O2, -O3 はそれぞれEuslispコンパイラの最適化のレベルを指示する。 これは、(optimize 1 or 2 or 3)と宣言するのと同等である。 -S0, -S1, -S2, -S3は、compiler:*safety*に0,1,2,3を設定する。 もし*safety*が2より小さければ、割り込みチェックのためのコードを発行しない。 もし、プログラムが無限ループに入ったとき、制御を失うことになる。 もし*safety*が0のときは、引き数の数をチェックしない。 -Vフラグは、コンパイルされている関数名を表示する。 -cフラグは、ccの実行やforkを防ぐ。 -Dは、*features*リストに続く引き数を置く。 これは、読み込みマクロ#-と#+ を用いた条件付きコンパイルのために使用することができる。
コンパイラは"xxx.l"という名のEuslispソースプログラムを 中間Cプログラムファイル"xxx.c" とヘッダーファイル"xxx.h"に変換する。 それから、Cコンパイラが実行され、"xxx.o"が生成される。 中間ファイル"xxx.c"と"xxx.h"はクロスコンパイルの目的のために残される。 したがって、違うアーキテクチャーの機械の上で使用したいとき、UNIX命令のccで "xxx.c"ファイルをコンパイルするだけでよい。 コンパイルされたコードは、'(load "xxx")'によってEuslispにロードされる。
中間ファイルはそれぞれ、"eus.h"ヘッダーファイルを参照する。 このファイルは、*eusdir*/cディレクトリに置かれていると仮定している。 *eusdir*は、EUSDIR環境変数からコピーされる。 もし設定されてなければ、/usr/local/eus/がデフォルトディレクトリ として扱われる。
コンパイルされたとき、中間のCプログラムは普通元のソースコードよりも かなり大きくなる。例えば、1,161行のlispソースコード"l/common.l"は、 8,194行の"l/common.c"と544行の"l/common.h"に展開される。 1,000行のlispソースをコンパイルするのは、難しい作業ではないが、 10,000行近いCのプログラムを最適コンパイルすることは、長い時間(数分)かかる とともに、たくさんのディスク空間を消費する。 そのため、もし相対的に大きなプログラムのコンパイルをするならば、 /var/tmpに十分なディスクがあるかどうかを確認すること。 そうでなければ、CCは死ぬだろう。 TMPDIR環境変数をもっと大きなディスク部分に設定することが助かる道である。
リンクがロード時または実行時に実行されるので、 eusのカーネルがバージョンアップされても再コンパイルする必要はない。 もう一方で、実行時リンクは不便なことがある。 2つの関数AとBが"x.l"ファイルにありAがBを呼び出していると仮定する。 "x.l"をコンパイル後、"x.o"をロードし内部でBを呼び出しているAを呼び出そうとする。 それから、Bの中でbugを見つけると、たぶんBを再定義しようとするだろう。 ここで、コンパイルされたAとコンパイルされていないBとができる。 再びAを呼び出したとすると、Aはまだ古いコンパイルされているBを呼び出す。 これは、Aが最初にBを呼び出したとき固定的にリンクされるからである。 この問題を避けるためには、Aを再定義しなおすかあるいは"x.o"がロードされた直後で Aを呼び出す前にBを再定義しなければならない。
コンパイルされたコードがロードされるとき、一般的にdefunやdefmethodの 列である最上位コードが実行される。 この最上位コードはロードモジュールのエントリ関数として定義されている。 コンパイラがそのエントリ関数の名前を付け、 ローダがこの関数の名前を正確にわからなければならない。 状況を簡単にするために、コンパイラとローダの両方とも そのエントリ関数の名前としてオブジェクトファイルのbasenameと同一のもの と仮定する。 例えば、もし"fib.l"をコンパイルしたならば、 コンパイラは"fib.c"のエントリ関数として"fib(...)"を生成する。 そして、ローダはオブジェクトファイル"fib.o"の中から"fib"を探す。 最終的にオブジェクトファイルはUnixの"cc"や"ld"で生成されるので、 このエントリ関数名は、C関数の命名ルールを満足しなければならない。 したがって、ファイル名としてCの予約キーワード(例えば、 "int", "struct", "union", "register", "extern"など)や"c/eus.h"に 定義されているプライベート指示語(例えば、"pointer", "cons", "makeint"など)を避けなければならない。 もし、ソースファイルの名前としてこれらの予約語の内の1つを 使用しなければならないならば、コンパイラやローダの :entry引数を別に指定すること。
closureの使用には制限がある。 closureの中のreturn-from特殊書式とunwind-protectの中のclean-up書式は いつも正しくコンパイルされるわけではない。
disassembleは、実現されていない。 コンパイルされたコードを解析するためには中間Cプログラムを見るかあるいは adbを使用する。
euscomp &rest filename [UNIXコマンド]
compile-file [関数]
srcfile &key (verbose nil)
(optimize 2) (c-optimize 1) (safety 1) ; optimization level
(pic t) ; position independent code
(cc t) ; run c compiler
(entry (pathname-name file))
compile-file-if-src-newer srcfile &key compiler-options [関数]
load [関数]
fname &key (verbose *load-verbose*)
(package *package*)
(entry (pathname-name fname))
(symbol-input nil)
(symbol-output "")
(print nil)
(ld-option "")
:entryオプションは、ロードモジュールを初期化する入力アドレスを 指定する。 たとえば、:entry "_myfunc"オプションは_myfuncから実行を始める ことを意味する。 デフォルト入力アドレスは、10.3節に記述されているように ロードされたファイル名のbasenameである。 ライブラリモジュール名は:ld-optionオプション文字列の中に指定 することができる。 たとえば、suncoreライブラリを使用するモジュールをリンクするために、 :ld-optionには "-lsuncore -lsunwindow -lpixrect -lm -lc" を与える必要がある。Solarisシステム以外では、 ライブラリがリンクされるときldは2度実行される。 1度はサイズを決定するため、2度目は固有のメモリーに実際にリンクするため。
:symbol-inputと:symbol-outputオプションはあるオブジェクト モジュールから他のモジュールへの参照を解決するため、あるいは ライブラリーの2重ロードを避けるために使用される。 A,B2つのオブジェクトモジュールがあり,BがAの中で定義されているsymbolを 参照しているとする。 :symbol-output ="a.out"を指定してモジュールAをロードする。 このリンクによって生成されたsymbol情報は、a.outに書き込まれる。 モジュールBをロードするためには、BからAへの参照を解決する :symbol-input = "a.out"を指定しなければならない。
Solaris2 OSにおいて、コンパイルコードのロードは、動的ロードライブラリの中の
dlopenを呼び出すことにより実行している。
dlopenの使用は、共有オブジェクトに制限されている。
そのオブジェクトは、位置に依存するようにコンパイルされるために、
"-K pic"オプションを指定する。
また、dlopenは同じファイルを2度オープンすることができないので、
既にロードされているファイルに関しては、dlcloseを最初に
実行する。
provide module-name &rest version-info [関数]
require module-name &rest load-arg [関数]
system:binload [関数]
opath qpath &optional (entry (pathname-name opath))
(symfile "/usr/local/bin/eus")
(symout "a.out")
(ldopt "")
describe obj &optional (stream *standard-output*) [関数]
describe-list list &optional (stream *standard-output*) [関数]
break &optional (prompt ":: ") [関数]
apropos strng &optional pack [関数]
apropos-list strng &optional pack [関数]
constants &optional (string "") (pkg *package*) [関数]
variables &optional (string "") (pkg *package*) [関数]
functions &optional (string "") (pkg *package*) [関数]
btrace &optional (depth 10) [関数]
timing count &rest forms [マクロ]
sys:list-all-instances aclass &optional scan-sub [関数]
sys:list-all-special-bindings [関数]
dump-object file &rest objects [関数]
dump-structure file &rest objects [関数]
dump-loadable-structure file &rest symbols [関数]
(setq a (make-cube 1 2 3)) ;; sample for dump-object (dump-object "a-cube.l" a) (with-open-file (f "a-cube.l" :direction :input) (setq a (read f))) (print a) ;; sample for dump-structure (dump-structure "a-cube.l" a) (with-open-file (f "a-cube.l" :direction :input) (setq a (read f))) (print a) ;; sample for dump-loadable-structure (dump-loadable-structure "a-cube.l" a) (load "a-cube.l") (print a)
sys:save path &optional (symbol-file "") starter [関数]
この最上位関数は、任意の数の引き数を受け取れるようにプログラムすべきである。 その命令行の上の引き数はそれぞれ、強制的に文字列にされ、最上位関数に渡される。 以下に示すプログラムは、最初の引き数に与えられたファイルからくり返し読み込み、 2番目の引き数のファイルに整形表示する。
(defun pprint-copy (infile outfile) (with-open-file (in infile) (with-open-file (out outfile :direction :output) (let ((eof (cons nil nil)) (exp)) (while (not (eq (setq exp (read in nil eof)) eof)) (pprint exp out)))))) (defun pprint-copy-top (&rest argv) (when (= (length argv) 2) (pprint-copy (first argv) (second argv))))
一度これらの関数をEuslispの中に定義すれば、 (save "ppcopy" "" 'pprint-copy-top)でppcopyという名のUNIXで実行 可能な命令を作る。
Solaris上のEuslispにおいて、saveがないので、 最上位評価関数はこの手法では変更できない。 代わりに、独自の最上位評価関数を定義するためにlib/eusrt.lを 修正し、*toplevel*に設定することができる。 lib/eusrt.lには、Euslispの起動時に評価される初期化手続きを 定義している。
lisp-implementation-version [関数]
This document was generated using the LaTeX2HTML translator on Sat Feb 5 14:36:44 JST 2022 from EusLisp version 138fb6ee Merge pull request #482 from k-okada/apply_dfsg_patch