X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fprint.lisp;h=a8ac834dbb42f969dd2678bd937aff63e34812c6;hb=25d3ce6406a74dca652ff4bb27f025986626958a;hp=8a3fd1daf0f8723943a03acaaf674e0f4f58e55e;hpb=86bb510cb4c955eb27cffbae65ad9e3e10bb6aa1;p=jscl.git diff --git a/src/print.lisp b/src/print.lisp index 8a3fd1d..a8ac834 100644 --- a/src/print.lisp +++ b/src/print.lisp @@ -16,18 +16,34 @@ ;; You should have received a copy of the GNU General Public License ;; along with JSCL. If not, see . +(/debug "loading print.lisp!") + ;;; Printer +(defun lisp-escape-string (string) + (let ((output "") + (index 0) + (size (length string))) + (while (< index size) + (let ((ch (char string index))) + (when (or (char= ch #\") (char= ch #\\)) + (setq output (concat output "\\"))) + (when (or (char= ch #\newline)) + (setq output (concat output "\\")) + (setq ch #\n)) + (setq output (concat output (string ch)))) + (incf index)) + (concat "\"" output "\""))) + ;;; Return T if the string S contains characters which need to be ;;; escaped to print the symbol name, NIL otherwise. -(defun escape-symbol-name-p (s &optional uppercase) +(defun escape-symbol-name-p (s) (let ((dots-only t)) (dotimes (i (length s)) (let ((ch (char s i))) (setf dots-only (and dots-only (char= ch #\.))) (when (or (terminalp ch) (char= ch #\:) - (and uppercase (not (char= ch (char (string-upcase (string ch)) 0)))) (char= ch #\\) (char= ch #\|)) (return-from escape-symbol-name-p t)))) @@ -80,13 +96,13 @@ #+nil (mapcar #'potential-number-p '("/" "/5" "+" "1+" "1-" "foo+" "ab.cd" "_" "^" "^/-")) -(defun escape-token-p (string &optional uppercase) +(defun escape-token-p (string) (or (potential-number-p string) - (escape-symbol-name-p string uppercase))) + (escape-symbol-name-p string))) ;;; Returns the token in a form that can be used for reading it back. -(defun escape-token (s &optional uppercase) - (if (escape-token-p s uppercase) +(defun escape-token (s) + (if (escape-token-p s) (let ((result "|")) (dotimes (i (length s)) (let ((ch (char s i))) @@ -98,64 +114,139 @@ s)) (defvar *print-escape* t) +(defvar *print-circle* nil) -(defun write-to-string (form) - (cond - ((null form) "NIL") - ((symbolp form) - (let ((name (symbol-name form)) - (package (symbol-package form))) - ;; Check if the symbol is accesible from the current package. It - ;; is true even if the symbol's home package is not the current - ;; package, because it could be inherited. - (if (eq form (find-symbol (symbol-name form))) - (escape-token (symbol-name form) (not (eq package *js-package*))) - ;; Symbol is not accesible from *PACKAGE*, so let us prefix - ;; the symbol with the optional package or uninterned mark. - (concat (cond - ((null package) "#") - ((eq package (find-package "KEYWORD")) "") - (t (escape-token (package-name package) t))) - ":" - (if (and package - (eq (second (multiple-value-list - (find-symbol name package))) - :internal)) - ":" - "") - (escape-token name (not (eq package *js-package*))))))) - ((integerp form) (integer-to-string form)) - ((floatp form) (float-to-string form)) - ((characterp form) - (concat "#\\" - (case form - (#\newline "newline") - (#\space "space") - (otherwise (string form))))) - ((stringp form) (if *print-escape* - (concat "\"" (escape-string form) "\"") - form)) - ((functionp form) - (let ((name (oget form "fname"))) - (if name - (concat "#") - (concat "#")))) - ((listp form) - (concat "(" - (join-trailing (mapcar #'write-to-string (butlast form)) " ") - (let ((last (last form))) - (if (null (cdr last)) - (write-to-string (car last)) - (concat (write-to-string (car last)) " . " (write-to-string (cdr last))))) - ")")) - ((arrayp form) - (concat "#" (if (zerop (length form)) - "()" - (write-to-string (vector-to-list form))))) - ((packagep form) - (concat "#")) - (t - (concat "#")))) +;;; FIXME: Please, rewrite this in a more organized way. +(defun write-to-string (form &optional known-objects object-ids) + (when (and (not known-objects) *print-circle*) + ;; To support *print-circle* some objects must be tracked for + ;; sharing: conses, arrays and apparently-uninterned symbols. + ;; These objects are placed in an array and a parallel array is + ;; used to mark if they're found multiple times by assining them + ;; an id starting from 1. + ;; + ;; After the tracking has been completed the printing phas can + ;; begin: if an object has an id > 0 then #= is prefixed and + ;; the id is changed to negative. If an object has an id < 0 then + ;; #<-n># is printed instead of the object. + ;; + ;; The processing is O(n^2) with n = number of tracked + ;; objects. Hopefully it will become good enough when the new + ;; compiler is available. + (setf known-objects (make-array 100)) + (setf object-ids (make-array 100)) + (let ((n 0) + (sz 100) + (count 0)) + (labels ((mark (x) + (let ((i (position x known-objects))) + (if (= i -1) + (progn + (when (= n sz) + (setf sz (* 2 sz)) + ;; KLUDGE: storage vectors are an internal + ;; object which the printer should not know + ;; about. Use standard vector with fill + ;; pointers instead. + (resize-storage-vector known-objects sz) + (resize-storage-vector object-ids sz)) + (aset known-objects (1- (incf n)) x) + t) + (unless (aref object-ids i) + (aset object-ids i (incf count)) + nil)))) + (visit (x) + (cond + ((and x (symbolp x) (null (symbol-package x))) + (mark x)) + ((consp x) + (when (mark x) + (visit (car x)) + (visit (cdr x)))) + ((vectorp x) + (when (mark x) + (dotimes (i (length x)) + (visit (aref x i)))))))) + (visit form)))) + (let ((prefix "")) + (when (and *print-circle* + (or (consp form) + (vectorp form) + (and form (symbolp form) (null (symbol-package form))))) + (let* ((ix (position form known-objects)) + (id (aref object-ids ix))) + (cond + ((and id (> id 0)) + (setf prefix (format nil "#~S=" id)) + (aset object-ids ix (- id))) + ((and id (< id 0)) + (return-from write-to-string (format nil "#~S#" (- id))))))) + (concat prefix + (cond + ((null form) "NIL") + ((symbolp form) + (let ((name (symbol-name form)) + (package (symbol-package form))) + ;; Check if the symbol is accesible from the current package. It + ;; is true even if the symbol's home package is not the current + ;; package, because it could be inherited. + (if (eq form (find-symbol (symbol-name form))) + (escape-token (symbol-name form)) + ;; Symbol is not accesible from *PACKAGE*, so let us prefix + ;; the symbol with the optional package or uninterned mark. + (concat (cond + ((null package) "#") + ((eq package (find-package "KEYWORD")) "") + (t (escape-token (package-name package)))) + ":" + (if (and package + (eq (second (multiple-value-list + (find-symbol name package))) + :internal)) + ":" + "") + (escape-token name))))) + ((integerp form) (integer-to-string form)) + ((floatp form) (float-to-string form)) + ((characterp form) + (concat "#\\" + (case form + (#\newline "newline") + (#\space "space") + (otherwise (string form))))) + ((stringp form) (if *print-escape* + (lisp-escape-string form) + form)) + ((functionp form) + (let ((name (oget form "fname"))) + (if name + (concat "#") + (concat "#")))) + ((listp form) + (concat "(" + (join-trailing (mapcar (lambda (x) + (write-to-string x known-objects object-ids)) + (butlast form)) " ") + (let ((last (last form))) + (if (null (cdr last)) + (write-to-string (car last) known-objects object-ids) + (concat (write-to-string (car last) known-objects object-ids) + " . " + (write-to-string (cdr last) known-objects object-ids)))) + ")")) + ((vectorp form) + (let ((result "#(") + (sep "")) + (dotimes (i (length form)) + (setf result (concat result sep + (write-to-string (aref form i) + known-objects + object-ids))) + (setf sep " ")) + (concat result ")"))) + ((packagep form) + (concat "#")) + (t "#"))))) (defun prin1-to-string (form) (let ((*print-escape* t)) @@ -192,10 +283,12 @@ (concatf res "~")) ((char= next #\%) (concatf res *newline*)) + ((char= next #\*) + (pop arguments)) (t (concatf res (format-special next (car arguments))) (pop arguments)))) - (setq res (concat res (char-to-string c)))) + (setq res (concat res (string c)))) (incf i))) (if destination (progn @@ -204,6 +297,6 @@ res))) (defun format-special (chr arg) - (case chr + (case (char-upcase chr) (#\S (prin1-to-string arg)) - (#\a (princ-to-string arg)))) + (#\A (princ-to-string arg))))