From: D Herring
Subject: REPL *read-suppress* conundrum
Date: 
Message-ID: <49436195$0$17070$6e1ede2f@read.cnntp.org>
If you're at a REPL, and type (setf *read-suppress* t), is there any 
way to recover?  It this where lisp systems go to die?

The practical interest in this question arises from the following 
code, modelled after CPP's #if/#else/#endif.

#?(read-suppress-if t) ; sets *read-suppress* t, pushes old value
; stuff which never sees the light of day
#?(read-suppress-else) ; ~toggles *read-suppress*
; stuff which gets read
#?(read-suppress-endif) ; pops *read-suppress*

If someone didn't match their clauses, then (load "file") could be 
disastrous...  Is there any way to automatically restore 
*read-suppress* at the end of a file?  I don't see a portable way to 
hook into LOAD.

Thanks,
Daniel

From: budden
Subject: Re: REPL *read-suppress* conundrum
Date: 
Message-ID: <aee0a051-818b-4354-99eb-0efcd273c866@j35g2000yqh.googlegroups.com>
Hi Daniel,

> If someone didn't match their clauses, then (load "file") could be
disastrous...
A. (defun careful-load (&rest args) (apply load args) (check-read-
suppress))
B. You might alter *features*, this is more high-level. E.g.:

#.(ifdef-area my-condition) ; pushes either :ifdef or :else to
*features*, depending on my-condition
#+ifdef (
(defun foo () ) ; note it starts from first column, to cheat emacs
)
#+else (
  ; disabled code
)
#.(end-ifdef-area) ; checks that either :ifdef or :else is on top of
*features* and then pops

This way, you can still err if you dont set #.(pop-ifdef), but, at
least, you won't kill the
interpreter. Maybe even better would be to have

#.(ifdef my-condition)
if-code ...
:else
else-code
:endif

If you forget your :endif, it will err at the end of file. I guess
then ifdefs can even be made nestable.
I got some code which might fit for that if modified, but I got no
time to check that. Not sure this is extremely
easy to do.

--------
Now I'm $5/hour lisp freelancer.
From: budden
Subject: Re: REPL *read-suppress* conundrum
Date: 
Message-ID: <4eb54f53-068d-4e84-82b0-02c9c3f0668c@q30g2000vbn.googlegroups.com>
Sorry, I forgot progn in #+ifdef and #+else
From: Jens Teich
Subject: Re: REPL *read-suppress* conundrum
Date: 
Message-ID: <m2myf0ullo.fsf@jensteich.de>
This question comes very often, google search 'learn lisp' in this
group.

By the way: the book you definitely need is called Practical Common Lisp
by Peter Seibel. You can start reading it online.

Jens
From: Jens Teich
Subject: Re: REPL *read-suppress* conundrum
Date: 
Message-ID: <m2iqpouljc.fsf@jensteich.de>
Jens Teich <········@jensteich.de> writes:

> This question comes very often, google search 'learn lisp' in this
> group.
>
> By the way: the book you definitely need is called Practical Common Lisp
> by Peter Seibel. You can start reading it online.
>
> Jens

sorry wrong thread
From: D Herring
Subject: Re: REPL *read-suppress* conundrum
Date: 
Message-ID: <4943dd44$0$17070$6e1ede2f@read.cnntp.org>
budden wrote:
>> If someone didn't match their clauses, then (load "file") could be
> disastrous...
> A. (defun careful-load (&rest args) (apply load args) (check-read-
> suppress))

I want this to be transparent to calling code.  Thus we can't expect 
the user to use safe-load or safe-compile-file or...

> B. You might alter *features*, this is more high-level. E.g.:
> 
> #.(ifdef-area my-condition) ; pushes either :ifdef or :else to
> *features*, depending on my-condition
> #+ifdef (
> (defun foo () ) ; note it starts from first column, to cheat emacs
> )
> #+else (
>   ; disabled code
> )
> #.(end-ifdef-area) ; checks that either :ifdef or :else is on top of
> *features* and then pops
> 
> This way, you can still err if you dont set #.(pop-ifdef), but, at
> least, you won't kill the
> interpreter. Maybe even better would be to have
> 
> #.(ifdef my-condition)
> if-code ...
> :else
> else-code
> :endif

Unfortunately, these aren't transparent either.  Since the read macro 
is now reading the nested forms, it has to return them as a list, 
whereas an outer reader may have seen them as individual elements... 
For DEFUNs and the like, this may be solved with a PROGN; but for 
actual data...

