CodeReading: EmacsLisp "interactive"関数
Kazuki Ohta, 2006/02/12
実際に.elファイルを読んでみようと思う。インタプリタの構造を大体理解しているので、分からない部分が有ればすぐにCの下層へと降りて調べれば良い。で、lisp/以下の何かのファイルを読もうと思うのだが、なんとなく気になるのが各関数の冒頭に登場するinteractiveという関数。これは何だろうと気になったので調べてみる事にする。interactiveは例えば次のようにして使われる(lisp/tabify.el)。untabify関数はmarkで指定された範囲のtabをspaceに置き換える関数なのであるのに対し、tabifyは指定された範囲のspaceをtabに置き換える関数だ。
(defun untabify (start end)
"Convert all tabs in region to multiple spaces, preserving columns.
Called non-interactively, the region is specified by arguments
START and END, rather than by the position of point and mark.
The variable `tab-width' controls the spacing of tab stops."
(interactive "r")
(save-excursion
(save-restriction
(narrow-to-region (point-min) end)
(goto-char start)
(while (search-forward "\t" nil t) ; faster than re-search
(forward-char -1)
(let ((tab-beg (point))
(indent-tabs-mode nil)
column)
(skip-chars-forward "\t")
(setq column (current-column))
(delete-region tab-beg (point))
(indent-to column))))))
DEFUN ("interactive", Finteractive, Sinteractive, 0, UNEVALLED, 0,
doc: /* Specify a way of parsing arguments for interactive use of a function.
For example, write
(defun foo (arg) "Doc string" (interactive "p") ...use arg...)
to make ARG be the prefix argument when `foo' is called as a command.
The "call" to `interactive' is actually a declaration rather than a function;
it tells `call-interactively' how to read arguments
to pass to the function.
When actually called, `interactive' just returns nil.
The argument of `interactive' is usually a string containing a code letter
followed by a prompt. (Some code letters do not use I/O to get
the argument and do not need prompts.) To prompt for multiple arguments,
give a code letter, its prompt, a newline, and another code letter, etc.
Prompts are passed to format, and may use % escapes to print the
arguments that have already been read.
If the argument is not a string, it is evaluated to get a list of
arguments to pass to the function.
Just `(interactive)' means pass no args when calling interactively.
Code letters available are:
a -- Function name: symbol with a function definition.
b -- Name of existing buffer.
B -- Name of buffer, possibly nonexistent.
c -- Character (no input method is used).
C -- Command name: symbol with interactive function definition.
d -- Value of point as number. Does not do I/O.
D -- Directory name.
e -- Parametrized event (i.e., one that's a list) that invoked this command.
If used more than once, the Nth `e' returns the Nth parameterized event.
This skips events that are integers or symbols.
f -- Existing file name.
F -- Possibly nonexistent file name.
G -- Possibly nonexistent file name, defaulting to just directory name.
i -- Ignored, i.e. always nil. Does not do I/O.
k -- Key sequence (downcase the last event if needed to get a definition).
K -- Key sequence to be redefined (do not downcase the last event).
m -- Value of mark as number. Does not do I/O.
M -- Any string. Inherits the current input method.
n -- Number read using minibuffer.
N -- Raw prefix arg, or if none, do like code `n'.
p -- Prefix arg converted to number. Does not do I/O.
P -- Prefix arg in raw form. Does not do I/O.
r -- Region: point and mark as 2 numeric args, smallest first. Does no I/O.
s -- Any string. Does not inherit the current input method.
S -- Any symbol.
U -- Mouse up event discarded by a previous k or K argument.
v -- Variable name: symbol that is user-variable-p.
x -- Lisp expression read but not evaluated.
X -- Lisp expression read and evaluated.
z -- Coding system.
Z -- Coding system, nil if no prefix arg.
In addition, if the string begins with `*'
then an error is signaled if the buffer is read-only.
This happens before reading any arguments.
If the string begins with `@', then Emacs searches the key sequence
which invoked the command for its first mouse click (or any other
event which specifies a window), and selects that window before
reading any arguments. You may use both `@' and `*'; they are
processed in the order that they appear.
usage: (interactive ARGS) */)
(args)
Lisp_Object args;
{
return Qnil;
}
DEFUN ("call-interactively", Fcall_interactively, Scall_interactively, 1, 3, 0,
doc: /* Call FUNCTION, reading args according to its interactive calling specs.
Return the value FUNCTION returns.
The function contains a specification of how to do the argument reading.
In the case of user-defined functions, this is specified by placing a call
to the function `interactive' at the top level of the function body.
See `interactive'.
Optional second arg RECORD-FLAG non-nil
means unconditionally put this command in the command-history.
Otherwise, this is done only if an arg is read using the minibuffer.
Optional third arg KEYS, if given, specifies the sequence of events to
supply if the command inquires which events were used to invoke it.
If KEYS is omitted or nil, the return value of `this-command-keys' is used. */)
(function, record_flag, keys)
Lisp_Object function, record_flag, keys;
{
関数宣言
fun = indirect_function (function);
/* Decode the kind of function. Either handle it and return,
or go to `lose' if not interactive, or go to `retry'
to specify a different function, or set either STRING or SPECS. */
if (SUBRP (fun))
{
string = (unsigned char *) XSUBR (fun)->prompt;
if (!string)
{
lose:
function = wrong_type_argument (Qcommandp, function);
goto retry;
}
}
else if (COMPILEDP (fun))
{
if ((XVECTOR (fun)->size & PSEUDOVECTOR_SIZE_MASK) <= COMPILED_INTERACTIVE)
goto lose;
specs = XVECTOR (fun)->contents[COMPILED_INTERACTIVE];
}
else
{
Lisp_Object form;
GCPRO2 (function, prefix_arg);
form = Finteractive_form (function);
UNGCPRO;
if (CONSP (form))
specs = filter_specs = Fcar (XCDR (form));
else
goto lose;
}
適当に解析
tem = string;
for (i = 1; *tem; i++)
{
switch (*tem)
{
case hogehoge:
..
break;
case 'r': /* Region, point and mark as 2 args. */
check_mark (1);
set_marker_both (point_marker, Qnil, PT, PT_BYTE);
/* visargs[i+1] = Qnil; */
foo = marker_position (current_buffer->mark);
/* visargs[i] = Qnil; */
args[i] = PT < foo ? point_marker : current_buffer->mark;
varies[i] = 3;
args[++i] = PT > foo ? point_marker : current_buffer->mark;
varies[i] = 4;
break;
}
}
....
{
Lisp_Object val;
specbind (Qcommand_debug_status, Qnil);
val = Ffuncall (count + 1, args);
UNGCPRO;
return unbind_to (speccount, val);
}
}
SUBRP (fun)の場合は、まずindirect_function関数でfunを取得し、そっからXSUBR (fun)->promptで取得されている。indirect_function関数はsrc/data.cで定義されている。
/* If OBJECT is a symbol, find the end of its function chain and
return the value found there. If OBJECT is not a symbol, just
return it. If there is a cycle in the function chain, signal a
cyclic-function-indirection error.
This is like Findirect_function, except that it doesn't signal an
error if the chain ends up unbound. */
Lisp_Object
indirect_function (object)
register Lisp_Object object;
{
Lisp_Object tortoise, hare;
hare = tortoise = object;
for (;;)
{
if (!SYMBOLP (hare) || EQ (hare, Qunbound))
break;
hare = XSYMBOL (hare)->function;
if (!SYMBOLP (hare) || EQ (hare, Qunbound))
break;
hare = XSYMBOL (hare)->function;
tortoise = XSYMBOL (tortoise)->function;
if (EQ (hare, tortoise))
Fsignal (Qcyclic_function_indirection, Fcons (object, Qnil));
}
return hare;
}
さて、次はXSUBR (fun)->promptの謎。XSUBRはたぶんtagged pointerのtagを除去しているだけだろうから、実際に重要なのはprompt変数。どうせこの辺りはsrc/lisp.hに有るのだろうと目星を付けると、案の定有りましたよ。
/* This structure describes a built-in function.
It is generated by the DEFUN macro only.
defsubr makes it into a Lisp object.
This type is treated in most respects as a pseudovector,
but since we never dynamically allocate or free them,
we don't need a next-vector field. */
struct Lisp_Subr
{
EMACS_INT size;
Lisp_Object (*function) ();
short min_args, max_args;
char *symbol_name;
char *prompt;
char *doc;
};
/* Define a built-in function for calling from Lisp.
`lname' should be the name to give the function in Lisp,
as a null-terminated C string.
`fnname' should be the name of the function in C.
By convention, it starts with F.
`sname' should be the name for the C constant structure
that records information on this function for internal use.
By convention, it should be the same as `fnname' but with S instead of F.
It's too bad that C macros can't compute this from `fnname'.
`minargs' should be a number, the minimum number of arguments allowed.
`maxargs' should be a number, the maximum number of arguments allowed,
or else MANY or UNEVALLED.
MANY means pass a vector of evaluated arguments,
in the form of an integer number-of-arguments
followed by the address of a vector of Lisp_Objects
which contains the argument values.
UNEVALLED means pass the list of unevaluated arguments
`prompt' says how to read arguments for an interactive call.
See the doc string for `interactive'.
A null string means call interactively with no arguments.
`doc' is documentation for the user. */
#if (!defined (__STDC__) && !defined (PROTOTYPES)) \
|| defined (USE_NONANSI_DEFUN)
#define DEFUN(lname, fnname, sname, minargs, maxargs, prompt, doc) \
Lisp_Object fnname (); \
DECL_ALIGN (struct Lisp_Subr, sname) = \
{ PVEC_SUBR | (sizeof (struct Lisp_Subr) / sizeof (EMACS_INT)), \
fnname, minargs, maxargs, lname, prompt, 0}; \
Lisp_Object fnname
#else
/* This version of DEFUN declares a function prototype with the right
arguments, so we can catch errors with maxargs at compile-time. */
#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
/* Note that the weird token-substitution semantics of ANSI C makes
this work for MANY and UNEVALLED. */
#define DEFUN_ARGS_MANY (int, Lisp_Object *)
#define DEFUN_ARGS_UNEVALLED (Lisp_Object)
#define DEFUN_ARGS_0 (void)
#define DEFUN_ARGS_1 (Lisp_Object)
#define DEFUN_ARGS_2 (Lisp_Object, Lisp_Object)
#define DEFUN_ARGS_3 (Lisp_Object, Lisp_Object, Lisp_Object)
#define DEFUN_ARGS_4 (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object)
#define DEFUN_ARGS_5 (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, \
Lisp_Object)
#define DEFUN_ARGS_6 (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, \
Lisp_Object, Lisp_Object)
#define DEFUN_ARGS_7 (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, \
Lisp_Object, Lisp_Object, Lisp_Object)
#define DEFUN_ARGS_8 (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, \
Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object)
#endif
DEFUN ("defun", Fdefun, Sdefun, 2, UNEVALLED, 0,
doc: /* Define NAME as a function.
The definition is (lambda ARGLIST [DOCSTRING] BODY...).
See also the function `interactive'.
usage: (defun NAME ARGLIST [DOCSTRING] BODY...) */)
(args)
Lisp_Object args;
{
register Lisp_Object fn_name;
register Lisp_Object defn;
fn_name = Fcar (args);
CHECK_SYMBOL (fn_name);
defn = Fcons (Qlambda, Fcdr (args));
if (!NILP (Vpurify_flag))
defn = Fpurecopy (defn);
if (CONSP (XSYMBOL (fn_name)->function)
&& EQ (XCAR (XSYMBOL (fn_name)->function), Qautoload))
LOADHIST_ATTACH (Fcons (Qt, fn_name));
Ffset (fn_name, defn);
LOADHIST_ATTACH (Fcons (Qdefun, fn_name));
return fn_name;
}
if (SUBRP (fun))
{
...
}
else if (COMPILEDP (fun))
{
...
}
else
{
Lisp_Object form;
GCPRO2 (function, prefix_arg);
form = Finteractive_form (function);
UNGCPRO;
if (CONSP (form))
specs = filter_specs = Fcar (XCDR (form));
else
goto lose;
}
/* If either SPECS or STRING is set to a string, use it. */
if (STRINGP (specs))
{
/* Make a copy of string so that if a GC relocates specs,
`string' will still be valid. */
string = (unsigned char *) alloca (SBYTES (specs) + 1);
bcopy (SDATA (specs), string,
SBYTES (specs) + 1);
}
DEFUN ("interactive-form", Finteractive_form, Sinteractive_form, 1, 1, 0,
doc: /* Return the interactive form of CMD or nil if none.
If CMD is not a command, the return value is nil.
Value, if non-nil, is a list \(interactive SPEC). */)
(cmd)
Lisp_Object cmd;
{
Lisp_Object fun = indirect_function (cmd);
if (SUBRP (fun))
{
if (XSUBR (fun)->prompt)
return list2 (Qinteractive, build_string (XSUBR (fun)->prompt));
}
else if (COMPILEDP (fun))
{
if ((ASIZE (fun) & PSEUDOVECTOR_SIZE_MASK) > COMPILED_INTERACTIVE)
return list2 (Qinteractive, AREF (fun, COMPILED_INTERACTIVE));
}
else if (CONSP (fun))
{
Lisp_Object funcar = XCAR (fun);
if (EQ (funcar, Qlambda))
return Fassq (Qinteractive, Fcdr (XCDR (fun)));
else if (EQ (funcar, Qautoload))
{
struct gcpro gcpro1;
GCPRO1 (cmd);
do_autoload (fun, cmd);
UNGCPRO;
return Finteractive_form (cmd);
}
}
return Qnil;
}
という訳で今回はinteractiveについて追ってみました。
[ return ]