diff src/fns.c @ 5285:99de5fd48e87

Tighten up Common Lisp compatibility, #'butlast, #'nbutlast, #'tailp, #'ldiff lisp/ChangeLog addition: 2010-10-14 Aidan Kehoe <kehoea@parhasard.net> * byte-optimize.el (side-effect-free-fns): * cl-macs.el (remf, getf): * cl-extra.el (tailp, cl-set-getf, cl-do-remf): * cl.el (ldiff, endp): Tighten up Common Lisp compatibility for #'ldiff, #'endp, #'tailp; add circularity checking for the first two. #'cl-set-getf and #'cl-do-remf were Lisp implementations of #'plist-put and #'plist-remprop; change the names to aliases, changes the macros that use them to using #'plist-put and #'plist-remprop directly. src/ChangeLog addition: 2010-10-14 Aidan Kehoe <kehoea@parhasard.net> * fns.c (Fnbutlast, Fbutlast): Tighten up Common Lisp compatibility for these two functions; they need to operate on dotted lists without erroring. tests/ChangeLog addition: 2010-10-14 Aidan Kehoe <kehoea@parhasard.net> * automated/lisp-tests.el (x): Test #'nbutlast, #'butlast with dotted lists. Check that #'ldiff and #'tailp don't hang on circular lists; check that #'tailp returns t with circular lists when that is appropriate. Test them both with dotted lists.
author Aidan Kehoe <kehoea@parhasard.net>
date Thu, 14 Oct 2010 18:50:38 +0100
parents be436ac36ba4
children 28651c24b3f8 b9167d522a9a
line wrap: on
line diff
--- a/src/fns.c	Tue Oct 12 21:11:46 2010 +0100
+++ b/src/fns.c	Thu Oct 14 18:50:38 2010 +0100
@@ -1570,72 +1570,99 @@
 
 DEFUN ("nbutlast", Fnbutlast, 1, 2, 0, /*
 Modify LIST to remove the last N (default 1) elements.
+
 If LIST has N or fewer elements, nil is returned and LIST is unmodified.
+Otherwise, LIST may be dotted, but not circular.
 */
        (list, n))
 {
-  EMACS_INT int_n;
+  Elemcount int_n = 1;
 
   CHECK_LIST (list);
 
-  if (NILP (n))
-    int_n = 1;
-  else
+  if (!NILP (n))
     {
       CHECK_NATNUM (n);
       int_n = XINT (n);
     }
 
-  {
-    Lisp_Object last_cons = list;
-
-    EXTERNAL_LIST_LOOP_1 (list)
-      {
-	if (int_n-- < 0)
-	  last_cons = XCDR (last_cons);
-      }
-
-    if (int_n >= 0)
-      return Qnil;
-
-    XCDR (last_cons) = Qnil;
-    return list;
-  }
+  if (CONSP (list))
+    {
+      Lisp_Object last_cons = list;
+
+      EXTERNAL_LIST_LOOP_3 (elt, list, tail)
+	{
+	  if (int_n-- < 0)
+	    {
+	      last_cons = XCDR (last_cons);
+	    }
+
+	  if (!CONSP (XCDR (tail)))
+	    {
+	      break;
+	    }
+	}
+
+      if (int_n >= 0)
+	{
+	  return Qnil;
+	}
+
+      XCDR (last_cons) = Qnil;
+    }
+
+  return list;
 }
 
 DEFUN ("butlast", Fbutlast, 1, 2, 0, /*
 Return a copy of LIST with the last N (default 1) elements removed.
+
 If LIST has N or fewer elements, nil is returned.
+Otherwise, LIST may be dotted, but not circular, and `(butlast LIST 0)'
+converts a dotted into a true list.
 */
        (list, n))
 {
-  EMACS_INT int_n;
+  Lisp_Object retval = Qnil, retval_tail = Qnil;
+  Elemcount int_n = 1;
 
   CHECK_LIST (list);
 
-  if (NILP (n))
-    int_n = 1;
-  else
+  if (!NILP (n))
     {
       CHECK_NATNUM (n);
       int_n = XINT (n);
     }
 
-  {
-    Lisp_Object retval = Qnil;
-    Lisp_Object tail = list;
-
-    EXTERNAL_LIST_LOOP_1 (list)
-      {
-	if (--int_n < 0)
-	  {
-	    retval = Fcons (XCAR (tail), retval);
-	    tail = XCDR (tail);
-	  }
-      }
-
-    return Fnreverse (retval);
-  }
+  if (CONSP (list))
+    {
+      Lisp_Object tail = list;
+
+      EXTERNAL_LIST_LOOP_3 (elt, list, list_tail)
+	{
+	  if (--int_n < 0)
+	    {
+	      if (NILP (retval_tail))
+		{
+		  retval = retval_tail = Fcons (XCAR (tail), Qnil);
+		}
+	      else
+		{
+		  XSETCDR (retval_tail, Fcons (XCAR (tail), Qnil));
+		  retval_tail = XCDR (retval_tail);
+		}
+
+	      tail = XCDR (tail);
+	    }
+
+	  if (!CONSP (XCDR (list_tail)))
+	    {
+	      break;
+	    }
+	}
+    }
+
+  return retval;
 }
 
 DEFUN ("member", Fmember, 2, 2, 0, /*