[cl-l10n-cvs] CVS update: cl-l10n/ChangeLog cl-l10n/README cl-l10n/cl-l10n.asd cl-l10n/load-locale.lisp cl-l10n/locale.lisp cl-l10n/package.lisp cl-l10n/parse-number.lisp cl-l10n/printers.lisp cl-l10n/tests.lisp cl-l10n/utils.lisp

Sean Ross sross at common-lisp.net
Thu Dec 30 11:56:45 UTC 2004


Update of /project/cl-l10n/cvsroot/cl-l10n
In directory common-lisp.net:/tmp/cvs-serv10150

Modified Files:
	ChangeLog README cl-l10n.asd load-locale.lisp locale.lisp 
	package.lisp parse-number.lisp printers.lisp tests.lisp 
	utils.lisp 
Log Message:
ChangeLog 2004-12-30
Date: Thu Dec 30 12:56:41 2004
Author: sross

Index: cl-l10n/ChangeLog
diff -u cl-l10n/ChangeLog:1.6 cl-l10n/ChangeLog:1.7
--- cl-l10n/ChangeLog:1.6	Fri Dec 17 11:06:43 2004
+++ cl-l10n/ChangeLog	Thu Dec 30 12:56:38 2004
@@ -1,3 +1,11 @@
+2004-12-30 Sean Ross	<sross at common-lisp.net>
+	* printers.lisp, load-locale.lisp: Changed format-number and
+	format-money to use a format string created at locale load time.
+	* locale.lisp: Cache Getter functions.
+	
+2004-12-20 Sean Ross	<sross at common-lisp.net>
+	* printers.lisp: Added formatter.
+	
 2004-12-17 Sean Ross	<sross at common-lisp.net>
 	* printers.lisp: Fixed incorrect sign when printing 
 	numbers and money.


Index: cl-l10n/README
diff -u cl-l10n/README:1.1 cl-l10n/README:1.2
--- cl-l10n/README:1.1	Tue Nov 30 11:05:07 2004
+++ cl-l10n/README	Thu Dec 30 12:56:38 2004
@@ -8,7 +8,7 @@
 cl-l10n is a localization package for common-lisp. It is meant 
 to be serve the same purpose as Allegro Common Lisp's
 various locale functions. It currently runs on 
-CMUCL, SBCL, CLISP and Lispworks although porting to a new
+CMUCL, SBCL, CLISP, ECL and Lispworks although porting to a new
 implementation should be ridiculously trivial.
 
 


Index: cl-l10n/cl-l10n.asd
diff -u cl-l10n/cl-l10n.asd:1.6 cl-l10n/cl-l10n.asd:1.7
--- cl-l10n/cl-l10n.asd:1.6	Fri Dec 17 11:06:43 2004
+++ cl-l10n/cl-l10n.asd	Thu Dec 30 12:56:38 2004
@@ -11,7 +11,7 @@
   :name "CL-L10N"
   :author "Sean Ross <sdr at jhb.ucs.co.za>"
   :maintainer "Sean Ross <sdr at jhb.ucs.co.za>"
-  :version "0.1.10"
+  :version "0.2.0"
   :description "Portable CL Locale Support"
   :long-description "Portable CL Package to support localization"
   :licence "MIT"
@@ -19,10 +19,10 @@
                (:file "parse-number" :depends-on ("package"))
                (:file "utils" :depends-on ("package"))
                (:file "locale" :depends-on ("utils"))
-               (:file "printers" :depends-on ("locale"))
+               (:file "load-locale" :depends-on ("locale"))
+               (:file "printers" :depends-on ("load-locale"))
                (:file "parsers" :depends-on ("printers" "parse-number"))
