changeset 5084:6afe991b8135

Add a PARSE_KEYWORDS macro, use it in #'make-hash-table. lisp/ChangeLog addition: 2010-03-01 Aidan Kehoe <kehoea@parhasard.net> * cl-seq.el (cl-parsing-keywords): * cl-macs.el (cl-do-arglist): Use the new invalid-keyword-argument error here. src/ChangeLog addition: 2010-03-01 Aidan Kehoe <kehoea@parhasard.net> * lisp.h (PARSE_KEYWORDS): New macro, for parsing keyword arguments from C subrs. * elhash.c (Fmake_hash_table): Use it. * general-slots.h (Q_allow_other_keys): Add this symbol. * eval.c (non_nil_allow_other_keys_p): (invalid_keyword_argument): New functions, called from the keyword argument parsing code. * data.c (init_errors_once_early): Add the new invalid-keyword-argument error here.
author Aidan Kehoe <kehoea@parhasard.net>
date Mon, 01 Mar 2010 21:05:33 +0000
parents 88f955fa5a7f
children 1ee30d3f9dd0
files lisp/ChangeLog lisp/cl-macs.el lisp/cl-seq.el src/ChangeLog src/data.c src/elhash.c src/eval.c src/general-slots.h src/lisp.h
diffstat 9 files changed, 208 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Fri Feb 26 15:52:24 2010 +0000
+++ b/lisp/ChangeLog	Mon Mar 01 21:05:33 2010 +0000
@@ -1,3 +1,9 @@
+2010-03-01  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* cl-seq.el (cl-parsing-keywords):
+	* cl-macs.el (cl-do-arglist):
+	Use the new invalid-keyword-argument error here.
+
 2010-02-26  Aidan Kehoe  <kehoea@parhasard.net>
 
 	Back out Ben's revision c673987f5f3d.
--- a/lisp/cl-macs.el	Fri Feb 26 15:52:24 2010 +0000
+++ b/lisp/cl-macs.el	Mon Mar 01 21:05:33 2010 +0000
@@ -494,8 +494,7 @@
 			  (list t
 				(list
 				 'error
-				 (format "Keyword argument %%s not one of %s"
-					 keys)
+                                 ''invalid-keyword-argument
 				 (list 'car var)))))))
 	    (push (list 'let (list (list var restarg)) check) bind-forms)))
       (while (and (eq (car args) '&aux) (pop args))
--- a/lisp/cl-seq.el	Fri Feb 26 15:52:24 2010 +0000
+++ b/lisp/cl-seq.el	Mon Mar 01 21:05:33 2010 +0000
@@ -107,7 +107,7 @@
 							   other-keys))))
 				  '(car (cdr (memq (quote :allow-other-keys)
 						   cl-keys)))
-				  '(error "Bad keyword argument %s"
+				  '(error 'invalid-keyword-argument
 					  (car cl-keys-temp)))
 			    '(setq cl-keys-temp (cdr (cdr cl-keys-temp)))))))
 	  body))))
--- a/src/ChangeLog	Fri Feb 26 15:52:24 2010 +0000
+++ b/src/ChangeLog	Mon Mar 01 21:05:33 2010 +0000
@@ -1,3 +1,15 @@
+2010-03-01  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* lisp.h (PARSE_KEYWORDS): New macro, for parsing keyword
+	arguments from C subrs.
+	* elhash.c (Fmake_hash_table): Use it.
+	* general-slots.h (Q_allow_other_keys): Add this symbol.
+	* eval.c (non_nil_allow_other_keys_p):
+	(invalid_keyword_argument):
+	New functions, called from the keyword argument parsing code.
+	* data.c (init_errors_once_early):
+	Add the new invalid-keyword-argument error here.
+
 2010-02-26  Aidan Kehoe  <kehoea@parhasard.net>
 
 	* file-coding.c (Fmake_coding_system_internal):
--- a/src/data.c	Fri Feb 26 15:52:24 2010 +0000
+++ b/src/data.c	Mon Mar 01 21:05:33 2010 +0000
@@ -41,7 +41,8 @@
 Lisp_Object Qcircular_list, Qcircular_property_list;
 Lisp_Object Qinvalid_argument, Qinvalid_constant, Qwrong_type_argument;
 Lisp_Object Qargs_out_of_range;
-Lisp_Object Qwrong_number_of_arguments, Qinvalid_function, Qno_catch;
+Lisp_Object Qwrong_number_of_arguments, Qinvalid_function;
+Lisp_Object Qinvalid_keyword_argument, Qno_catch;
 Lisp_Object Qinternal_error, Qinvalid_state, Qstack_overflow, Qout_of_memory;
 Lisp_Object Qvoid_variable, Qcyclic_variable_indirection;
 Lisp_Object Qvoid_function, Qcyclic_function_indirection;