Either way, I had already coded up nested forms using the following 
syntax (and no external state).

#?(read-if test true-form false-form)

Thanks for looking at this problem,
Daniel
From: D Herring
Subject: Re: REPL *read-suppress* conundrum
Date: 
Message-ID: <4943e443$0$17067$6e1ede2f@read.cnntp.org>
D Herring wrote:
> budden wrote:
>>> If someone didn't match their clauses, then (load "file") could be
>> disastrous...
...
> Either way, I had already coded up nested forms using the following 
> syntax (and no external state).
> 
> #?(read-if test true-form false-form)

Never mind.  The above form is the "best possible".  Even if I could 
safely set *read-suppress*, the following still wouldn't work as desired.

(list
#?(read-suppress-if t)
   1
   2
#?(read-suppress-else)
   3
   4
#?(read-suppress-endif)
   5)

desired: '(3 4 5)
actual: '(nil nil 3 4 5) ; The reader returns nil instead of (values).

The only way to cleanly elide the undesired forms is for the read 
macro to return them as a list or (values).  However this adds an 
implicit list around them.

Implemented this way, the above would return '((3 4) 5).  Still not 
desired.

It is better to simply force the user to use an explicit list notation...

So the above might be written correctly as
`(,@#?(read-if nil
                '(1 2)
                '(3 4))
   5)

- Daniel
From: Pascal J. Bourguignon
Subject: Re: REPL *read-suppress* conundrum
Date: 
Message-ID: <87skos3s0v.fsf@informatimago.com>
D Herring <········@at.tentpost.dot.com> writes:

> D Herring wrote:
>> budden wrote:
>>>> If someone didn't match their clauses, then (load "file") could be
>>> disastrous...
> ...
>> Either way, I had already coded up nested forms using the following
>> syntax (and no external state).
>>
>> #?(read-if test true-form false-form)
>
> Never mind.  The above form is the "best possible".  Even if I could
> safely set *read-suppress*, the following still wouldn't work as
> desired.
>
> (list
> #?(read-suppress-if t)
>   1
>   2
> #?(read-suppress-else)
>   3
>   4
> #?(read-suppress-endif)
>   5)
>
> desired: '(3 4 5)
> actual: '(nil nil 3 4 5) ; The reader returns nil instead of (values).
>
> The only way to cleanly elide the undesired forms is for the read
> macro to return them as a list or (values).  However this adds an
> implicit list around them.
>
> Implemented this way, the above would return '((3 4) 5).  Still not
> desired.
>
> It is better to simply force the user to use an explicit list notation...
>
> So the above might be written correctly as
> `(,@#?(read-if nil
>                '(1 2)
>                '(3 4))
>   5)

Well, if you don't insist on using *read-suppress*, and you just want
some custom syntax, let's say:

(list
#if 0
  1
  2
#else
  3 
  4
#endif
  5)

then I would suggest to write a reader macros bound to #\# #\i and #\#
#\e (and even, the second one is just to offer nice error messages to
the user), implementing the the C preprocessor directive parsing.  But
to do it you will still need to keep state across calls to read, so I'd
rather not do that.

I guess what you really want is just to use cpp:

(defun loadh (file)
  (let ((preprocessed (merge-pathnames ".lisp" file nil)))
      (when (zerop (ext:shell
                     (format nil "cpp -DALTERNATIVE_PROTON -DOPTION_DEMOCLES=0 ~S ~S"
                                 (merge-pathnames ".lisph" file nil)
                                 preprocesed)))
      (load preprocessed))))


You will even get macros for the same price!
 


-- 
__Pascal Bourguignon__
From: D Herring
Subject: Re: REPL *read-suppress* conundrum
Date: 
Message-ID: <4944093b$0$17070$6e1ede2f@read.cnntp.org>
Pascal J. Bourguignon wrote:
> D Herring <········@at.tentpost.dot.com> writes:
> 
>> D Herring wrote:
>>> budden wrote:
>>>>> If someone didn't match their clauses, then (load "file") could be
>>>> disastrous...
>> ...
>>> Either way, I had already coded up nested forms using the following
>>> syntax (and no external state).
>>>
>>> #?(read-if test true-form false-form)
>> Never mind.  The above form is the "best possible".  Even if I could
>> safely set *read-suppress*, the following still wouldn't work as
>> desired.

In fact, it isn't.  This can be done entirely safely and transparently.

