all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Paul Pogonyshev <pogonyshev@gmail.com>
To: Eli Zaretskii <eliz@gnu.org>
Cc: emacs-devel@gnu.org
Subject: Re: Arbitrary function: find the number(s) of expected arguments
Date: Fri, 25 Mar 2016 17:16:42 +0100	[thread overview]
Message-ID: <CAG7Bpaq0SOM8A1-cgsZsh-avCPh_yaUxFZda-qXdSzQDLTpGcA@mail.gmail.com> (raw)
In-Reply-To: <83pouj0wx8.fsf@gnu.org>


[-- Attachment #1.1: Type: text/plain, Size: 723 bytes --]

Eli Zaretskii wrote:

> If we are going to keep sub-arity, I'd prefer if this new function
> called it, instead of copying its code inline.
>

Done.


> Also, I believe you said you'd write the documentation?  Could you
> please add that?  Then the patch will be ready to go in, I think.
>

In the attached patch I modified `doc/lispref/functions.texi': text about
`subr-arity' is moved to a new section above about `func-arity' and
adapted as needed.  `subr-arity' is still in the documentation, but I
replaced its description with an advice to use `func-arity' instead. Is
that enough?

Do you still need changelog entries? Long time since I committed
anything to Emacs, maybe you finally got rid of them (I hope)?

Paul

[-- Attachment #1.2: Type: text/html, Size: 1661 bytes --]

[-- Attachment #2: func-arity.diff --]
[-- Type: text/plain, Size: 7490 bytes --]

diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index a2e94c3..559bf00 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -143,6 +143,18 @@ function, i.e., can be passed to @code{funcall}.  Note that
 and returns @code{nil} for special forms.
 @end defun
 
+  It is also possible to find out how many arguments an arbitrary
+function expects:
+
+@defun func-arity function
+This function provides information about the argument list of given
+@var{function}.  The returned value is a pair
+@code{(@var{min} . @var{max})}.  @var{min} is the minimum number of
+args.  @var{max} is the maximum number or the symbol @code{many}, for a
+function with @code{&rest} arguments, or the symbol @code{unevalled} if
+@var{function} is a special form.
+@end defun
+
 @noindent
 Unlike @code{functionp}, the next three functions do @emph{not} treat
 a symbol as its function definition.
@@ -176,12 +188,9 @@ function.  For example:
 @end defun
 
 @defun subr-arity subr
-This function provides information about the argument list of a
-primitive, @var{subr}.  The returned value is a pair
-@code{(@var{min} . @var{max})}.  @var{min} is the minimum number of
-args.  @var{max} is the maximum number or the symbol @code{many}, for a
-function with @code{&rest} arguments, or the symbol @code{unevalled} if
-@var{subr} is a special form.
+Works like @code{func-arity}, but only for built-in functions and
+without symbol indirection.  New code should use @code{func-arity}
+instead.
 @end defun
 
 @node Lambda Expressions
diff --git a/src/bytecode.c b/src/bytecode.c
index 9ae2e82..8108b17 100644
--- a/src/bytecode.c
+++ b/src/bytecode.c
@@ -1987,6 +1987,23 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
   return result;
 }
 
+/* `args_template' has the same meaning as in exec_byte_code() above. */
+Lisp_Object
+get_byte_code_arity (Lisp_Object args_template)
+{
+  if (INTEGERP (args_template))
+    {
+      ptrdiff_t at = XINT (args_template);
+      bool rest = (at & 128) != 0;
+      int mandatory = at & 127;
+      ptrdiff_t nonrest = at >> 8;
+
+      return Fcons (make_number (mandatory), rest ? Qmany : make_number (nonrest));
+    }
+  else
+    error ("Unknown args template!");
+}
+
 void
 syms_of_bytecode (void)
 {
diff --git a/src/eval.c b/src/eval.c
index 74b30e6..e0493c6 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -90,6 +90,7 @@ union specbinding *backtrace_top (void) EXTERNALLY_VISIBLE;
 
 static Lisp_Object funcall_lambda (Lisp_Object, ptrdiff_t, Lisp_Object *);
 static Lisp_Object apply_lambda (Lisp_Object, Lisp_Object, ptrdiff_t);
+static Lisp_Object lambda_arity (Lisp_Object);
 
 static Lisp_Object
 specpdl_symbol (union specbinding *pdl)
@@ -2934,6 +2935,115 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
   return unbind_to (count, val);
 }
 
+DEFUN ("func-arity", Ffunc_arity, Sfunc_arity, 1, 1, 0,
+       doc: /* Return minimum and maximum number of args allowed for FUNCTION.
+FUNCTION must be a function of some kind.
+The returned value is a pair (MIN . MAX).  MIN is the minimum number
+of args.  MAX is the maximum number or the symbol `many', for a
+function with `&rest' args, or `unevalled' for a special form.  */)
+  (Lisp_Object function)
+{
+  Lisp_Object original;
+  Lisp_Object funcar;
+  Lisp_Object result;
+  short minargs, maxargs;
+
+  original = function;
+
+ retry:
+
+  /* Optimize for no indirection.  */
+  function = original;
+  if (SYMBOLP (function) && !NILP (function)
+      && (function = XSYMBOL (function)->function, SYMBOLP (function)))
+    function = indirect_function (function);
+
+  if (SUBRP (function))
+    result = Fsubr_arity (function);
+  else if (COMPILEDP (function))
+    result = lambda_arity (function);
+  else
+    {
+      if (NILP (function))
+	xsignal1 (Qvoid_function, original);
+      if (!CONSP (function))
+	xsignal1 (Qinvalid_function, original);
+      funcar = XCAR (function);
+      if (!SYMBOLP (funcar))
+	xsignal1 (Qinvalid_function, original);
+      if (EQ (funcar, Qlambda)
+	  || EQ (funcar, Qclosure))
+	result = lambda_arity (function);
+      else if (EQ (funcar, Qautoload))
+	{
+	  Fautoload_do_load (function, original, Qnil);
+	  goto retry;
+	}
+      else
+	xsignal1 (Qinvalid_function, original);
+    }
+  return result;
+}
+
+/* FUN must be either a lambda-expression or a compiled-code object.  */
+static Lisp_Object
+lambda_arity (Lisp_Object fun)
+{
+  Lisp_Object val, syms_left, next;
+  ptrdiff_t minargs, maxargs;
+  bool optional;
+
+  if (CONSP (fun))
+    {
+      if (EQ (XCAR (fun), Qclosure))
+	{
+	  fun = XCDR (fun);	/* Drop `closure'.  */
+	  CHECK_LIST_CONS (fun, fun);
+	}
+      syms_left = XCDR (fun);
+      if (CONSP (syms_left))
+	syms_left = XCAR (syms_left);
+      else
+	xsignal1 (Qinvalid_function, fun);
+    }
+  else if (COMPILEDP (fun))
+    {
+      ptrdiff_t size = ASIZE (fun) & PSEUDOVECTOR_SIZE_MASK;
+      if (size <= COMPILED_STACK_DEPTH)
+	xsignal1 (Qinvalid_function, fun);
+      syms_left = AREF (fun, COMPILED_ARGLIST);
+      if (INTEGERP (syms_left))
+        return get_byte_code_arity (syms_left);
+    }
+  else
+    emacs_abort ();
+
+  minargs = maxargs = optional = 0;
+  for (; CONSP (syms_left); syms_left = XCDR (syms_left))
+    {
+      next = XCAR (syms_left);
+      if (!SYMBOLP (next))
+	xsignal1 (Qinvalid_function, fun);
+
+      if (EQ (next, Qand_rest))
+	return Fcons (make_number (minargs), Qmany);
+      else if (EQ (next, Qand_optional))
+	optional = 1;
+      else
+	{
+          if (!optional)
+            minargs++;
+          maxargs++;
+        }
+    }
+
+  if (!NILP (syms_left))
+    xsignal1 (Qinvalid_function, fun);
+
+  return Fcons (make_number (minargs), make_number (maxargs));
+}
+
+
 DEFUN ("fetch-bytecode", Ffetch_bytecode, Sfetch_bytecode,
        1, 1, 0,
        doc: /* If byte-compiled OBJECT is lazy-loaded, fetch it now.  */)
@@ -3808,6 +3918,7 @@ alist of active lexical bindings.  */);
   defsubr (&Seval);
   defsubr (&Sapply);
   defsubr (&Sfuncall);
+  defsubr (&Sfunc_arity);
   defsubr (&Srun_hooks);
   defsubr (&Srun_hook_with_args);
   defsubr (&Srun_hook_with_args_until_success);
diff --git a/src/lisp.h b/src/lisp.h
index e606ffa..7c8b452 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4215,6 +4215,7 @@ extern struct byte_stack *byte_stack_list;
 extern void relocate_byte_stack (void);
 extern Lisp_Object exec_byte_code (Lisp_Object, Lisp_Object, Lisp_Object,
 				   Lisp_Object, ptrdiff_t, Lisp_Object *);
+extern Lisp_Object get_byte_code_arity (Lisp_Object);
 
 /* Defined in macros.c.  */
 extern void init_macros (void);
diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el
index 8617369..57ff0c5 100644
--- a/test/src/fns-tests.el
+++ b/test/src/fns-tests.el
@@ -208,3 +208,13 @@
   (should (string-version-lessp "foo1.25.5.png" "foo1.125.5"))
   (should (string-version-lessp "2" "1245"))
   (should (not (string-version-lessp "1245" "2"))))