@@ -3472,6 +3473,7 @@
   DEFERROR_STANDARD (Qwrong_number_of_arguments, Qinvalid_argument);
   DEFERROR_STANDARD (Qinvalid_function, Qinvalid_argument);
   DEFERROR_STANDARD (Qinvalid_constant, Qinvalid_argument);
+  DEFERROR_STANDARD (Qinvalid_keyword_argument, Qinvalid_argument);
   DEFERROR (Qno_catch, "No catch for tag", Qinvalid_argument);
 
   DEFERROR_STANDARD (Qinvalid_state, Qerror);
--- a/src/elhash.c	Fri Feb 26 15:52:24 2010 +0000
+++ b/src/elhash.c	Mon Mar 01 21:05:33 2010 +0000
@@ -84,7 +84,7 @@
 #include "opaque.h"
 
 Lisp_Object Qhash_tablep;
-static Lisp_Object Qhashtable, Qhash_table;
+static Lisp_Object Qhashtable, Qhash_table, Qmake_hash_table;
 static Lisp_Object Qweakness, Qvalue, Qkey_or_value, Qkey_and_value;
 static Lisp_Object Vall_weak_hash_tables;
 static Lisp_Object Qrehash_size, Qrehash_threshold;
@@ -993,29 +993,27 @@
 */
        (int nargs, Lisp_Object *args))
 {
-  int i = 0;
-  Lisp_Object test	       = Qnil;
-  Lisp_Object size	       = Qnil;
-  Lisp_Object rehash_size      = Qnil;
-  Lisp_Object rehash_threshold = Qnil;
-  Lisp_Object weakness	       = Qnil;
-
-  while (i + 1 < nargs)
-    {
-      Lisp_Object keyword = args[i++];
-      Lisp_Object value   = args[i++];
+#ifdef NO_NEED_TO_HANDLE_21_4_CODE
+  PARSE_KEYWORDS (Qmake_hash_table, nargs, args, 0, 5,
+                  (test, size, rehash_size, rehash_threshold, weakness),
+                  NULL, weakness = Qunbound), 0);
+#else
+  PARSE_KEYWORDS (Qmake_hash_table, nargs, args, 0, 6,
+                  (test, size, rehash_size, rehash_threshold, weakness,
+		   type), (type = Qunbound, weakness = Qunbound), 0);
 
-      if      (EQ (keyword, Q_test))		 test		  = value;
-      else if (EQ (keyword, Q_size))		 size		  = value;
-      else if (EQ (keyword, Q_rehash_size))	 rehash_size	  = value;
-      else if (EQ (keyword, Q_rehash_threshold)) rehash_threshold = value;
-      else if (EQ (keyword, Q_weakness))	 weakness	  = value;
-      else if (EQ (keyword, Q_type))/*obsolete*/ weakness	  = value;
-      else invalid_constant ("Invalid hash table property keyword", keyword);
+  if (EQ (weakness, Qunbound))
+    {
+      if (EQ (weakness, Qunbound) && !EQ (type, Qunbound))
+        {
+          weakness = type;
+        }
+      else
+        {
+          weakness = Qnil;
+        }
     }
-
-  if (i < nargs)
-    sferror ("Hash table property requires a value", args[i]);
+#endif
 
 #define VALIDATE_VAR(var) \
 if (!NILP (var)) hash_table_##var##_validate (Q##var, var, ERROR_ME);
@@ -1854,6 +1852,7 @@
   DEFSYMBOL_MULTIWORD_PREDICATE (Qhash_tablep);
   DEFSYMBOL (Qhash_table);
   DEFSYMBOL (Qhashtable);
+  DEFSYMBOL (Qmake_hash_table);
   DEFSYMBOL (Qweakness);
   DEFSYMBOL (Qvalue);
   DEFSYMBOL (Qkey_or_value);
--- a/src/eval.c	Fri Feb 26 15:52:24 2010 +0000
+++ b/src/eval.c	Mon Mar 01 21:05:33 2010 +0000
@@ -418,6 +418,29 @@
 static Lisp_Object maybe_get_trapping_problems_backtrace (void);
 
 
+
+/* When parsing keyword arguments; is some element of NARGS
+   :allow-other-keys, and is that element followed by a non-nil Lisp
+   object? */
+
+Boolint
+non_nil_allow_other_keys_p (Elemcount offset, int nargs, Lisp_Object *args)
+{
+  Lisp_Object key, value;
+  while (offset + 1 < nargs)
+    {
+      key = args[offset++];
+      value = args[offset++];
+      if (EQ (key, Q_allow_other_keys)) 
+	{
+          /* The ANSI Common Lisp standard says the first value for a given
+             keyword overrides. */
+          return !NILP (value);
+	}
+    }
+  return 0;
+}
+
 /************************************************************************/
 /*			The subr object type				*/
 /************************************************************************/
@@ -3050,6 +3073,12 @@
 }
 
 DOESNT_RETURN
