Next: Object Internals, Previous: C Dialect, Up: GNU Emacs Internals [Contents][Index]
LispプリミティブとはCで実装されたLisp関数です。Lispから呼び出せるようにC関数インターフェースの詳細はCのマクロで処理されます。新たなCコードの記述のしかたを真に理解するにはソースを読むのが唯一の方法ですが、ここではいくつかの事項について説明します。
スペシャルフォームの例として以下はeval.cのor
です(通常の関数は同様の一般的な外観をもつ)。
DEFUN ("or", For, Sor, 0, UNEVALLED, 0, doc: /* Eval args until one of them yields non-nil, then return that value. The remaining args are not evalled at all. If all args return nil, return nil.
usage: (or CONDITIONS...) */) (Lisp_Object args) { Lisp_Object val = Qnil;
while (CONSP (args)) { val = eval_sub (XCAR (args)); if (!NILP (val)) break; args = XCDR (args); QUIT; }
return val; }
ではDEFUN
マクロの引数について詳細に説明しましょう。以下はそれらのテンプレートです:
DEFUN (lname, fname, sname, min, max, interactive, doc)
これは関数名として定義するLispシンボル名。上記例ではor
。
これは関数のC関数名。これはCコードでその関数を呼び出すために使用される名前。名前は慣習として‘F’の後にLisp名をつけて、Lisp名のすべてのダッシュ(‘-’)をアンダースコアに変更する。つまりCコードから呼び出す場合にはFor
を呼び出す。
これはLispでその関数を表すsubrオブジェクト用にデータ保持のための構造体に使用されるC変数名。この構造体はそのシンボルを作成してそれの定義にsubrオブジェクトを格納する初期化ルーチンでLispシンボル名を伝達する。慣習により常にfnameの‘F’を‘S’に置き換えた名前になる。
これは関数が要求する引数の最小個数。関数or
は最小で0個の引数を受け入れる。
これは関数が受け入れる引数の最大個数が定数なら引数の最大個数。あるいはUNEVALLED
なら未評価の引数を受け取るスペシャルフォーム、MANY
なら評価される引数の個数に制限がないことを意味する(&rest
と等価)。UNEVALLED
とMANY
はいずれもマクロ。maxが数字ならminより大きく8より小さいこと。
これはLisp関数でinteractive
の引数として使用されるようなインタラクティブ仕様(文字列)。or
の場合は0(nullポインター)であり、それはor
がインタラクティブに呼び出せないことを示す。値""
はインタラクティブに呼び出す際に関数が引数を引き受けるべきではないことを示す。値が‘"(’で始まる場合には、その文字列はLispフォームとして評価される。たとえば:
DEFUN ("foo", Ffoo, Sfoo, 0, UNEVALLED, "(list (read-char-by-name \"Insert character: \")\ (prefix-numeric-value current-prefix-arg)\ t))", doc: /* … /*)
これはドキュメント文字列。複数行を含むために特別なことを要しないので、これにはCの文字列構文ではなくCコメント構文を使用する。‘doc:’の後のコメントはドキュメント文字列として認識される。コメントの開始と終了の区切り文字‘/*’と‘*/’はドキュメント文字列の一部にはならない。
ドキュメント文字列の最後の行がキーワード‘usage:’で始まる場合には、その行の残りの部分は引数リストをドキュメント化するためのものとして扱われる。この方法によりCコード内で使用される引数名とは異なる引数名をドキュメント文字列内で使用することができる。その関数の引数の個数に制限がなければ‘usage:’は必須。
Lispコードでのドキュメント文字列にたいするすべての通常ルール(Documentation Tipsを参照)はCコードのドキュメント文字列にも適用される。
DEFUN
マクロ呼び出しの後には、そのC関数にたいする引数リストを引数のタイプを含めて記述しなければなりません。そのプリミティブがLispで固定された最大個数をもつ引数を受け入れるならLisp引数それぞれにたいして1つのC引数をもち、各引数のタイプはLisp_Object
でなければなりません(ファイルlisp.hではタイプLisp_Object
の値を作成する種々のマクロと関数が宣言されている)。そのプリミティブのLispの最大引数個数に上限がなければ正確に2つのC引数をもたなければなりません。1つ目はLisp引数の個数で、2つ目はそれらの値を含むブロックのアドレスです。これらはそれぞれint
、Lisp_Object *
のタイプをもちます。Lisp_Object
は任意のデータ型と任意のLispオブジェクトを保持できるので実行時のみ実際のデータ型を判断できます。特定のタイプの引数だけを受け入れるプリミティブを記述したければ適切な述語を使用してタイプを明確にチェックしなければなりません(Type Predicatesを参照)。
関数For
自体ではローカル変数args
はEmacsのスタックマーキングによるガーベージコレクションで制御されるオブジェクトを参照します。たとえガーベージコレクターがCのLisp_Object
スタック変数から到達可能なオブジェクトは再利用しないとしても、文字列の内容のようなオブジェクトの非オブジェクトコンポーネントは移動するかもしれないので、非オブジェクトコンポーネントにアクセスする関数はLisp評価を処理した後にはそれらのアドレスを再取得するよう注意しなければなりません。Lisp評価は直接と間接を問わずeval_sub
かFeval
の呼び出しを通じて発生する可能性があります。
ループ内部でのQUIT
の呼び出しには注意してください。このマクロはユーザーがC-gを押下したかどうかをチェックして、もし押下していたら処理をabortします。潜在的に多数の繰り返しを要するすべてのループでこれを行う必要があります。この場合には引数のリストは非常に長くなる可能性があります。これはEmacsの応答性とユーザーエクスペリエンスを向上します。
Emacsが一度ダンプされた後に変数に何か書き込まれているときには、その静的変数やグローバル変数にCの初期化を使用してはなりません。初期化されたこれらの変数はEmacsのダンプの結果として、(特定のオペレーティングシステムでは)読み取り専用となるメモリーエリアに割り当てられます。Pure Storageを参照してください。
C関数の定義だけではLispプリミティブを利用可能にするのに十分ではありません。そのプリミティブにたいしてLispシンボルを作成して関数セルに適切なsubrオブジェクトを格納しなければなりません。このコードは以下のようになるでしょう:
defsubr (&sname);
ここでsnameはDEFUN
の3つ目の引数として使用する名前です。
すでにLispプリミティブが定義されたファイルにプリミティブを追加する場合には、(そのファイル終端付近にある)syms_of_something
という名前の関数を探してdefsubr
の呼び出しを追加してください。ファイルにこの関数がない、または新たなファイルを作成する場合にはsyms_of_filename
(例:
syms_of_myfile
)を追加します。それからemacs.cでそれらの関数が呼び出されるすべての箇所を探してsyms_of_filename
の呼び出しを追加してください。
関数syms_of_filename
はLisp変数として可視となるすべてのC変数を定義する場所でもあります。DEFVAR_LISP
はタイプLisp_Object
のC変数をLispから可視にします。DEFVAR_INT
はタイプint
のC変数を常に整数となる値をもつようにしてLispから可視にします。DEFVAR_BOOL
はタイプint
のC変数を常にt
かnil
のいずれかとなる値をもつようにしてLispから可視にします。DEFVAR_BOOL
で定義された変数はバイトコンパイラーに使用されるリストbyte-boolean-vars
に自動的に追加されることに注意してください。
Cで定義されたLisp変数をdefcustom
で宣言された変数のように振る舞わせたければcus-start.elに適切なエントリーを追加してください。
タイプLisp_Object
のファイルをスコープとするC変数を定義する場合には、以下のようにsyms_of_filename
内でstaticpro
を呼び出してガーベージコレクションから保護しなければなりません:
staticpro (&variable);
以下はより複雑な引数をもつ別の関数例です。これはwindow.cからのコードであり、Lispオブジェクトを操作するためのマクロと関数の使用を示すものです。
DEFUN ("coordinates-in-window-p", Fcoordinates_in_window_p, Scoordinates_in_window_p, 2, 2, 0, doc: /* Return non-nil if COORDINATES are in WINDOW. ...
or `right-margin' is returned. */) (register Lisp_Object coordinates, Lisp_Object window) { struct window *w; struct frame *f; int x, y; Lisp_Object lx, ly;
CHECK_LIVE_WINDOW (window); w = XWINDOW (window); f = XFRAME (w->frame); CHECK_CONS (coordinates); lx = Fcar (coordinates); ly = Fcdr (coordinates); CHECK_NUMBER_OR_FLOAT (lx); CHECK_NUMBER_OR_FLOAT (ly); x = FRAME_PIXEL_X_FROM_CANON_X (f, lx) + FRAME_INTERNAL_BORDER_WIDTH(f); y = FRAME_PIXEL_Y_FROM_CANON_Y (f, ly) + FRAME_INTERNAL_BORDER_WIDTH(f);
switch (coordinates_in_window (w, x, y)) { case ON_NOTHING: /* NOT in window at all. */ return Qnil;
...
case ON_MODE_LINE: /* In mode line of window. */ return Qmode_line;
...
case ON_SCROLL_BAR: /* On scroll-bar of window. */ /* Historically we are supposed to return nil in this case. */ return Qnil;
default: abort (); } }
CコードはCで記述されていなければ名前で呼び出すことはできないことに注意してください。Lispで記述された関数を呼び出すには関数funcall
をCで具現化したFfuncall
を使用します。Lisp関数funcall
は個数制限なしの引数を受け付けるので、Cでの引数はLispレベルでの引数個数とそれらの値を含む1次元配列という2個の引数になります。Lispレベルでの1つ目の引数は呼び出す関数、残りはそれに渡す引数です。
C関数call0
、call1
、call2
、...は個数が固定された引数でLisp関数を手軽に呼び出す便利な方法を提供します。これらはFfuncall
を呼び出すことにより機能します。
eval.cは例を探すのに適したファイルです。lisp.hには重要なマクロと関数の定義がいくつか含まれています。
副作用をもたない関数を定義する場合には、コンパイラーのオプティマイザーに知らせるためにside-effect-free-fns
とside-effect-and-error-free-fns
をバインドするbyte-opt.el内のコードを更新してください。
Next: Object Internals, Previous: C Dialect, Up: GNU Emacs Internals [Contents][Index]