+
+(ert-deftest fns-tests-func-arity ()
+  (should (equal (func-arity 'car) '(1 . 1)))
+  (should (equal (func-arity 'caar) '(1 . 1)))
+  (should (equal (func-arity 'format) '(1 . many)))
+  (should (equal (func-arity 'Info-goto-node) '(1 . 3)))
+  (should (equal (func-arity (lambda (&rest x))) '(0 . many)))
+  (should (equal (func-arity (eval (lambda (x &optional y)) nil)) '(1 . 2)))
+  (should (equal (func-arity (eval (lambda (x &optional y)) t)) '(1 . 2)))
+  (should (equal (func-arity 'let) '(1 . unevalled))))

  reply	other threads:[~2016-03-25 16:16 UTC|newest]

Thread overview: 60+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-03-15 18:48 Arbitrary function: find the number(s) of expected arguments Paul Pogonyshev
2016-03-15 22:45 ` Davis Herring
2016-03-16  7:41   ` Paul Pogonyshev
2016-03-19 12:26     ` Paul Pogonyshev
2016-03-19 13:10       ` Eli Zaretskii
2016-03-19 13:42         ` Paul Pogonyshev
2016-03-19 13:54         ` Michael Heerdegen
2016-03-19 14:08           ` Eli Zaretskii
2016-03-19 15:20             ` Michael Heerdegen
2016-03-19 15:43               ` Eli Zaretskii
2016-03-19 15:57                 ` Michael Heerdegen
2016-03-19 16:24                   ` Eli Zaretskii
2016-03-19 17:43                     ` Michael Heerdegen
2016-03-19 17:50                       ` Eli Zaretskii
2016-03-19 17:59                         ` Michael Heerdegen
2016-03-19 18:14                           ` Eli Zaretskii
2016-03-19 16:14                 ` Philipp Stephani
2016-03-19 16:27                   ` Michael Heerdegen
2016-03-19 16:27                   ` Eli Zaretskii
2016-03-19 16:30                     ` Philipp Stephani
2016-03-19 16:32                       ` Eli Zaretskii
2016-03-19 16:34                         ` Philipp Stephani
2016-03-19 16:46                           ` Philipp Stephani
2016-03-19 16:47                           ` Eli Zaretskii
2016-03-19 17:16                             ` Philipp Stephani
2016-03-19 17:48                               ` Eli Zaretskii
2016-03-19 17:49                                 ` Philipp Stephani
2016-03-19 18:11                                   ` Eli Zaretskii
2016-03-19 18:35                                     ` Michael Heerdegen
2016-04-18 23:02                 ` Davis Herring
2016-03-19 19:52         ` Stefan Monnier
2016-03-19 20:33           ` Eli Zaretskii
2016-03-19 22:43             ` Stefan Monnier
2016-03-26 15:55           ` Elias Mårtenson
2016-03-26 17:20             ` Stefan Monnier
2016-03-19 14:26       ` Philipp Stephani
2016-03-19 16:51         ` Paul Pogonyshev
2016-03-19 18:09           ` Eli Zaretskii
2016-03-19 19:32             ` Michael Heerdegen
2016-03-19 19:39               ` Eli Zaretskii
2016-03-19 20:59                 ` Michael Heerdegen
2016-03-21 18:36             ` Paul Pogonyshev
2016-03-25  8:44               ` Eli Zaretskii
2016-03-25 16:16                 ` Paul Pogonyshev [this message]
2016-03-25 16:35                   ` Drew Adams
2016-03-25 17:16                     ` Paul Pogonyshev
2016-03-25 18:19                       ` Drew Adams
2016-03-25 18:28                         ` Clément Pit--Claudel
2016-03-25 18:51                           ` Use plain-text for mail [was: Arbitrary function: find the number(s) of expected arguments] Drew Adams
2016-03-25 18:57                             ` Use plain-text for mail [ Lars Magne Ingebrigtsen
2016-03-25 19:49                               ` Andreas Schwab
2016-03-26  1:12                             ` Use plain-text for mail [was: Arbitrary function: find the number(s) of expected arguments] Yuri Khan
2016-03-25 17:39                     ` Arbitrary function: find the number(s) of expected arguments Eli Zaretskii
2016-03-25 18:31                       ` Drew Adams
2016-03-26  8:27                   ` Eli Zaretskii
2016-03-26 11:42                     ` Paul Pogonyshev
2016-04-02  9:48                       ` Eli Zaretskii
     [not found]           ` <<83y49e731p.fsf@gnu.org>
2016-03-19 19:21             ` Drew Adams
2016-04-18 18:43               ` Davis Herring
2016-03-16  3:47 ` Stefan Monnier

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAG7Bpaq0SOM8A1-cgsZsh-avCPh_yaUxFZda-qXdSzQDLTpGcA@mail.gmail.com \
    --to=pogonyshev@gmail.com \
    --cc=eliz@gnu.org \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.