Make clos objects printable in lisp
Asked Answered
P

1

11

If you want to make CLOS objects in common lisp printable (print readably), how do you go about doing this without using anything but print and read.

Profane answered 21/6, 2010 at 16:41 Comment(0)
P
5

There are two parts to doing this, at least in my solution, however you will need this function (thanks to the guys at cl-prevalence for this (warn LLGPL)

(defun get-slots (object)
  ;; thanks to cl-prevalence
  #+openmcl
  (mapcar #'ccl:slot-definition-name
      (#-openmcl-native-threads ccl:class-instance-slots
       #+openmcl-native-threads ccl:class-slots
       (class-of object)))
  #+cmu
  (mapcar #'pcl:slot-definition-name (pcl:class-slots (class-of object)))
  #+sbcl
  (mapcar #'sb-pcl:slot-definition-name (sb-pcl:class-slots (class-of object)))
  #+lispworks
  (mapcar #'hcl:slot-definition-name (hcl:class-slots (class-of object)))
  #+allegro
  (mapcar #'mop:slot-definition-name (mop:class-slots (class-of object)))
  #+sbcl
  (mapcar #'sb-mop:slot-definition-name (sb-mop:class-slots (class-of object)))
  #+clisp
  (mapcar #'clos:slot-definition-name (clos:class-slots (class-of object)))
  #-(or openmcl cmu lispworks allegro sbcl clisp)
  (error "not yet implemented"))

Then, for reading you will need to run this piece of code, which sets up 1/2 of the syntax which is { type-of-object ((slot-name . slot-value) (slot-name . slot-value) ...)

(set-macro-character 
     #\{
     #'(lambda (str char)
     (declare (ignore char))
     (let ((list (read-delimited-list #\} str t)))
       (let ((type (first list))
         (list (second list)))
         (let ((class (allocate-instance (find-class type))))
           (loop for i in list do
            (setf (slot-value class (car i)) (cdr i)))
           class)))))

For printing, use

(defmethod print-object ((object standard-object) stream)
  (format stream "{ ~s ~s}" (type-of object)
      (loop for i in (get-slots object)
    collect (cons i (slot-value object i)))))

A *print-readably* is highly recommended when using all these methods. Also, note that circular relationships are untested

Profane answered 21/6, 2010 at 17:3 Comment(8)
you should have a balanced external representation. Currently you have an opening character, but no corresponding closing character. For reading then see for example READ-DELIMITED-LIST. See the example at lispworks.com/documentation/lw50/CLHS/Body/f_rd_del.htm .Washbasin
maybe you're right, but it serves well for my purposes as there must be exactly two objects after the {Profane
yes, but it will confuse all editors. s-expressions should be balanced. It's a simple change anyway.Washbasin
format is meant to be dumped and then rammed back in with no editingProfane
Yeah, but you publish an ugly hack on Stackoverflow and others may copy it. If you keep it just to your software, dealing with it is only your problem. Publishing substandard code on Stackoverflow is not a good idea. Your code is simply not a good example. S-Expression based formats are balanced and Common Lisp already gives an example with the external format for structures.Washbasin
Great. You can simplify the form a bit and get rid of two lets by replacing them with one DESTRUCTURING-BIND: (destructuring-bind (type . list) (read-delimited-list ... Is the variable CLASS not better named OBJECT or INSTANCE? See also the above mentioned example in the CLHS. You might want to set the syntax attribute for the } character.Washbasin
something in my codebase snapped when I tried that. Thanks, though. P.S., the running incantation of the code is at motm.svn.sourceforge.net/viewvc/motm/trunk/src/… and also while you're there feel free to check out the rest of the game (motm.sourceforge.net)Profane
Much better to use closer-mopLinseed

© 2022 - 2024 — McMap. All rights reserved.