Subject: 24.4.50; move parse-integer and digit-char-p to cl-lib

From: Leo Liu <[email-masked]>
Date: Thu, 25 Sep 2014 16:19:59 +0800

Any comments on the following patch:

=== modified file 'lisp/calendar/parse-time.el'
--- lisp/calendar/parse-time.el	2014-08-03 15:38:52 +0000
+++ lisp/calendar/parse-time.el	2014-09-25 08:11:25 +0000
@@ -34,21 +34,12 @@
 
 ;;; Code:
 
-(eval-when-compile (require 'cl-lib))
-
-(defvar parse-time-digits (make-vector 256 nil))
+(require 'cl-lib)
 
 ;; Byte-compiler warnings
 (defvar parse-time-elt)
 (defvar parse-time-val)
 
-(unless (aref parse-time-digits ?0)
-  (cl-loop for i from ?0 to ?9
-           do (aset parse-time-digits i (- i ?0))))
-
-(defsubst digit-char-p (char)
-  (aref parse-time-digits char))
-
 (defsubst parse-time-string-chars (char)
   (save-match-data
     (let (case-fold-search str)
@@ -62,27 +53,6 @@
 (put 'parse-error 'error-conditions '(parse-error error))
 (put 'parse-error 'error-message "Parsing error")
 
-(defsubst parse-integer (string &optional start end)
-  "[CL] Parse and return the integer in STRING, or nil if none."
-  (let ((integer 0)
-	(digit 0)
-	(index (or start 0))
-	(end (or end (length string))))
-    (when (< index end)
-      (let ((sign (aref string index)))
-	(if (or (eq sign ?+) (eq sign ?-))
-	    (setq sign (parse-time-string-chars sign)
-		  index (1+ index))
-	  (setq sign 1))
-	(while (and (< index end)
-		    (setq digit (digit-char-p (aref string index))))
-	  (setq integer (+ (* integer 10) digit)
-		index (1+ index)))
-	(if (/= index end)
-	    (signal 'parse-error `("not an integer"
-				   ,(substring string (or start 0) end)))
-	  (* sign integer))))))
-
 (defun parse-time-tokenize (string)
   "Tokenize STRING into substrings."
   (let ((start nil)
@@ -100,7 +70,7 @@
 		  (setq c (parse-time-string-chars (aref string index))))
 	(setq all-digits (and all-digits (eq c ?0))))
       (if (<= index end)
-	  (push (if all-digits (parse-integer string start index)
+	  (push (if all-digits (cl-parse-integer string :start start :end index)
 		  (substring string start index))
 		list)))
     (nreverse list)))
@@ -147,8 +117,8 @@
 	       (= 5 (length parse-time-elt))
 	       (or (= (aref parse-time-elt 0) ?+)
 		   (= (aref parse-time-elt 0) ?-))))
-     ,#'(lambda () (* 60 (+ (parse-integer parse-time-elt 3 5)
-			    (* 60 (parse-integer parse-time-elt 1 3)))
+     ,#'(lambda () (* 60 (+ (cl-parse-integer parse-time-elt :start 3 :end 5)
+			    (* 60 (cl-parse-integer parse-time-elt :start 1 :end 3)))
 		      (if (= (aref parse-time-elt 0) ?-) -1 1))))
     ((5 4 3)
      ,#'(lambda () (and (stringp parse-time-elt)
@@ -210,9 +180,10 @@
 		(let ((new-val (if rule
 				   (let ((this (pop rule)))
 				     (if (vectorp this)
-					 (parse-integer
+					 (cl-parse-integer
 					  parse-time-elt
-					  (aref this 0) (aref this 1))
+					  :start (aref this 0)
+					  :end (aref this 1))
 				       (funcall this)))
 				 parse-time-val)))
 		  (rplaca (nthcdr (pop slots) time) new-val))))))))

=== modified file 'lisp/emacs-lisp/cl-extra.el'
--- lisp/emacs-lisp/cl-extra.el	2014-03-20 18:16:47 +0000
+++ lisp/emacs-lisp/cl-extra.el	2014-09-25 08:14:57 +0000
@@ -383,6 +383,28 @@
   "Return 1 if X is positive, -1 if negative, 0 if zero."
   (cond ((> x 0) 1) ((< x 0) -1) (t 0)))
 