-               (:file "i18n" :depends-on ("printers"))
-               (:file "load-locale" :depends-on ("locale")))
+               (:file "i18n" :depends-on ("printers")))
   :depends-on (:cl-ppcre))
 
 (defmethod perform :after ((o load-op) (c (eql (find-system :cl-l10n))))


Index: cl-l10n/load-locale.lisp
diff -u cl-l10n/load-locale.lisp:1.6 cl-l10n/load-locale.lisp:1.7
--- cl-l10n/load-locale.lisp:1.6	Wed Dec  1 12:48:40 2004
+++ cl-l10n/load-locale.lisp	Thu Dec 30 12:56:38 2004
@@ -9,6 +9,9 @@
   (let ((name (aif (position #\. loc-name)
                    (subseq loc-name 0 it)
                    loc-name)))
+    (unless use-cache
+      ;; The local file might have changed so ...
+      (clear-getter-cache))
     (acond ((and (not name) (not errorp)) nil)
            ((and use-cache (get-locale name)) it)
            ((probe-file (merge-pathnames *locale-path* name))
@@ -16,10 +19,18 @@
            ((not errorp) (warn "Can't find locale ~A." name))
            (errorp (locale-error "Can't find locale ~A." name)))))
 
+(defvar *locale-type* 'locale)
+
+(defun locale-des->locale (loc)
+  (etypecase loc
+    (locale loc)
+    (string (locale loc))
+    (symbol (locale (string loc)))))
+
 (defun load-locale (name)
   (let ((path (merge-pathnames *locale-path* name)))
-    (format t "~&;; Loading locale from ~A.~%" path)
-    (let ((locale (make-instance 'locale :name name))
+    (cl:format t "~&;; Loading locale from ~A.~%" path)
+    (let ((locale (make-instance *locale-type* :name name))
           (*read-eval* nil)
           (*print-circle* nil))
       (with-open-file (stream path
@@ -30,6 +41,7 @@
             (awhen (make-category locale it (parse-category it stream
                                                             escape comment))
               (setf (get-category (category-name it) locale) it)))))
+      (add-printers locale)
       (setf (get-locale name) locale))))
 
 (defun load-all-locales (&optional (*locale-path* *locale-path*))
@@ -39,6 +51,67 @@
         (handler-case (load-locale (pathname-name x))
           (locale-error (c) (warn "Unable to load locale ~A. ~%~A." x c)))))))
 
+
+(defun create-number-fmt-string (locale no-ts)
+  (cl:format nil "~~A~~,,'~A,~A~A~~{~~A~~}" (schar (locale-thousands-sep locale) 0)
+             (locale-grouping locale)
+             (if no-ts "D" ":D")))
+
+(defun get-descriptors (minusp locale)
+  (if minusp 
+      (values (locale-n-sep-by-space locale)
+              (= 1 (locale-n-cs-precedes locale))
+              (locale-n-sign-posn locale)
+              (locale-negative-sign locale))
+      (values (locale-p-sep-by-space locale)
+              (= 1 (locale-p-cs-precedes locale))
+              (locale-p-sign-posn locale)
+              (locale-positive-sign locale))))
+
+(defun create-money-fmt-string (locale no-ts minusp)
+  (multiple-value-bind (sep-by-space prec spos sign) 
+      (get-descriptors minusp locale)
+    (let ((sym-sep (if (zerop sep-by-space) "" " ")))
+      (with-output-to-string (stream)
+        ;; sign and sign separator
+        (when (or* (= spos 0 1 3))
+          (princ (if (zerop spos) "(" sign) stream)
+          (when (= 2 sep-by-space)
+            (princ #\Space stream)))
+        ;; Sym and seperator
+        (princ "~A" stream)
+        (when prec
+          (princ sym-sep stream))
+        ;; Actual number
+        (cl:format stream "~~,,'~A,~A~A~~{~~A~~}" 
+                   (schar (locale-mon-thousands-sep locale) 0)
+                   (locale-mon-grouping locale)
+                   (if no-ts "D" ":D"))
+        (unless prec
+          (princ sym-sep stream))
+        (princ "~A" stream)
+        (when (or* (= spos 0 2 4))
+          (when (= 2 sep-by-space)
+            (princ #\Space stream))
+          (princ (if (zerop spos) ")" sign) stream))))))
+
+(defun add-printers (locale)
+  (setf (printers locale)
+        (nconc (list :number-no-ts
+                     (create-number-fmt-string locale t))
+               (list :number-ts
+                     (create-number-fmt-string locale nil))
+               (list :money-p-no-ts
+                     (create-money-fmt-string locale t nil))
+               (list :money-p-ts
+                     (create-money-fmt-string locale nil nil))
+               (list :money-n-no-ts
+                     (create-money-fmt-string locale t t))
+               (list :money-n-ts
+                     (create-money-fmt-string locale nil t))
+               (printers locale))))
+        
+
 (defvar *category-loaders*
   '(("LC_IDENTIFICATION" . load-identification)
     ("LC_MONETARY" . load-category)
@@ -61,11 +134,11 @@
 (defun load-category (locale name vals)
   (declare (ignore locale))
   (let ((cat (make-instance 'category :name name)))
-    (typecase vals 
+    (etypecase vals 
       (category vals)
-      (t (dolist (x vals)
-           (setf (get-cat-val (car x) cat) (cdr x)))
-         cat))))
+      (cons (dolist (x vals)
+              (setf (get-cat-val (car x) cat) (cdr x)))
+            cat))))
 
 (defvar *id-vals* 
   '(("title" . title)
@@ -165,22 +238,24 @@
                    :everything)
                   #\>))
 
+(defvar *match-scanner* (cl-ppcre:create-scanner *regex*))
+
 (defun old-real-value (val)
-  (aif (all-matches-as-strings *regex* val)
+  (aif (all-matches-as-strings *match-scanner* val)
        (map #-lispworks 'string #+lispworks 
             'lw:text-string #'real-character it)
        val))
 
 ;; KLUDGE
 (defun real-value (val)
-  (let ((val (old-real-value val)))
-    (if (string= val "\"\"")
-        ""
-        val)))
+  (remove #\" (old-real-value val)))
 
 
+(defvar *split-scanner* 
+  (cl-ppcre:create-scanner '(:char-class #\;)))
+                         
 (defun parse-value (val)
-  (let ((all-vals (split '(:char-class #\;) val)))
+  (let ((all-vals (split *split-scanner* val)))
     (if (singlep all-vals)
         (real-value (car all-vals))
         (mapcar #'real-value all-vals))))
@@ -201,9 +276,9 @@
   (loop for line = (read-line stream nil stream)
         until (eq line stream) do
     (if (and (> (length line) 3) (search "LC_" line :end2 3)
-             (not (some #'(lambda (x)
-                            (search x line :test #'string=))
-                        *ignore-categories*))) 
+             (notany #'(lambda (x)
+                         (search x line :test #'string=))
+                     *ignore-categories*))
         (return-from next-header line))))
 
 (defun load-default-locale ()


Index: cl-l10n/locale.lisp
diff -u cl-l10n/locale.lisp:1.5 cl-l10n/locale.lisp:1.6
--- cl-l10n/locale.lisp:1.5	Wed Dec  8 11:02:23 2004
+++ cl-l10n/locale.lisp	Thu Dec 30 12:56:38 2004
@@ -4,8 +4,9 @@
 ;; TODO
 ;;  What to do with LC_CTYPE, LC_COLLATE
 ;;  Test on windows.
-;;  Parsers?
+;;  Parsers (money and time)
 ;;  locale aliases
+;;  Optimizing print-time
 
 (in-package :cl-l10n )
 
@@ -18,29 +19,29 @@
                    (append (pathname-directory path)
                            '("locales"))
                    :defaults #P"")))
-                  
 
 (defvar *locale* nil)
 
-(defvar *locales* (make-hash-table :test #'equal))
+(defvar *locales* (make-hash-table :test #'equal)
+  "Hash table containing all loaded locales keyed on name (eg. \"af_ZA\")")
 
 ;; Conditions
 (defun locale-report (obj stream)
-  (format stream "~A" (mesg obj)))
+  (cl:format stream "~A" (mesg obj)))
 
 (define-condition locale-error ()
   ((mesg :accessor mesg :initarg :mesg :initform "Unknown."))
   (:report locale-report))
 
 (defun locale-error (string &rest args)
-  (error 'locale-error :mesg (apply #'format nil string args)))
-
+  (error 'locale-error :mesg (apply #'cl:format nil string args)))
 
 ;; Classes
 (defclass locale ()
   ((locale-name :accessor locale-name :initarg :name 
                 :initform (required-arg :name))
    (title :accessor title :initarg :title :initform nil)
+   (printers :accessor printers :initarg :printers :initform nil)
    (source :accessor source :initarg :source :initform nil)
    (language :accessor language :initarg :language :initform nil)
    (territory :accessor territory :initarg :territory :initform nil)
@@ -74,7 +75,6 @@
 (defmacro get-cat-val (value cat)
   `(gethash ,value (vals ,cat)))
 
-
 (defun locale-value (locale cat key)
   (awhen (get-category cat locale)
     (get-cat-val key it)))
@@ -86,19 +86,26 @@
   #+clisp (ext:getenv word)
   #+ecl (si:getenv word))
 
-
 ;; Getters
+(let ((getter-cache (make-hash-table :test #'equal)))
+  (defun gett-value (locale cat key &optional (wrap #'identity))
+    (let ((lookup-key (list locale cat key)))
+      (multiple-value-bind (val win) (gethash lookup-key getter-cache)
+        (if (or val win)
+            val
+            (setf (gethash lookup-key getter-cache)
+                  (funcall wrap (locale-value locale cat key)))))))
+  (defun clear-getter-cache ()
+    (setf getter-cache (make-hash-table :test #'equal))))
+
 (defmacro defgetter (key cat &key wrap)
   (let ((name (symb "LOCALE-" (substitute #\- #\_ (string-upcase key)))))
     `(progn 
-      (defun ,name (&optional (locale *locale*))
-        (let ((locale (locale-des->locale locale)))
-          (when locale
-            (awhen (get-category ,cat locale)
-              ,(if wrap
-                   `(funcall ,wrap (get-cat-val ,key it))
-                   `(get-cat-val ,key it))))))
-      (export ',name))))
+       (defun ,name (&optional (locale *locale*))
+         (let ((locale (locale-des->locale locale)))
+           (when locale
+             (gett-value locale ,cat ,key ,@(if wrap (list wrap) nil)))))
+       (export ',name))))
 
 (defun parse-car-or-val (x)
   (values (parse-integer (if (consp x) (car x) x))))
@@ -146,4 +153,4 @@
 (defgetter "measurement" "LC_MEASUREMENT")
 
 
-;; EOF
\ No newline at end of file
+;; EOF


Index: cl-l10n/package.lisp
diff -u cl-l10n/package.lisp:1.3 cl-l10n/package.lisp:1.4
--- cl-l10n/package.lisp:1.3	Wed Dec  8 11:02:23 2004
+++ cl-l10n/package.lisp	Thu Dec 30 12:56:38 2004
@@ -4,11 +4,11 @@
 
 (defpackage #:cl-l10n 
   (:use #:cl #:cl-ppcre)
-  (:shadow cl:format)
+  (:shadow cl:format cl:formatter)
   (:export #:locale-name #:category-name #:locale #:category #:locale-error
            #:get-category #:get-cat-val #:locale-value #:load-all-locales
            #:*locale* #:*locale-path* #:*locales*
            #:format-number #:print-number #:format-money #:print-money
            #:format-time #:print-time #:add-resources #:bundle 
-           #:add-resource #:gettext #:parse-number))
+           #:add-resource #:gettext #:parse-number #:*float-digits*))
            


Index: cl-l10n/parse-number.lisp
diff -u cl-l10n/parse-number.lisp:1.2 cl-l10n/parse-number.lisp:1.3
--- cl-l10n/parse-number.lisp:1.2	Fri Dec 17 11:06:43 2004
+++ cl-l10n/parse-number.lisp	Thu Dec 30 12:56:38 2004
@@ -40,8 +40,8 @@
 	   :initarg :reason
 	   :initform "Not specified"))
   (:report (lambda (c s)
-	     (format s "Invalid number: ~S [Reason: ~A]"
-		     (value c) (reason c)))))
+	     (cl:format s "Invalid number: ~S [Reason: ~A]"
+                   (value c) (reason c)))))
 
 (declaim (inline parse-integer-and-places))
 (defun parse-integer-and-places (string start end &key (radix 10))


Index: cl-l10n/printers.lisp
diff -u cl-l10n/printers.lisp:1.6 cl-l10n/printers.lisp:1.7
--- cl-l10n/printers.lisp:1.6	Fri Dec 17 11:06:43 2004
+++ cl-l10n/printers.lisp	Thu Dec 30 12:56:38 2004
@@ -2,64 +2,34 @@
 ;; See the file LICENCE for licence information.
 (in-package :cl-l10n)
 
-;; Number and Money
-(defun digits-list (integer &optional (radix 10))
-  (assert (>= integer 0))
-  (loop collect (mod integer radix)
-        while (> (setf integer (floor integer radix)) 0)))
-
-(defun print-int (stream sign int sep grouping)
-  (let* ((digits (digits-list int))
-         (fmt-string (mkstr "~A~{~{~A~}~^" sep "~}")))
-    (format stream fmt-string 
-            sign (mapcar #'nreverse (nreverse (group digits grouping))))))
-
+;; Number
 (defun get-sign (arg locale)
   (cond ((plusp arg) (locale-positive-sign locale))
         ((minusp arg) (locale-negative-sign locale))
         (t "")))
 
-(defun get-point (locale no-point float-part)
-  (if (and (string= float-part "0") no-point)
-      ""
-      (locale-decimal-point locale)))
-
-(defun get-sep (locale no-sep)
-  (if no-sep 
-      ""
-      (locale-thousands-sep locale)))
-
-(defun locale-des->locale (loc)
-  (etypecase loc
-    (locale loc)
-    (string (locale loc))
-    (symbol (locale (string loc)))))
-
 (defvar *float-digits* 2
   "Used when all values after the decimal point are zero to
 determine the number of zero's to print")
 
+(defun fix-float-string (string size)
+  (if (string= string "")
+      (make-string size :initial-element #\0)
+      string))
+
 (defun format-number (stream arg no-dp no-ts
                       &optional (locale *locale*))
-  (let ((locale (locale-des->locale locale)))
-    (multiple-value-bind (int-part float-part) (split-float (abs (float arg)))
-      (let* ((sign (get-sign arg locale))
-             (point (get-point locale no-dp float-part))
-             (float-part (if (every #'(lambda (x)
-                                        (zerop (or (digit-char-p x) 1)))
-                                    float-part)
-                             (make-string *float-digits*
-                                          :initial-element #\0)
-                             float-part))
-             (sep (get-sep locale no-ts))
-             (grouping (locale-grouping locale)))
-        (print-int stream sign int-part sep grouping)
-        (unless (and (every #'(lambda (x)
-                                (zerop (or (digit-char-p x) 1)))
-                            float-part)
-                     no-dp)
-          (princ point stream)
-          (princ float-part stream))))))
+  (let ((locale (locale-des->locale locale))
+        (float-part (float-part (coerce (abs arg) 'double-float))))
+    (cl:format stream 
+               (getf (printers locale)
+                     (if no-ts :number-no-ts :number-ts))
+               (get-sign arg locale)
+               (truncate (abs arg))
+               (unless (and (string= "" float-part) no-dp)
+                 (list (locale-decimal-point locale)
+                       (fix-float-string float-part *float-digits*))))
+    (values)))
 
 (defun print-number (number &key (stream *standard-output*)
                      no-ts no-dp (locale *locale*))
@@ -67,64 +37,49 @@
     (format-number stream number no-dp no-ts locale)
     number))
 
-(defun get-float-part (float locale use-int-sym)
-  (let ((size (if use-int-sym
-                  (locale-int-frac-digits locale)
-                  (locale-frac-digits locale)))
-        (len (length float)))
-    (cond ((>= len size)
-           (subseq float 0 size))
-          ((< len size)
-           (with-output-to-string (x)
-             (princ float x)
-             (dotimes (z (- size len))
-               (princ 0 x))))
-          (t float))))
-
-(defun get-descriptors (val locale)
-  (if (minusp val)
-      (values (locale-n-sep-by-space locale)
-              (= 1 (locale-n-cs-precedes locale))
-              (locale-n-sign-posn locale))
-      (values (locale-p-sep-by-space locale)
-              (= 1 (locale-p-cs-precedes locale))
-              (locale-p-sign-posn locale))))
 
-;; FIXME . Rounding  and float coercion.
+;; Money
+(defvar *default-round-mode* :round)
+  
+(defun round-money (float frac-digits &key (round-mode *default-round-mode*))
+  (let ((round-fn (ecase round-mode
+                    (:round #'fround)
+                    (:down #'ffloor)
+                    (:up #'fceiling))))
+    (let ((size (expt 10 frac-digits)))
+      (/ (funcall round-fn (* float size)) size))))
+
+(defun get-money-printer (minusp no-ts)
+  (if minusp
+      (if no-ts
+          :money-n-no-ts
+          :money-n-ts)
+      (if no-ts
+          :money-p-no-ts
+          :money-p-ts)))
+
 (defun format-money (stream arg use-int-sym no-ts &optional (locale *locale*))
-  (let ((locale (locale-des->locale locale)))
-    (multiple-value-bind (int-part float-part) 
-        (split-float (abs (float arg 1.0d0)))
-      (multiple-value-bind (sep-by-space prec spos) 
-          (get-descriptors arg locale)
-        (let* ((sign (get-sign arg locale))
-               (float-part (get-float-part float-part locale use-int-sym))
-               (point (locale-mon-decimal-point locale))
-               (sep (if no-ts "" (locale-mon-thousands-sep locale)))
-               (grouping (locale-mon-grouping locale))
-               (sym (if use-int-sym
-                        (locale-int-curr-symbol locale)
-                        (locale-currency-symbol locale)))
-               (sym-sep (if (zerop sep-by-space) "" " ")))
-          
-          (when (or* (= spos 0 1 3))
-            (princ (if (zerop spos) "(" sign) stream)
-            (when (= 2 sep-by-space)
-              (print #\Space stream)))
-          
-          (when prec 
-            (format stream "~A~A" sym sym-sep))
-          
-          (print-int stream "" int-part sep grouping)
-          (unless (or* (string= float-part "" "0"))
-            (princ point stream)
-            (princ float-part stream))
-          (unless prec
-            (format stream "~A~A" sym-sep (trim sym)))
-          (when (or* (= spos 0 2 4))
-            (when (= 2 sep-by-space)
-              (print #\Space stream))
-            (princ (if (zerop spos) ")" sign) stream)))))))
+  (let* ((locale (locale-des->locale locale))
+         (frac-digits (if use-int-sym
+                          (locale-int-frac-digits locale)
+                          (locale-frac-digits locale)))
+         (val-to-print (round-money (abs (coerce arg 'double-float))
+                                    frac-digits))
+         (float-part (float-part (coerce val-to-print 'float)))
+         (sym (if use-int-sym
+                  (locale-int-curr-symbol locale)
+                  (locale-currency-symbol locale)))
+         (prec (= 1 (locale-n-cs-precedes locale))))
+    (cl:format stream 
+               (getf (printers locale) 
+                     (get-money-printer (minusp arg) no-ts))
+               (if prec sym "")
+               (truncate (abs val-to-print))
+               (unless (zerop frac-digits)
+                 (list (locale-mon-decimal-point locale)
+                       (fix-float-string float-part frac-digits)))
+               (if prec "" (trim sym))))
+  (values))
 
 (defun print-money (num &key (stream *standard-output*) use-int-sym no-ts
                     (locale *locale*))
@@ -132,8 +87,7 @@
     (format-money stream num use-int-sym no-ts locale)
     num))
 
-
-;; Time and date printing.
+;; ;; Time and date printing.
 (defun get-time-fmt-string (locale show-date show-time)
   (cond ((and show-time show-date)
          (locale-d-t-fmt locale))
@@ -144,9 +98,10 @@
         (show-time (locale-t-fmt locale))
         (show-date (locale-d-fmt locale))))
 
-
 (defvar *time-formatters* (make-hash-table))
 (defmacro def-formatter (sym &body body)
+  "Creates a function with body which can be looked up using lookup-formatter
+   using the character SYM." 
   (let ((name (gensym (mkstr "FORMATTER-" sym))))
     `(flet ((,name (stream locale ut sec min hour date month year day 
                            daylight-p zone)
@@ -161,11 +116,13 @@
        it
        (locale-error "No format directive for char ~S." char)))
 
-(defun pad-val (val &optional (pad "0"))
-  (if (< val 10)
-      (format nil "~A~A" pad val)
-      val))
-
+(defun princ-pad-val (val stream &optional (pad "0"))
+  (declare (type stream stream) (optimize speed)
+           (type fixnum val))
+  (when (< val 10)
+    (princ pad stream))
+  (princ val stream))
+      
 (defun last-2-digits (val)
   (mod val 100))
 
@@ -181,27 +138,27 @@
 
 
 (def-formatter #\b
-    (format stream "~:(~A~)" (nth (1- month) (locale-abmon locale))))
-
+    (cl:format stream (cl:formatter "~:(~A~)") 
+               (nth (1- month) (locale-abmon locale))))
 
 (def-formatter #\B
-    (format stream "~:(~A~)"
+    (cl:format stream (cl:formatter "~:(~A~)")
             (nth (1- month) (locale-mon locale))))
 
 (def-formatter #\c
     (print-time-string "%a %b %d %T %Z %Y" stream ut locale))
 
 (def-formatter #\C
-    (princ (pad-val (truncate (/ year 100))) stream))
+    (princ-pad-val (truncate (/ year 100)) stream))
 
 (def-formatter #\d
-    (princ (pad-val date) stream))
+    (princ-pad-val date stream))
 
 (def-formatter #\D
     (print-time-string "%m/%d/%y" stream ut locale))
 
 (def-formatter #\e 
-    (princ (pad-val month " ") stream))
+    (princ-pad-val month stream " "))
 
 (def-formatter #\F
     (print-time-string "%Y-%m-%d" stream ut locale))
@@ -217,11 +174,10 @@
            stream))
 
 (def-formatter #\H
-    (princ (pad-val hour) stream))
+    (princ-pad-val hour stream))
 
 (def-formatter #\I
-    (princ (pad-val (if (> hour 12) (- hour 12) hour))
-           stream))
+    (princ-pad-val (if (> hour 12) (- hour 12) hour) stream))
 
 (defvar *mon-days* 
   '(31 28 31 30 31 30 31 31 30 31 30 31))
@@ -240,25 +196,23 @@
     (loop repeat (1- month) 
        for x in (if (leap-year-p year) *mon-days-leap* *mon-days*) do
          (incf total x))
-    (incf total date)
-    total))
+    (incf total date)))
 
 (def-formatter #\j 
     (princ (day-of-year date month year) stream))
 
 (def-formatter #\k
-    (princ (pad-val hour " ") stream))
+    (princ-pad-val hour stream " "))
 
 (def-formatter #\l
-    (princ (pad-val (if (> hour 12) (- hour 12) hour)
-                    " ")
-           stream))
+    (princ-pad-val (if (> hour 12) (- hour 12) hour) stream
+                   " "))
 
 (def-formatter #\m
-    (princ (pad-val month) stream))
+    (princ-pad-val month stream))
 
 (def-formatter #\M
-    (princ (pad-val min) stream))
+    (princ-pad-val min stream))
 
 (def-formatter #\n
     (princ #\Newline stream))
@@ -290,7 +244,7 @@
     (princ (- ut *1970-01-01*) stream))
 
 (def-formatter #\S
-    (princ (pad-val sec) stream))
+    (princ-pad-val sec stream))
 
 (def-formatter #\t
     (princ #\Tab stream))
@@ -327,7 +281,7 @@
     (print-time-string "%R:%S" stream ut locale))
 
 (def-formatter #\y
-    (princ (pad-val (last-2-digits year)) stream))
+    (princ-pad-val (last-2-digits year) stream))
 
 (def-formatter #\Y
     (princ year stream))
@@ -336,7 +290,7 @@
     (let ((d-zone (if daylight-p (1- zone) zone)))
       (multiple-value-bind (hr mn) (truncate (abs d-zone))
         (princ (if (minusp d-zone) #\+ #\-) stream)
-        (format stream "~2,'0D~2,'0D"
+        (cl:format stream (cl:formatter "~2,'0D~2,'0D")
                 hr (floor (* 60 mn))))))
 
 ;; FIXME should be printing SAST rather than +0200
@@ -349,9 +303,11 @@
   (let ((locale (locale-des->locale (or locale *locale*))))
     (print-time-string (or fmt (get-time-fmt-string locale 
                                                     show-date show-time))
-                       stream ut locale)))
+                       stream ut locale))
+  (values))
 
 (defun print-time-string (fmt-string stream ut locale)
+  (declare (optimize speed) (type simple-string fmt-string))
   (let ((values (multiple-value-list (decode-universal-time ut))))
     (loop for x across fmt-string 
        with perc = nil do
@@ -360,7 +316,8 @@
                     (progn (princ #\% stream) (setf perc nil))
                     (setf perc t)))
            (t (if perc
-                  (progn (apply (lookup-formatter x) stream locale ut values)
+                  (progn (apply (the function (lookup-formatter x))
+                                stream locale ut values)
                          (setf perc nil))
                   (princ x stream)))))))
 
@@ -372,6 +329,9 @@
       
 
 ;; Format
+(defmacro formatter (fmt-string)
+  (etypecase fmt-string 
+    (string `(cl:formatter ,(parse-fmt-string fmt-string)))))
 
 (defun format (stream fmt-cntrl &rest args)
   (apply #'cl:format stream
@@ -380,10 +340,18 @@
            (string (parse-fmt-string fmt-cntrl)))
          args))
 
+(defvar *scanner* (cl-ppcre:create-scanner "~[@v,:]*[m|u|n|M|U|N]"))
+
 (defun parse-fmt-string (string)
+  (if (cl-ppcre:scan *scanner* string)
+      (really-parse-fmt-string string)
+      string))
+
+(defun really-parse-fmt-string (string)
+  (declare (optimize speed) (type string string))
   (with-output-to-string (fmt-string)
     (loop for char across string 
-          with tilde = nil do
+       with tilde = nil do
          (case char
            ((#\@ #\v #\, #\:) (princ char fmt-string))
            (#\~ (princ char fmt-string)
@@ -391,9 +359,10 @@
                     (setf tilde nil)
                     (setf tilde t)))
            (t (if tilde
-                  (progn (setf tilde nil) (princ (get-replacement char) fmt-string))
+                  (progn (setf tilde nil) 
+                         (princ (get-replacement char) fmt-string))
                   (princ char fmt-string)))))))
-
+  
 (defvar *directive-replacements*
   '((#\M . "/cl-l10n:format-money/")
     (#\U . "/cl-l10n:format-time/")


Index: cl-l10n/tests.lisp
diff -u cl-l10n/tests.lisp:1.3 cl-l10n/tests.lisp:1.4
--- cl-l10n/tests.lisp:1.3	Fri Dec 17 11:06:43 2004
+++ cl-l10n/tests.lisp	Thu Dec 30 12:56:38 2004
@@ -1,7 +1,7 @@
 ;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
 ;; See the file LICENCE for licence information.
 (defpackage :cl-l10n-tests
-  (:shadowing-import-from :cl-l10n format)
+  (:shadowing-import-from :cl-l10n format formatter)
   (:use :cl :regression-test :cl-l10n))
 
 (in-package :cl-l10n-tests)
@@ -144,6 +144,19 @@
 
 (deftest format.3
     (format nil "~v:@m" "sv_SE" 1000)
+  "1000,00 SEK")
+
+;; formatter
+(deftest formatter.1
+    (format nil (formatter "~v:@U") "en_ZA" 3091103120)
+  "Sun 14 Dec 1997 17:45:20 +0200")
+
+(deftest formatter.2
+    (format nil (formatter "~v:n") "en_ZA" 1000)
+  "1,000")
+
+(deftest formatter.3
+    (format nil (formatter "~v:@m") "sv_SE" 1000)
   "1000,00 SEK")
 
 


Index: cl-l10n/utils.lisp
diff -u cl-l10n/utils.lisp:1.4 cl-l10n/utils.lisp:1.5
--- cl-l10n/utils.lisp:1.4	Tue Dec  7 10:21:55 2004
+++ cl-l10n/utils.lisp	Thu Dec 30 12:56:38 2004
@@ -84,8 +84,6 @@
                    (nreverse (cons source acc))))))
     (if list (rec list nil) nil)))
 
-
-
 (defun winner (test get seq)
   (if (null seq)
       nil
@@ -108,37 +106,22 @@
                     :initial-value (apply last-fn args))))
       #'identity))
 
+(defun float-part (float)
+  (if (zerop float)
+      ""
+      (multiple-value-call 'extract-float-part (flonum-to-digits float))))
 
-(defun get-first (fore aft)
-  (if (< fore 1)
-      "0"
-      (with-output-to-string (x)
-        (let ((length (length aft)))
-          (cond ((> fore length)
-                 (princ aft x)
-                 (dotimes (z (- fore length))
-                   (princ 0 x)))
-                (t (princ (subseq aft 0 fore)
-                          x)))))))
-
-(defun get-second (fore aft)
+(defun extract-float-part (dp-pos aft)
   (let ((length (length aft)))
-    (if (> fore length)
-        "0"
+    (if (> dp-pos length)
+        ""
         (with-output-to-string (x)
-          (cond ((minusp fore)
-                 (dotimes (z (abs fore))
+          (cond ((minusp dp-pos)
+                 (dotimes (z (abs dp-pos))
                    (princ 0 x))
                  (princ aft x))
-                (t (princ (subseq aft fore)
+                (t (princ (subseq aft dp-pos)
                           x)))))))
-
-(defun split-float (float)
-  (multiple-value-bind (fore aft) (flonum-to-digits float)
-    (values (parse-integer (get-first fore aft))
-            (let ((val (get-second fore aft)))
-              (if (string= val "") "0" val)))))
-
 
 ;; From sbcl sources (src/code/print.lisp)
 (defconstant single-float-min-e




More information about the Cl-l10n-cvs mailing list