diff options
| author | Paul Pogonyshev | 2016-03-26 11:19:43 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2016-03-26 11:19:43 +0300 |
| commit | 6f3243db55e61847784178ea812f28ddf003544a (patch) | |
| tree | e2bbb4e4c3a49ab661524135c6b1a610580431b8 | |
| parent | 368b9bb45f125061506d43af4bd4791ab2cfd7b9 (diff) | |
| download | emacs-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.texi | 40 | ||||
| -rw-r--r-- | etc/NEWS | 7 | ||||
| -rw-r--r-- | src/bytecode.c | 18 | ||||
| -rw-r--r-- | src/eval.c | 111 | ||||
| -rw-r--r-- | src/lisp.h | 1 | ||||
| -rw-r--r-- | test/src/fns-tests.el | 11 |
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 | |||
| 143 | and returns @code{nil} for special forms. | 143 | and 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 | ||
| 147 | function expects: | ||
| 148 | |||
| 149 | @defun func-arity function | ||
| 150 | This function provides information about the argument list of the | ||
| 151 | specified @var{function}. The returned value is a cons cell of the | ||
| 152 | form @w{@code{(@var{min} . @var{max})}}, where @var{min} is the | ||
| 153 | minimum number of arguments, and @var{max} is either the maximum | ||
| 154 | number 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 | |||
| 158 | Note that this function might return inaccurate results in some | ||
| 159 | situations, such as the following: | ||
| 160 | |||
| 161 | @itemize @minus | ||
| 162 | @item | ||
| 163 | Functions defined using @code{apply-partially} (@pxref{Calling | ||
| 164 | Functions, apply-partially}). | ||
| 165 | |||
| 166 | @item | ||
| 167 | Functions that are advised using @code{advice-add} (@pxref{Advising | ||
| 168 | Named Functions}). | ||
| 169 | |||
| 170 | @item | ||
| 171 | Functions that determine the argument list dynamically, as part of | ||
| 172 | their code. | ||
| 173 | @end itemize | ||
| 174 | |||
| 175 | @end defun | ||
| 176 | |||
| 146 | @noindent | 177 | @noindent |
| 147 | Unlike @code{functionp}, the next three functions do @emph{not} treat | 178 | Unlike @code{functionp}, the next three functions do @emph{not} treat |
| 148 | a symbol as its function definition. | 179 | a 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 |
| 179 | This function provides information about the argument list of a | 210 | This works like @code{func-arity}, but only for built-in functions and |
| 180 | primitive, @var{subr}. The returned value is a pair | 211 | without symbol indirection. It signals an error for non-built-in |
| 181 | @code{(@var{min} . @var{max})}. @var{min} is the minimum number of | 212 | functions. We recommend to use @code{func-arity} instead. |
| 182 | args. @var{max} is the maximum number or the symbol @code{many}, for a | ||
| 183 | function 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 |
| @@ -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 | ||
| 186 | of an arbitrary function. | ||
| 187 | This is a generalization of 'subr-arity' for functions that are not | ||
| 188 | built-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 |
| 186 | non-nil when the last character scanned might be the first character | 193 | non-nil when the last character scanned might be the first character |
| 187 | of a two character construct, i.e. a comment delimiter or escaped | 194 | of 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. */ | ||
| 1991 | Lisp_Object | ||
| 1992 | get_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 | |||
| 1990 | void | 2008 | void |
| 1991 | syms_of_bytecode (void) | 2009 | syms_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 | ||
| 91 | static Lisp_Object funcall_lambda (Lisp_Object, ptrdiff_t, Lisp_Object *); | 91 | static Lisp_Object funcall_lambda (Lisp_Object, ptrdiff_t, Lisp_Object *); |
| 92 | static Lisp_Object apply_lambda (Lisp_Object, Lisp_Object, ptrdiff_t); | 92 | static Lisp_Object apply_lambda (Lisp_Object, Lisp_Object, ptrdiff_t); |
| 93 | static Lisp_Object lambda_arity (Lisp_Object); | ||
| 93 | 94 | ||
| 94 | static Lisp_Object | 95 | static Lisp_Object |
| 95 | specpdl_symbol (union specbinding *pdl) | 96 | specpdl_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 | ||
| 2938 | DEFUN ("func-arity", Ffunc_arity, Sfunc_arity, 1, 1, 0, | ||
| 2939 | doc: /* Return minimum and maximum number of args allowed for FUNCTION. | ||
| 2940 | FUNCTION must be a function of some kind. | ||
| 2941 | The returned value is a cons cell (MIN . MAX). MIN is the minimum number | ||
| 2942 | of args. MAX is the maximum number, or the symbol `many', for a | ||
| 2943 | function 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. */ | ||
| 2989 | static Lisp_Object | ||
| 2990 | lambda_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 | |||
| 2937 | DEFUN ("fetch-bytecode", Ffetch_bytecode, Sfetch_bytecode, | 3047 | DEFUN ("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; | |||
| 4215 | extern void relocate_byte_stack (void); | 4215 | extern void relocate_byte_stack (void); |
| 4216 | extern Lisp_Object exec_byte_code (Lisp_Object, Lisp_Object, Lisp_Object, | 4216 | extern 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 *); |
| 4218 | extern Lisp_Object get_byte_code_arity (Lisp_Object); | ||
| 4218 | 4219 | ||
| 4219 | /* Defined in macros.c. */ | 4220 | /* Defined in macros.c. */ |
| 4220 | extern void init_macros (void); | 4221 | extern 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)))) | ||