From: Jacek Generowicz
Subject: Literals for CLOS objects.
Date: 
Message-ID: <g03d6en85y.fsf@scumbag.ecs.soton.ac.uk>
I am trying to create a literal representation for instances of a CLOS
class. I have managed to make a couple of read macros:

(set-macro-character #\] (get-macro-character #\)))

(set-dispatch-macro-character #\# #\[
  #'(lambda (stream char1 char2)
      (apply #'make-grid-reference (read-delimited-list #\] stream
t))))

(set-dispatch-macro-character #\# #\g
  #'(lambda (stream char1 char2)
      (apply #'make-grid-reference (read stream t nil t))))

which seem to work:

* #[su 123 456]
#<GRID-REFERENCE {4810C445}>

* #g(su 123 456)
#<GRID-REFERENCE {4813077D}>

Now that I have made the objects readable, can I persuade print to
print them readably ?

I also don't really understand why the list in the #g version doesn't
need to be quoted to avoid interpretation of the su as a function
name.

Jacek

From: Kent M Pitman
Subject: Re: Literals for CLOS objects.
Date: 
Message-ID: <sfw3d6eepse.fsf@world.std.com>
Jacek Generowicz <···@ecs.soton.ac.uk> writes:

> 
> I am trying to create a literal representation for instances of a CLOS
> class. I have managed to make a couple of read macros:
> 
> (set-macro-character #\] (get-macro-character #\)))

This isn't portable.  You aren't guaranteed that ")" will be implemented
as a readmacro, so you may be getting NIL back from GET-MACRO-CHARACTER.
 
> (set-dispatch-macro-character #\# #\[
>   #'(lambda (stream char1 char2)
>       (apply #'make-grid-reference (read-delimited-list #\] stream
> t))))
> 
> (set-dispatch-macro-character #\# #\g
>   #'(lambda (stream char1 char2)
>       (apply #'make-grid-reference (read stream t nil t))))
> 
> which seem to work:
> 
> * #[su 123 456]
> #<GRID-REFERENCE {4810C445}>
> 
> * #g(su 123 456)
> #<GRID-REFERENCE {4813077D}>
> 
> Now that I have made the objects readable, can I persuade print to
> print them readably ?

Something like this should work:

(defmethod print-object ((gref grid-reference) stream)
  (with-slots (name xpos ypos) gref
    (if (or *print-escape* *print-readably*)
        (format stream "[~A ~D ~D]" name xpos ypos)
        (format stream "Grid ~S @ (~D,~D)" name xpos ypos))))	

> I also don't really understand why the list in the #g version doesn't
> need to be quoted to avoid interpretation of the su as a function
> name.

In #g's case, the list is the result of READ.  READ does not evaluate its
arg.  That would need quoting if you simply returned it from the readmacro
handler, since then it would get to EVAL.  However, you instead take the
unevaluated form and pass it (spread) into make-grid-reference, which 
yields an object of type grid-reference.  Such objects are self-evaluating.
(Only symbols and lists are treated specially  by the evaluator; all other
datatypes self-quote.)  
From: Jacek Generowicz
Subject: Re: Literals for CLOS objects.
Date: 
Message-ID: <g0pu9ilh69.fsf@scumbag.ecs.soton.ac.uk>
Kent M Pitman <······@world.std.com> writes:

> > (set-macro-character #\] (get-macro-character #\)))
> 
> This isn't portable.  You aren't guaranteed that ")" will be implemented
> as a readmacro, so you may be getting NIL back from GET-MACRO-CHARACTER.

Is there a way to make it safer ?

> Something like this should work:
> 
> (defmethod print-object ((gref grid-reference) stream)
>   (with-slots (name xpos ypos) gref
>     (if (or *print-escape* *print-readably*)
>         (format stream "[~A ~D ~D]" name xpos ypos)
>         (format stream "Grid ~S @ (~D,~D)" name xpos ypos))))	

Thanks.

Will *print-escape* or *print-readably* be changed by anything other
than explicit re-bindings by me ?

> > I also don't really understand why the list in the #g version doesn't
> > need to be quoted to avoid interpretation of the su as a function
> > name.
> 
> In #g's case, the list is the result of READ.  READ does not evaluate its
> arg.  That would need quoting if you simply returned it from the readmacro
> handler, since then it would get to EVAL.  However, you instead take the
> unevaluated form and pass it (spread) into make-grid-reference, which 
> yields an object of type grid-reference.  Such objects are self-evaluating.
> (Only symbols and lists are treated specially  by the evaluator; all other
> datatypes self-quote.)  

Oh dear, I forgot about the eval part of read-eval-print.

Apologies.

Jacek
From: Kent M Pitman
Subject: Re: Literals for CLOS objects.
Date: 
Message-ID: <sfwheuupht5.fsf@world.std.com>
Jacek Generowicz <···@ecs.soton.ac.uk> writes:

> Kent M Pitman <······@world.std.com> writes:
> 
> > > (set-macro-character #\] (get-macro-character #\)))
> > 
> > This isn't portable.  You aren't guaranteed that ")" will be implemented
> > as a readmacro, so you may be getting NIL back from GET-MACRO-CHARACTER.
> 
> Is there a way to make it safer ?

What I always do is 

 (defvar *close-bracket* (list '*close-bracket*))
 (defun read-close-bracket (stream char)
   *close-bracket*)
 (set-macro-character #\] 'read-close-bracket)

Then I write something that does something similar to read-delimited-list,
but looks for *close-bracket* to be the result of the call to READ.

> > Something like this should work:
> > 
> > (defmethod print-object ((gref grid-reference) stream)
> >   (with-slots (name xpos ypos) gref
> >     (if (or *print-escape* *print-readably*)
> >         (format stream "[~A ~D ~D]" name xpos ypos)
> >         (format stream "Grid ~S @ (~D,~D)" name xpos ypos))))	
> 
> Thanks.
> 
> Will *print-escape* or *print-readably* be changed by anything other
> than explicit re-bindings by me ?

PRINC binds *PRINT-ESCAPE* to NIL and PRIN1 binds it to T.
 
WRITE will bind any of the printer variables if you ask it to.

~A calls PRINC.  ~S calls PRIN1.

WITH-STANDARD-IO-SYNTAX binds *PRINT-ESCAPE* and *PRINT-READABLY* to T.
I don't think anything else binds *PRINT-READABLY* unconditionally to T.

In general, though, don't think in terms of "what does it", but rather in
terms of "what does it mean".  *PRINT-ESCAPE* means "I am doing a PRIN1" if
T, and "I amd doing a PRINC" if NIL.  *PRINT-READABLY* implies *PRINT-ESCAPE*
is T (even if *PRINT-ESCAPE* is NIL) and adds the additional meaning "I'd
rather get an error now while printing than later when re-reading if this
is not going to re-read right in an environment with the same reader
settings as I have active now".

> > > I also don't really understand why the list in the #g version doesn't
> > > need to be quoted to avoid interpretation of the su as a function
> > > name.
> > 
> > In #g's case, the list is the result of READ.  READ does not evaluate its
> > arg.  That would need quoting if you simply returned it from the readmacro
> > handler, since then it would get to EVAL.  However, you instead take the
> > unevaluated form and pass it (spread) into make-grid-reference, which 
> > yields an object of type grid-reference.  Such objects are self-evaluating.
> > (Only symbols and lists are treated specially  by the evaluator; all other
> > datatypes self-quote.)  
> 
> Oh dear, I forgot about the eval part of read-eval-print.
>
> Apologies.

>* chuckle *<
From: Raymond Wiker
Subject: Re: Literals for CLOS objects.
Date: 
Message-ID: <86ae0mkkf9.fsf@raw.grenland.fast.no>
Jacek Generowicz <···@ecs.soton.ac.uk> writes:

> Will *print-escape* or *print-readably* be changed by anything other
> than explicit re-bindings by me ?

        format will bind these, according to what directive you're
using (check the meanings of ~A, ~S and ~W). Also, check the
relationship between write, print, pprint, princ and prin1 (handily
summarised under one entry in the HyperSpec).

-- 
Raymond Wiker
·············@fast.no
From: Jacek Generowicz
Subject: Re: Literals for CLOS objects.
Date: 
Message-ID: <g0itf23947.fsf@scumbag.ecs.soton.ac.uk>
I can't get it to work in clisp.

A trivial example:

(defclass literal ()
  ((one :initarg :one :accessor one)
   (two :initarg :two :accessor two)))

(defun make-literal (one two)
  (make-instance 'literal :one one :two two))

(set-dispatch-macro-character #\# #\g
  #'(lambda (stream the-# the-g)
      (apply #'make-literal (read stream t nil t))))

(defmethod print-object ((lit literal) stream)
  (with-slots (one two) lit
    (format stream "#g(~A ~A)" one two)))

CMUCL:

  * #g(123 123)
  
  In: LAMBDA (STREAM |THE-#| THE-G)
    #'(LAMBDA (STREAM |THE-#| THE-G) (APPLY #'MAKE-LITERAL (READ STREAM
  T NIL T)))
  Note: Variable |THE-#| defined but never used.
  Note: Variable THE-G defined but never used.
  
  #g(123 123)
  
CLisp:
  
  [5]> #g(123 123)
  
  *** - EVAL: illegal form #g(123 123)

What am I doing wrong ?
From: Pierre R. Mai
Subject: Re: Literals for CLOS objects.
Date: 
Message-ID: <87vgj2lh6f.fsf@orion.bln.pmsf.de>
Jacek Generowicz <···@ecs.soton.ac.uk> writes:

> (defclass literal ()
>   ((one :initarg :one :accessor one)
>    (two :initarg :two :accessor two)))
> 
> (defun make-literal (one two)
>   (make-instance 'literal :one one :two two))
> 
> (set-dispatch-macro-character #\# #\g
>   #'(lambda (stream the-# the-g)
>       (apply #'make-literal (read stream t nil t))))

[...]

> CLisp:
>   
>   [5]> #g(123 123)
>   
>   *** - EVAL: illegal form #g(123 123)
> 
> What am I doing wrong ?

Nothing it seems.  This might be a bug in some earlier version of
CLISP, but it works for me in CLISP 2.27, which is the current release
version I think.

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Kent M Pitman
Subject: Re: Literals for CLOS objects.
Date: 
Message-ID: <sfwk7zijp3t.fsf@world.std.com>
Jacek Generowicz <···@ecs.soton.ac.uk> writes:

> I can't get it to work in clisp.
> 
> A trivial example:
> [...]
> (set-dispatch-macro-character #\# #\g
>   #'(lambda (stream the-# the-g)
>       (apply #'make-literal (read stream t nil t))))
> [...]
 
I agree with Pierre that the code you posted looks ok.

Of course, in the part above, you should add:
 (declare (ignore the-# the-g))
to avoid the warning about unused variables.
From: Jacek Generowicz
Subject: Re: Literals for CLOS objects.
Date: 
Message-ID: <g03d65wiw9.fsf@scumbag.ecs.soton.ac.uk>
Kent M Pitman <······@world.std.com> writes:

> Jacek Generowicz <···@ecs.soton.ac.uk> writes:
> 
> > I can't get it to work in clisp.
> > 
> > A trivial example:
> > [...]
> > (set-dispatch-macro-character #\# #\g
> >   #'(lambda (stream the-# the-g)
> >       (apply #'make-literal (read stream t nil t))))
> > [...]
>  
> I agree with Pierre that the code you posted looks ok.

Yes, I installed the latest release of Clisp and it works, thanks Pierre.

> Of course, in the part above, you should add:
>  (declare (ignore the-# the-g))
> to avoid the warning about unused variables.

Grand, thanks.