aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Pogonyshev2016-03-26 11:19:43 +0300
committerEli Zaretskii2016-03-26 11:19:43 +0300
commit6f3243db55e61847784178ea812f28ddf003544a (patch)
treee2bbb4e4c3a49ab661524135c6b1a610580431b8
parent368b9bb45f125061506d43af4bd4791ab2cfd7b9 (diff)
downloademacs-6f3243db55e61847784178ea812f28ddf003544a.tar.gz
emacs-6f3243db55e61847784178ea812f28ddf003544a.zip
Implement 'func-arity'
* src/eval.c (Ffunc_arity, lambda_arity): New functions. * src/bytecode.c (get_byte_code_arity): New function. * src/lisp.h (get_byte_code_arity): Add prototype. * doc/lispref/functions.texi (What Is a Function): Document 'func-arity'. * etc/NEWS: Mention 'func-arity'. * test/src/fns-tests.el (fns-tests-func-arity): New test set.
-rw-r--r--doc/lispref/functions.texi40
-rw-r--r--etc/NEWS7
-rw-r--r--src/bytecode.c18
-rw-r--r--src/eval.c111
-rw-r--r--src/lisp.h1
-rw-r--r--test/src/fns-tests.el11
6 files changed, 182 insertions, 6 deletions
diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index a2e94c34b62..ff21abba61e 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -143,6 +143,37 @@ function, i.e., can be passed to @code{funcall}. Note that
143and returns @code{nil} for special forms. 143and returns @code{nil} for special forms.
144@end defun 144@end defun
145 145
146 It is also possible to find out how many arguments an arbitrary
147function expects:
148
149@defun func-arity function
150This function provides information about the argument list of the
151specified @var{function}. The returned value is a cons cell of the
152form @w{@code{(@var{min} . @var{max})}}, where @var{min} is the
153minimum number of arguments, and @var{max} is either the maximum
154number of arguments, or the symbol @code{many} for functions with
155@code{&rest} arguments, or the symbol @code{unevalled} if
156@var{function} is a special form.
157
158Note that this function might return inaccurate results in some
159situations, such as the following:
160
161@itemize @minus
162@item
163Functions defined using @code{apply-partially} (@pxref{Calling
164Functions, apply-partially}).
165
166@item
167Functions that are advised using @code{advice-add} (@pxref{Advising
168Named Functions}).
169
170@item
171Functions that determine the argument list dynamically, as part of
172their code.
173@end itemize
174
175@end defun
176
146@noindent 177@noindent
147Unlike @code{functionp}, the next three functions do @emph{not} treat 178Unlike @code{functionp}, the next three functions do @emph{not} treat
148a symbol as its function definition. 179a symbol as its function definition.
@@ -176,12 +207,9 @@ function. For example:
176@end defun 207@end defun
177 208
178@defun subr-arity subr 209@defun subr-arity subr
179This function provides information about the argument list of a 210This works like @code{func-arity}, but only for built-in functions and
180primitive, @var{subr}. The returned value is a pair 211without symbol indirection. It signals an error for non-built-in
181@code{(@var{min} . @var{max})}. @var{min} is the minimum number of 212functions. We recommend to use @code{func-arity} instead.
182args. @var{max} is the maximum number or the symbol @code{many}, for a
183function with @code{&rest} arguments, or the symbol @code{unevalled} if
184@var{subr} is a special form.
185@end defun 213@end defun
186 214
187@node Lambda Expressions 215@node Lambda Expressions
diff --git a/etc/NEWS b/etc/NEWS
index 0bc61308945..ce21532b68d 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -182,6 +182,13 @@ a new window when opening man pages when there's already one, use
182 (mode . Man-mode)))) 182 (mode . Man-mode))))
183 183
184+++ 184+++
185** New function 'func-arity' returns information about the argument list
186of an arbitrary function.
187This is a generalization of 'subr-arity' for functions that are not
188built-in primitives. We recommend using this new function instead of
189'subr-arity'.
190
191+++
185** 'parse-partial-sexp' state has a new element. Element 10 is 192** 'parse-partial-sexp' state has a new element. Element 10 is
186non-nil when the last character scanned might be the first character 193non-nil when the last character scanned might be the first character
187of a two character construct, i.e. a comment delimiter or escaped 194of a two character construct, i.e. a comment delimiter or escaped
diff --git a/src/bytecode.c b/src/bytecode.c
index 9ae2e820d51..4ff15d2912a 100644
--- a/src/bytecode.c
+++ b/src/bytecode.c
@@ -1987,6 +1987,24 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
1987 return result; 1987 return result;
1988} 1988}
1989 1989
1990/* `args_template' has the same meaning as in exec_byte_code() above. */
1991Lisp_Object
1992get_byte_code_arity (Lisp_Object args_template)
1993{
1994 if (INTEGERP (args_template))
1995 {
1996 ptrdiff_t at = XINT (args_template);
1997 bool rest = (at & 128) != 0;
1998 int mandatory = at & 127;
1999 ptrdiff_t nonrest = at >> 8;
2000
2001 return Fcons (make_number (mandatory),
2002 rest ? Qmany : make_number (nonrest));
2003 }
2004 else
2005 error ("Unknown args template!");
2006}
2007
1990void 2008void
1991syms_of_bytecode (void) 2009syms_of_bytecode (void)
1992{ 2010{
diff --git a/src/eval.c b/src/eval.c
index 74b30e66bce..64a6655684c 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -90,6 +90,7 @@ union specbinding *backtrace_top (void) EXTERNALLY_VISIBLE;
90 90
91static Lisp_Object funcall_lambda (Lisp_Object, ptrdiff_t, Lisp_Object *); 91static Lisp_Object funcall_lambda (Lisp_Object, ptrdiff_t, Lisp_Object *);
92static Lisp_Object apply_lambda (Lisp_Object, Lisp_Object, ptrdiff_t); 92static Lisp_Object apply_lambda (Lisp_Object, Lisp_Object, ptrdiff_t);
93static Lisp_Object lambda_arity (Lisp_Object);
93 94
94static Lisp_Object 95static Lisp_Object
95specpdl_symbol (union specbinding *pdl) 96specpdl_symbol (union specbinding *pdl)
@@ -2934,6 +2935,115 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
2934 return unbind_to (count, val); 2935 return unbind_to (count, val);
2935} 2936}
2936 2937
2938DEFUN ("func-arity", Ffunc_arity, Sfunc_arity, 1, 1, 0,
2939 doc: /* Return minimum and maximum number of args allowed for FUNCTION.
2940FUNCTION must be a function of some kind.
2941The returned value is a cons cell (MIN . MAX). MIN is the minimum number
2942of args. MAX is the maximum number, or the symbol `many', for a
2943function with `&rest' args, or `unevalled' for a special form. */)
2944 (Lisp_Object function)
2945{
2946 Lisp_Object original;
2947 Lisp_Object funcar;
2948 Lisp_Object result;
2949 short minargs, maxargs;
2950
2951 original = function;
2952
2953 retry:
2954
2955 /* Optimize for no indirection. */
2956 function = original;
2957 if (SYMBOLP (function) && !NILP (function)
2958 && (function = XSYMBOL (function)->function, SYMBOLP (function)))
2959 function = indirect_function (function);
2960
2961 if (SUBRP (function))
2962 result = Fsubr_arity (function);
2963 else if (COMPILEDP (function))
2964 result = lambda_arity (function);
2965 else
2966 {
2967 if (NILP (function))
2968 xsignal1 (Qvoid_function, original);
2969 if (!CONSP (function))
2970 xsignal1 (Qinvalid_function, original);
2971 funcar = XCAR (function);
2972 if (!SYMBOLP (funcar))
2973 xsignal1 (Qinvalid_function, original);
2974 if (EQ (funcar, Qlambda)
2975 || EQ (funcar, Qclosure))
2976 result = lambda_arity (function);
2977 else if (EQ (funcar, Qautoload))
2978 {
2979 Fautoload_do_load (function, original, Qnil);
2980 goto retry;
2981 }
2982 else
2983 xsignal1 (Qinvalid_function, original);
2984 }
2985 return result;
2986}
2987
2988/* FUN must be either a lambda-expression or a compiled-code object. */
2989static Lisp_Object
2990lambda_arity (Lisp_Object fun)
2991{
2992 Lisp_Object val, syms_left, next;
2993 ptrdiff_t minargs, maxargs;
2994 bool optional;
2995
2996 if (CONSP (fun))
2997 {
2998 if (EQ (XCAR (fun), Qclosure))
2999 {
3000 fun = XCDR (fun); /* Drop `closure'. */
3001 CHECK_LIST_CONS (fun, fun);
3002 }
3003 syms_left = XCDR (fun);
3004 if (CONSP (syms_left))
3005 syms_left = XCAR (syms_left);
3006 else
3007 xsignal1 (Qinvalid_function, fun);
3008 }
3009 else if (COMPILEDP (fun))
3010 {
3011 ptrdiff_t size = ASIZE (fun) & PSEUDOVECTOR_SIZE_MASK;
3012 if (size <= COMPILED_STACK_DEPTH)
3013 xsignal1 (Qinvalid_function, fun);
3014 syms_left = AREF (fun, COMPILED_ARGLIST);
3015 if (INTEGERP (syms_left))
3016 return get_byte_code_arity (syms_left);
3017 }
3018 else
3019 emacs_abort ();
3020
3021 minargs = maxargs = optional = 0;
3022 for (; CONSP (syms_left); syms_left = XCDR (syms_left))
3023 {
3024 next = XCAR (syms_left);
3025 if (!SYMBOLP (next))
3026 xsignal1 (Qinvalid_function, fun);
3027
3028 if (EQ (next, Qand_rest))
3029 return Fcons (make_number (minargs), Qmany);
3030 else if (EQ (next, Qand_optional))
3031 optional = 1;
3032 else
3033 {
3034 if (!optional)
3035 minargs++;
3036 maxargs++;
3037 }
3038 }
3039
3040 if (!NILP (syms_left))
3041 xsignal1 (Qinvalid_function, fun);
3042
3043 return Fcons (make_number (minargs), make_number (maxargs));
3044}
3045
3046
2937DEFUN ("fetch-bytecode", Ffetch_bytecode, Sfetch_bytecode, 3047DEFUN ("fetch-bytecode", Ffetch_bytecode, Sfetch_bytecode,
2938 1, 1, 0, 3048 1, 1, 0,
2939 doc: /* If byte-compiled OBJECT is lazy-loaded, fetch it now. */) 3049 doc: /* If byte-compiled OBJECT is lazy-loaded, fetch it now. */)
@@ -3808,6 +3918,7 @@ alist of active lexical bindings. */);
3808 defsubr (&Seval); 3918 defsubr (&Seval);
3809 defsubr (&Sapply); 3919 defsubr (&Sapply);
3810 defsubr (&Sfuncall); 3920 defsubr (&Sfuncall);
3921 defsubr (&Sfunc_arity);
3811 defsubr (&Srun_hooks); 3922 defsubr (&Srun_hooks);
3812 defsubr (&Srun_hook_with_args); 3923 defsubr (&Srun_hook_with_args);
3813 defsubr (&Srun_hook_with_args_until_success); 3924 defsubr (&Srun_hook_with_args_until_success);
diff --git a/src/lisp.h b/src/lisp.h
index e606ffa0259..7c8b452dd5f 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4215,6 +4215,7 @@ extern struct byte_stack *byte_stack_list;
4215extern void relocate_byte_stack (void); 4215extern void relocate_byte_stack (void);
4216extern Lisp_Object exec_byte_code (Lisp_Object, Lisp_Object, Lisp_Object, 4216extern Lisp_Object exec_byte_code (Lisp_Object, Lisp_Object, Lisp_Object,
4217 Lisp_Object, ptrdiff_t, Lisp_Object *); 4217 Lisp_Object, ptrdiff_t, Lisp_Object *);
4218extern Lisp_Object get_byte_code_arity (Lisp_Object);
4218 4219
4219/* Defined in macros.c. */ 4220/* Defined in macros.c. */
4220extern void init_macros (void); 4221extern void init_macros (void);
diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el
index 861736995f4..688ff1f6bd9 100644
--- a/test/src/fns-tests.el
+++ b/test/src/fns-tests.el
@@ -208,3 +208,14 @@
208 (should (string-version-lessp "foo1.25.5.png" "foo1.125.5")) 208 (should (string-version-lessp "foo1.25.5.png" "foo1.125.5"))
209 (should (string-version-lessp "2" "1245")) 209 (should (string-version-lessp "2" "1245"))
210 (should (not (string-version-lessp "1245" "2")))) 210 (should (not (string-version-lessp "1245" "2"))))
211
212(ert-deftest fns-tests-func-arity ()
213 (should (equal (func-arity 'car) '(1 . 1)))
214 (should (equal (func-arity 'caar) '(1 . 1)))
215 (should (equal (func-arity 'format) '(1 . many)))
216 (require 'info)
217 (should (equal (func-arity 'Info-goto-node) '(1 . 3)))
218 (should (equal (func-arity (lambda (&rest x))) '(0 . many)))
219 (should (equal (func-arity (eval (lambda (x &optional y)) nil)) '(1 . 2)))
220 (should (equal (func-arity (eval (lambda (x &optional y)) t)) '(1 . 2)))
221 (should (equal (func-arity 'let) '(1 . unevalled))))