+invalid_keyword_argument (Lisp_Object function, Lisp_Object keyword)
+{
+  signal_error_1 (Qinvalid_keyword_argument, list2 (function, keyword));
+}
+
+DOESNT_RETURN
 invalid_constant (const Ascbyte *reason, Lisp_Object frob)
 {
   signal_error (Qinvalid_constant, reason, frob);
--- a/src/general-slots.h	Fri Feb 26 15:52:24 2010 +0000
+++ b/src/general-slots.h	Mon Mar 01 21:05:33 2010 +0000
@@ -49,6 +49,7 @@
 SYMBOL (Qactually_requested);
 SYMBOL (Qafter);
 SYMBOL (Qall);
+SYMBOL_KEYWORD (Q_allow_other_keys);
 SYMBOL (Qand);
 SYMBOL (Qappend);
 SYMBOL (Qascii);
--- a/src/lisp.h	Fri Feb 26 15:52:24 2010 +0000
+++ b/src/lisp.h	Mon Mar 01 21:05:33 2010 +0000
@@ -4041,6 +4041,136 @@
  while (NILP (Ffunctionp (fun)))		\
    signal_invalid_function_error (fun);		\
  } while (0)
+
+/************************************************************************/
+/*                      Parsing keyword arguments                       */
+/************************************************************************/
+
+/* The C subr must have been declared with MANY as its max args, and this
+   PARSE_KEYWORDS call must come before any statements.
+
+   FUNCTION is the name of the current function, as a symbol.
+
+   NARGS is the count of arguments supplied to FUNCTION.
+
+   ARGS is a pointer to the argument vector (not a Lisp vector) supplied to
+   FUNCTION.
+
+   KEYWORDS_OFFSET is the offset into ARGS where the keyword arguments start.
+
+   KEYWORD_COUNT is the number of keywords FUNCTION is normally prepared to
+   handle.
+
+   KEYWORDS is a parenthesised list of those keywords, without the initial
+   Q_.
+
+   KEYWORD_DEFAULTS allows you to set non-nil defaults. Put (keywordname =
+   initial_value) in this parameter, a collection of C statements surrounded
+   by parentheses and separated by the comma operator. If you don't need
+   this, supply NULL as KEYWORD_DEFAULTS.
+
+   ALLOW_OTHER_KEYS corresponds to the &allow-other-keys argument list
+   entry in defun*; it is 1 if other keys are normally allowed, 0
+   otherwise. This may be overridden in the caller by specifying
+   :allow-other-keys t in the argument list.
+
+   For keywords which appear multiple times in the called argument list, the
+   leftmost one overrides, as specified in section 7.1.1 of the CLHS.
+
+   If you want to check whether a given keyword argument was set (as in the
+   SVAR argument to defun*), supply Qunbound as its default in
+   KEYWORD_DEFAULTS, and examine it once PARSE_KEYWORDS is done. Lisp code
+   cannot supply Qunbound as an argument, so if it is still Qunbound, it was
+   not set.
+
+   There is no elegant way with this macro to have one name for the keyword
+   and an unrelated name for the local variable, as is possible with the
+   ((:keyword unrelated-var)) syntax in defun* and in Common Lisp. That
+   shouldn't matter in practice. */
+ 
+#define PARSE_KEYWORDS(function, nargs, args, keywords_offset,          \
+                       keyword_count, keywords, keyword_defaults,       \
+                       allow_other_keys)                                \
+  DECLARE_N_KEYWORDS_##keyword_count keywords;                          \
+                                                                        \
+  do                                                                    \
+    {                                                                   \
+      Lisp_Object pk_key, pk_value;                                     \
+      Elemcount pk_i = nargs - 1;                                       \
+      Boolint pk_allow_other_keys = allow_other_keys;                   \
+                                                                        \
+      if ((nargs - keywords_offset) & 1)                                \
+        {                                                               \
+          if (!allow_other_keys                                         \
+              && !(pk_allow_other_keys                                  \
+                   = non_nil_allow_other_keys_p (keywords_offset,       \
+                                                 nargs, args)))         \
+            {                                                           \
+              signal_wrong_number_of_arguments_error (function, nargs); \
+            }                                                           \
+          else                                                          \
+            {                                                           \
+              /* Ignore the trailing arg; so below always sees an even  \
+                 number of arguments. */                                \
+              pk_i -= 1;                                                \
+            }                                                           \
+        }                                                               \
+                                                                        \
+      (void)(keyword_defaults);                                         \
+                                                                        \
+      /* Start from the end, because the leftmost element overrides. */ \
+      while (pk_i > keywords_offset)                                    \
+        {                                                               \
+          pk_value = args[pk_i--];                                      \
+          pk_key = args[pk_i--];                                        \
+                                                                        \
+          if (0) {}                                                     \
+          CHECK_N_KEYWORDS_##keyword_count keywords                     \
+          else if (allow_other_keys || pk_allow_other_keys)             \
+            {                                                           \
+              continue;                                                 \
+            }                                                           \
+          else if (!(pk_allow_other_keys                                \
+                     = non_nil_allow_other_keys_p (keywords_offset,     \
+                                                   nargs, args)))       \
+            {                                                           \
+              invalid_keyword_argument (function, pk_key);              \
+            }                                                           \
+        }                                                               \
+    } while (0)
+
+#define DECLARE_N_KEYWORDS_1(a)                 \
+    Lisp_Object a = Qnil
+#define DECLARE_N_KEYWORDS_2(a,b)               \
+  DECLARE_N_KEYWORDS_1(a), b = Qnil
+#define DECLARE_N_KEYWORDS_3(a,b,c)             \
+  DECLARE_N_KEYWORDS_2(a,b), c = Qnil
+#define DECLARE_N_KEYWORDS_4(a,b,c,d)           \
+  DECLARE_N_KEYWORDS_3(a,b,c), d = Qnil
+#define DECLARE_N_KEYWORDS_5(a,b,c,d,e)         \
+  DECLARE_N_KEYWORDS_4(a,b,c,d), e = Qnil
+#define DECLARE_N_KEYWORDS_6(a,b,c,d,e,f)       \
+  DECLARE_N_KEYWORDS_5(a,b,c,d,e), f = Qnil
+#define DECLARE_N_KEYWORDS_7(a,b,c,d,e,f,g)     \
+  DECLARE_N_KEYWORDS_6(a,b,c,d,e,f), g = Qnil
+
+#define CHECK_N_KEYWORDS_1(a)                                           \
+    else if (EQ (pk_key, Q_##a)) { a = pk_value; }
+#define CHECK_N_KEYWORDS_2(a,b)             CHECK_N_KEYWORDS_1(a)       \
+    else if (EQ (pk_key, Q_##b)) { b = pk_value; }
+#define CHECK_N_KEYWORDS_3(a,b,c)           CHECK_N_KEYWORDS_2(a,b)     \
+    else if (EQ (pk_key, Q_##c)) { c = pk_value; }
+#define CHECK_N_KEYWORDS_4(a,b,c,d)         CHECK_N_KEYWORDS_3(a,b,c)   \
+    else if (EQ (pk_key, Q_##d)) { d = pk_value; }
+#define CHECK_N_KEYWORDS_5(a,b,c,d,e)       CHECK_N_KEYWORDS_4(a,b,c,d) \
+    else if (EQ (pk_key, Q_##e)) { e = pk_value; }
+#define CHECK_N_KEYWORDS_6(a,b,c,d,e,f)     CHECK_N_KEYWORDS_5(a,b,c,d,e) \
+    else if (EQ (pk_key, Q_##f)) { f = pk_value; }
+#define CHECK_N_KEYWORDS_7(a,b,c,d,e,f,g)   CHECK_N_KEYWORDS_6(a,b,c,d,e,f) \
+    else if (EQ (pk_key, Q_##g)) { g = pk_value; }
+
+Boolint non_nil_allow_other_keys_p (Elemcount offset, int nargs,
+                                    Lisp_Object *args);
 
 
 /************************************************************************/
@@ -4898,7 +5028,8 @@
     Qcircular_list, Qcircular_property_list, Qconversion_error,
     Qcyclic_variable_indirection, Qdomain_error, Qediting_error,
     Qend_of_buffer, Qend_of_file, Qerror, Qfile_error, Qinternal_error,
-    Qinvalid_change, Qinvalid_constant, Qinvalid_function, Qinvalid_operation,
+    Qinvalid_change, Qinvalid_constant, Qinvalid_function, 
+    Qinvalid_keyword_argument, Qinvalid_operation,
     Qinvalid_read_syntax, Qinvalid_state, Qio_error, Qlist_formation_error,
     Qmalformed_list, Qmalformed_property_list, Qno_catch, Qout_of_memory,
     Qoverflow_error, Qprinting_unreadable_object, Qquit, Qrange_error,
@@ -5126,6 +5257,8 @@
 						      Lisp_Object frob2));
 void maybe_invalid_argument (const Ascbyte *, Lisp_Object, Lisp_Object,
 			     Error_Behavior);
+MODULE_API DECLARE_DOESNT_RETURN (invalid_keyword_argument (Lisp_Object fun,
+                                                            Lisp_Object kw));
 MODULE_API DECLARE_DOESNT_RETURN (invalid_operation (const Ascbyte *reason,
 						     Lisp_Object frob));
 MODULE_API DECLARE_DOESNT_RETURN (invalid_operation_2 (const Ascbyte *reason,