+;;;###autoload
+(cl-defun cl-parse-integer (string &key start end radix junk-allowed)
+  "Parse integer from the substring of STRING from START to END."
+  (let* ((start (or start 0))
+	 (end   (or end (length string)))
+	 (radix (or radix 10)))
+    (or (< start end (1+ (length string)))
+	(error "Bad interval: [%d, %d)" start end))
+    (let ((sign (cl-case (aref string start)
+		  (?+ (cl-incf start) +1)
+		  (?- (cl-incf start) -1)
+		  (t  +1)))
+	  digit sum)
+      (while (and (< start end)
+		  (setq digit (cl-digit-char-p (aref string start) radix)))
+	(setq sum (+ (* (or sum 0) radix) digit)
+	      start (1+ start)))
+      (cond ((and junk-allowed (null sum)) sum)
+	    (junk-allowed (* sign sum))
+	    ((/= start end) (error "Not an integer string: %s" string))
+	    (t (* sign sum))))))
+
 
 ;; Random numbers.

=== modified file 'lisp/emacs-lisp/cl-lib.el'
--- lisp/emacs-lisp/cl-lib.el	2014-05-21 00:41:21 +0000
+++ lisp/emacs-lisp/cl-lib.el	2014-09-25 07:55:40 +0000
@@ -279,6 +279,25 @@
   "Return t if INTEGER is even."
   (eq (logand integer 1) 0))
 
+(defconst cl-digit-char-table
+  (let* ((digits (make-vector 256 nil))
+         (populate (lambda (start end base)
+                     (mapc (lambda (i)
+                             (aset digits i (+ base (- i start))))
+                           (number-sequence start end)))))
+    (funcall populate ?0 ?9 0)
+    (funcall populate ?A ?Z 10)
+    (funcall populate ?a ?z 10)
+    digits))
+
+(defun cl-digit-char-p (char &optional radix)
+  "Test if CHAR is a digit in the specified RADIX (default 10).
+If true return the decimal value of digit CHAR in RADIX."
+  (or (<= 2 (or radix 10) 36)
+      (signal 'args-out-of-range (list 'radix radix '(2 36))))
+  (let ((n (aref cl-digit-char-table char)))
+    (and n (< n (or radix 10)) n)))
+
 (defvar cl--random-state
   (vector 'cl--random-state-tag -1 30 (cl--random-time)))

=== modified file 'test/automated/cl-lib.el'
--- test/automated/cl-lib.el	2014-04-22 21:32:51 +0000
+++ test/automated/cl-lib.el	2014-09-25 08:16:10 +0000
@@ -223,6 +223,23 @@
     (should (= (cl-the integer (cl-incf side-effect)) 1))
     (should (= side-effect 1))))
 
+(ert-deftest cl-digit-char-p ()
+  (should (cl-digit-char-p ?3))
+  (should (cl-digit-char-p ?a 11))
+  (should-not (cl-digit-char-p ?a))
+  (should (cl-digit-char-p ?w 36))
+  (should-error (cl-digit-char-p ?a 37))
+  (should-error (cl-digit-char-p ?a 1)))
+
+(ert-deftest cl-parse-integer ()
+  (should-error (cl-parse-integer "abc"))
+  (should (null (cl-parse-integer "abc" :junk-allowed t)))
+  (should (= 342391 (cl-parse-integer "0123456789" :radix 8 :junk-allowed t)))
+  (should-error (cl-parse-integer "0123456789" :radix 8))
+  (should (= -239 (cl-parse-integer "-efz" :radix 16 :junk-allowed t)))
+  (should-error (cl-parse-integer "efz" :radix 16))
+  (should (= 239 (cl-parse-integer "zzef" :radix 16 :start 2))))
+
 (ert-deftest cl-loop-destructuring-with ()
   (should (equal (cl-loop with (a b c) = '(1 2 3) return (+ a b c)) 6)))