> Well, if you don't insist on using *read-suppress*, and you just want
> some custom syntax, let's say:
> 
> (list
> #if 0
>   1
>   2
> #else
>   3 
>   4
> #endif
>   5)
> 
> then I would suggest to write a reader macros bound to #\# #\i and #\#
> #\e (and even, the second one is just to offer nice error messages to
> the user), implementing the the C preprocessor directive parsing.  But
> to do it you will still need to keep state across calls to read, so I'd
> rather not do that.
> 
> I guess what you really want is just to use cpp:
...
> You will even get macros for the same price!


Thanks.  I finally figured out how to do what I want (without 
resorting to CPP); then when I came here to post, I found your replies.

My current code looks roughly like the following

(list
#?(read-suppress-if t)
; (let ((*read-suppress* t))
;    (do () (nil) (read)))
   1
   2
#?(read-suppress-else)
; (if suppressing
;     (signal 'stop-suppressing)
;     (let ((*read-suppress* t))
;        (do () (nil) (read))))
   3
   4
#?(read-suppress-endif)
; (when suppressing (signal 'stop-suppressing))
   5)

- Daniel
From: Pascal J. Bourguignon
Subject: Re: REPL *read-suppress* conundrum
Date: 
Message-ID: <87wse43tse.fsf@informatimago.com>
D Herring <········@at.tentpost.dot.com> writes:

> If you're at a REPL, and type (setf *read-suppress* t), is there any
> way to recover?  It this where lisp systems go to die?

Only lisp systems that are careless.  clisp doesn't uses the user's
*read-suppress* to read from the REPL:

[18]> (setf *read-suppress* t)
T
[19]> (read-from-string ":ahah")
NIL ;
5
[20]>  (setf *read-suppress* nil)
NIL
[21]> (read-from-string ":ahah")
:AHAH ;
5
[22]> 


> The practical interest in this question arises from the following
> code, modelled after CPP's #if/#else/#endif.
>
> #?(read-suppress-if t) ; sets *read-suppress* t, pushes old value
> ; stuff which never sees the light of day
> #?(read-suppress-else) ; ~toggles *read-suppress*
> ; stuff which gets read
> #?(read-suppress-endif) ; pops *read-suppress*

What about:

#|         ; sets *read-suppress* t, pushes old value
           ; stuff which never sees the light of day
|#         ; ~toggles *read-suppress*
           ; stuff which gets read
#-(and)pop ; pops *read-suppress*


Note that *read-suppress* still parses the text for strings,
balanced parentheses, etc.


[27]> (setf *read-suppress* t)
T
[28]> (read-from-string "( ab c")

*** - READ: input stream #<INPUT STRING-INPUT-STREAM> ends within an object
...
[30]> (read-from-string "( |ab c)")

*** - READ: input stream #<INPUT STRING-INPUT-STREAM> ends within a
token after multiple escape character
...

[32]> (read-from-string "( \"ab c)")
*** - READ: input stream #<INPUT STRING-INPUT-STREAM> ends within a
      string
...


So you don't win anything playing with *read-suppress* compared to
#|comments|#.


> If someone didn't match their clauses, then (load "file") could be
> disastrous...  Is there any way to automatically restore
> *read-suppress* at the end of a file?  I don't see a portable way to
> hook into LOAD.

Don't use *read-suppress* for that.  Use #|comments|#.


-- 
__Pascal Bourguignon__
From: D Herring
Subject: Re: REPL *read-suppress* conundrum
Date: 
Message-ID: <494409b1$0$17066$6e1ede2f@read.cnntp.org>
Pascal J. Bourguignon wrote:
> D Herring <········@at.tentpost.dot.com> writes:
> 
>> If you're at a REPL, and type (setf *read-suppress* t), is there any
>> way to recover?  It this where lisp systems go to die?
> 
> Only lisp systems that are careless.  clisp doesn't uses the user's
> *read-suppress* to read from the REPL:

Hmm...  SBCL does.


> Don't use *read-suppress* for that.  Use #|comments|#.

Exactly, except #| |# is static.

- Daniel
From: budden
Subject: Re: REPL *read-suppress* conundrum
Date: 
Message-ID: <7f73a061-bc3d-47de-b299-7ae25014d772@s9g2000prm.googlegroups.com>
If the file you're reading is data, and the data is always correct,
and no side effects on reading, and you don't care speed, you'd better
write a simple parser. Read everything, then filter out what you don't
need. It would look nicer, at least (but work slower). But I admit
your last solution is elegant (if there are no pitfalls).
----------
Now I'm $5/hour lisp freelancer