310754eb50cbd600f02330da40632933a7c42098
[jscl.git] / src / print.lisp
1 ;;; print.lisp ---
2
3 ;; Copyright (C) 2012, 2013 David Vazquez
4 ;; Copyright (C) 2012 Raimon Grau
5
6 ;; JSCL is free software: you can redistribute it and/or
7 ;; modify it under the terms of the GNU General Public License as
8 ;; published by the Free Software Foundation, either version 3 of the
9 ;; License, or (at your option) any later version.
10 ;;
11 ;; JSCL is distributed in the hope that it will be useful, but
12 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 ;; General Public License for more details.
15 ;;
16 ;; You should have received a copy of the GNU General Public License
17 ;; along with JSCL.  If not, see <http://www.gnu.org/licenses/>.
18
19 ;;; Printer
20
21 ;;; Return T if the string S contains characters which need to be
22 ;;; escaped to print the symbol name, NIL otherwise.
23 (defun escape-symbol-name-p (s &optional uppercase)
24   (let ((dots-only t))
25     (dotimes (i (length s))
26       (let ((ch (char s i)))
27         (setf dots-only (and dots-only (char= ch #\.)))
28         (when (or (terminalp ch)
29                   (char= ch #\:)
30                   (and uppercase (not (char= ch (char (string-upcase (string ch)) 0))))
31                   (char= ch #\\)
32                   (char= ch #\|))
33           (return-from escape-symbol-name-p t))))
34     dots-only))
35
36 (defun potential-number-p (s)
37   (let ((i 0)
38         (n (length s))
39         (ch nil))
40     (flet ((next ()
41                  (setf ch (and (< i n) (char s (1- (incf i)))))))
42       (next)
43       (cond
44        ((null ch) (return-from potential-number-p))
45        ((digit-char-p ch))
46        ((char= ch #\.))
47        ((char= ch #\+) (next))
48        ((char= ch #\-) (next))
49        (t (return-from potential-number-p)))
50       (when ch
51         (while (and ch (digit-char-p ch)) (next))
52         (when (null ch)
53           (return-from potential-number-p t)))
54       (when (char= ch #\.)
55         (next)
56         (when ch
57           (while (and ch (digit-char-p ch)) (next))))
58       (when (or (char= ch #\E) (char= ch #\e)
59                 (char= ch #\D) (char= ch #\d)
60                 (char= ch #\F) (char= ch #\f)
61                 (char= ch #\L) (char= ch #\l))
62         (next)
63         (cond
64          ((null ch) (return-from potential-number-p))
65          ((digit-char-p ch))
66          ((char= ch #\+) (next))
67          ((char= ch #\-) (next))
68          (t (return-from potential-number-p)))
69         (unless (and ch (digit-char-p ch))
70           (return-from potential-number-p))
71         (while (and ch (digit-char-p ch)) (next)))
72       (null ch))))
73
74 (defun escape-token-p (string &optional uppercase)
75   (or (potential-number-p string)
76       (escape-symbol-name-p string uppercase)))
77
78 (defun escape-token (s &optional uppercase)
79   (if (escape-token-p s uppercase)
80       (let ((result "|"))
81         (dotimes (i (length s))
82           (let ((ch (char s i)))
83             (when (or (char= ch #\|)
84                       (char= ch #\\))
85               (setf result (concat result "\\")))
86             (setf result (concat result (string ch)))))
87         (concat result "|"))
88       s))
89
90
91 (defvar *print-escape* t)
92
93 (defun write-to-string (form)
94   (cond
95     ((null form) "NIL")
96     ((symbolp form)
97      (multiple-value-bind (found-symbol status)
98          (find-symbol (symbol-name form))
99        (if (eq found-symbol form)
100            (escape-token (symbol-name form) (not (eq *package* *js-package*)))
101            (let ((package (symbol-package form))
102                  (name (symbol-name form)))
103              (concat (cond
104                        ((null package) "#")
105                        ((eq package (find-package "KEYWORD")) "")
106                        (t (escape-token (package-name package))))
107                      ":"
108                      (if (or (null package) (eq :external (second (multiple-value-list (find-symbol name package)))))
109                          "" ":")
110                      (escape-token name package))))))
111     ((integerp form) (integer-to-string form))
112     ((floatp form) (float-to-string form))
113     ((characterp form)
114      (concat "#\\"
115              (case form
116                (#\newline "newline")
117                (#\space "space")
118                (otherwise (string form)))))
119     ((stringp form) (if *print-escape*
120                         (concat "\"" (escape-string form) "\"")
121                         form))
122     ((functionp form)
123      (let ((name (oget form "fname")))
124        (if name
125            (concat "#<FUNCTION " name ">")
126            (concat "#<FUNCTION>"))))
127     ((listp form)
128      (concat "("
129              (join-trailing (mapcar #'write-to-string (butlast form)) " ")
130              (let ((last (last form)))
131                (if (null (cdr last))
132                    (write-to-string (car last))
133                    (concat (write-to-string (car last)) " . " (write-to-string (cdr last)))))
134              ")"))
135     ((arrayp form)
136      (concat "#" (if (zerop (length form))
137                      "()"
138                      (write-to-string (vector-to-list form)))))
139     ((packagep form)
140      (concat "#<PACKAGE " (package-name form) ">"))
141     (t
142      (concat "#<javascript object>"))))
143
144 (defun prin1-to-string (form)
145   (let ((*print-escape* t))
146     (write-to-string form)))
147
148 (defun princ-to-string (form)
149   (let ((*print-escape* nil))
150     (write-to-string form)))
151
152 (defun write-line (x)
153   (write-string x)
154   (write-string *newline*)
155   x)
156
157 (defun warn (string)
158   (write-string "WARNING: ")
159   (write-line string))
160
161 (defun print (x)
162   (write-line (prin1-to-string x))
163   x)
164
165 (defun format (destination fmt &rest args)
166   (let ((len (length fmt))
167         (i 0)
168         (res "")
169         (arguments args))
170     (while (< i len)
171       (let ((c (char fmt i)))
172         (if (char= c #\~)
173             (let ((next (char fmt (incf i))))
174               (cond
175                ((char= next #\~)
176                 (concatf res "~"))
177                ((char= next #\%)
178                 (concatf res *newline*))
179                (t
180                 (concatf res (format-special next (car arguments)))
181                 (pop arguments))))
182             (setq res (concat res (char-to-string c))))
183         (incf i)))
184     (if destination
185         (progn
186           (write-string res)
187           nil)
188         res)))
189
190 (defun format-special (chr arg)
191   (case chr
192     (#\S (prin1-to-string arg))
193     (#\a (princ-to-string arg))))