CodeReading: EmacsLisp "DEFUN"マクロ
Kazuki Ohta, 2005/01/27
前回は"eval"の内容を見ていった。次は"if"や"and"といった重要なsyntaxがどう定義されているかを見たいと思う。
ところで、こういった関数はどうやってインタプリタに登録されているのだろうか。まずはその仕組みから解き明かしてみる。その謎は"DEFUN"マクロにある。DEFUNはsrc/lisp.hで次のように定義されている。
#define DEFUN(lname, fnname, sname, minargs, maxargs, prompt, doc) \
Lisp_Object fnname DEFUN_ARGS_ ## maxargs ; \
DECL_ALIGN (struct Lisp_Subr, sname) = \
{ PVEC_SUBR | (sizeof (struct Lisp_Subr) / sizeof (EMACS_INT)), \
fnname, minargs, maxargs, lname, prompt, 0}; \
Lisp_Object fnname
/* First, try and define DECL_ALIGN(type,var) which declares a static
variable VAR of type TYPE with the added requirement that it be
TYPEBITS-aligned. */
#ifndef NO_DECL_ALIGN
# ifndef DECL_ALIGN
/* What compiler directive should we use for non-gcc compilers? -stef */
# if defined (__GNUC__)
# define DECL_ALIGN(type, var) \
type __attribute__ ((__aligned__ (1 << GCTYPEBITS))) var
# endif
# endif
#endif
ここで、実際にDEFUNを使用している部分を見てみる。
DEFUN ("eval", Feval, Seval, 1, 1, 0,
doc: /* Evaluate FORM and return its value. */)
(form)
Lisp_Object form;
{
hogehoge;
}
Lisp_Object Feval DEFUN_ARGS_1;
struct Lisp_Subr Seval =
{
PVEC_SUBR | (sizeof (struct Lisp_Subr) / sizeof (EMACS_INT)),
Feval,
1,
1,
"eval",
0,
0
};
Lisp_Object Feval(form)
Lisp_Object form;
{
hogehoge;
}
Lisp_Object Feval (Lisp_Object);
struct Lisp_Subr Seval =
{
PVEC_SUBR | (sizeof (struct Lisp_Subr) / sizeof (EMACS_INT)),
Feval,
1,
1,
"eval",
0,
0
};
Lisp_Object Feval(form)
Lisp_Object form;
{
hogehoge;
}
void
syms_of_eval ()
{
hogehoge();
defsubr (&Seval);
hogehoge();
}
さて、ここまで見ておけば後は何でも一緒だ。まずは"if"文を見てみる。if文を定義しているのは、同じくsrc/eval.cの次の部分だ。
DEFUN ("if", Fif, Sif, 2, UNEVALLED, 0,
doc: /* If COND yields non-nil, do THEN, else do ELSE...
Returns the value of THEN or the value of the last of the ELSE's.
THEN must be one expression, but ELSE... can be zero or more expressions.
If COND yields nil, and there are no ELSE's, the value is nil.
usage: (if COND THEN ELSE...) */)
(args)
Lisp_Object args;
{
register Lisp_Object cond;
struct gcpro gcpro1;
GCPRO1 (args);
cond = Feval (Fcar (args));
UNGCPRO;
if (!NILP (cond))
return Feval (Fcar (Fcdr (args)));
return Fprogn (Fcdr (Fcdr (args)));
}
さて中身をみると、まずは第一引数をFevalし、その結果が!NILPだったら、第二引数(THENの部分)を評価しそれをreturn。NILPで有れば第三引数移行を順番に評価して行く。prognはSchemeで言う所のbegin。確かめてみましょうか。
DEFUN ("progn", Fprogn, Sprogn, 0, UNEVALLED, 0,
doc: /* Eval BODY forms sequentially and return value of last one.
usage: (progn BODY ...) */)
(args)
Lisp_Object args;
{
register Lisp_Object val = Qnil;
struct gcpro gcpro1;
GCPRO1 (args);
while (CONSP (args))
{
val = Feval (XCAR (args));
args = XCDR (args);
}
UNGCPRO;
return val;
}
DEFUN ("and", Fand, Sand, 0, UNEVALLED, 0,
doc: /* Eval args until one of them yields nil, then return nil.
The remaining args are not evalled at all.
If no arg yields nil, return the last arg's value.
usage: (and CONDITIONS ...) */)
(args)
Lisp_Object args;
{
register Lisp_Object val = Qt;
struct gcpro gcpro1;
GCPRO1 (args);
while (CONSP (args))
{
val = Feval (XCAR (args));
if (NILP (val))
break;
args = XCDR (args);
}
UNGCPRO;
return val;
}
後は個々の関数の問題なんで、Emacsソースコードをある程度渡り歩けそう。
[ return ]