[Openmcl-devel] multiple read-time conditionals, puzzlement

Shannon Spires svs at bearlanding.com
Mon Mar 13 15:50:52 PDT 2023


Here's a patch that makes CCL behave like SBCL for Ron's examples. The 
issue is that during the act of reading the feature expression itself 
(the symbol or list that immediately follows the #\+ or #\- character) , 
*read-suppress* must be nil [or else some low-level reader that ignores 
*read-suppress* must be called]. SBCL does bind *read-suppress* to nil 
during this read; this patch makes CCL do so too.

(See #'sb-impl::sharp-plus-minus in the SBCL source.)

I hesitate to submit this patch because I don't know that it's a 
complete solution, and it's also likely that existing code depended on 
the old, non-SBCL-like behavior.

(in-package :ccl)
(defun read-feature (stream)
   (let* ((f (let* ((*package* *keyword-package*)
                    (*read-suppress* nil)) ;;; <-- This is the change
               (read stream t nil t))))
     (labels ((eval-feature (form)
                (cond ((atom form)
                       (member form *features*))
                      ((eq (car form) :not)
                       (not (eval-feature (cadr form))))
                      ((eq (car form) :and)
                       (dolist (subform (cdr form) t)
                         (unless (eval-feature subform) (return))))
                      ((eq (car form) :or)
                       (dolist (subform (cdr form) nil)
                         (when (eval-feature subform) (return t))))
                      (t (%err-disp $XRDFEATURE form)))))
       (if (eval-feature f) #\+ #\-))))

-SS

On 3/13/23 4:07 PM, phoebe Goldman wrote:
> Yes, you're right, good catch; in the second case, at step 2, I meant 
> for the feature expression to be false.
>
> I agree that you should not do this, and Robert's linked thread from 
> Allegro is evidence that implementations may mis-read nested read 
> conditionals.
>
>> On Mar 13, 2023, at 6:05 PM, Arthur Cater <arthur.cater at ucd.ie> wrote:
>>
>> Thank you for that thorough explanation, I appreciate it.
>> Permit me to make a minor correction, I believe in the “:FOO not in” 
>> part, step 2, you mean evaluate feature expression to false.
>>
>> fwiw I’d hate to find in code (even my own) anything more complex 
>> than that doubled-up test of the same feature expression. I was 
>> experimenting because
>> (a) it was the first time I’d seen such a construction and I wasn’t 
>> sure it would do what it was clearly intended to do. I thought it was 
>> neat.
>> (b) I was just curious about what such syntax would mean in a mixed case.
>>
>> Arthur
>>
>>> On 13 Mar 2023, at 21:30, phoebe Goldman <phoebe at goldman-tribe.org> 
>>> wrote:
>>>
>>> 2.4.8.17 Sharpsign Plus, 
>>> http://www.lispworks.com/documentation/HyperSpec/Body/02_dhq.htm , 
>>> says:
>>>
>>>> #+ operates by first reading the feature expression and then 
>>>> skipping over the form if the feature expression fails. While 
>>>> reading the test, the current package is the KEYWORD package. 
>>>> Skipping over the form is accomplished 
>>>> by binding *read-suppress* to true and then calling read.
>>>
>>> This means that a series of #+ or #- read-conditionals will result 
>>> in recursive calls to READ with a stack of T bindings on 
>>> *READ-SUPPRESS*. Note that neither reader macro ever binds 
>>> *READ-SUPPRESS* to NIL, only either introduces a binding of T or 
>>> uses the existing value.
>>>
>>> Assume that *READ-SUPPRESS* is globally NIL. Consider reading:
>>>
>>> #+foo #+foo a b
>>>
>>> If :FOO is in *FEATURES*, the reader will:
>>>
>>> 1. Read the first #+FOO, evaluate its feature expression to true, 
>>> and call READ recursively with no new *READ-SUPPRESS* binding.
>>>   2. Read the second #+FOO, evaluate its feature expression to true, 
>>> and call READ recursively with no new *READ-SUPPRESS* binding.
>>>     3. Read A with the root *READ-SUPPRESS* binding of NIL, and 
>>> return the symbol A.
>>> 4. Your initial call to READ has now returned, but there's more 
>>> input. Call READ again to get another object.
>>> 5. Read B the root *READ-SUPPRESS* binding of NIL, and return the 
>>> symbol B.
>>>
>>> If :FOO is not in *FEATURES*, the reader will:
>>>
>>> 1. Read the first #+FOO, evaluate its feature expression to false, 
>>> and call READ recursively with *READ-SUPPRESS* bound to T.
>>>   2. Within that binding, read the second #+FOO, evaluate its 
>>> feature expression to true, and call READ recursively with an 
>>> additional layer of *READ-SUPPRESS* = T binding.
>>>     3. Within both bindings, read A with *READ-SUPPRESS* true, and 
>>> return nothing.
>>>   4. Within the outer binding, read A with *READ-SUPPRESS* true, and 
>>> return nothing.
>>> 5. Your initial call to READ has now returned, and you didn't get 
>>> anything.
>>>
>>> When you're dealing with multiple distinct feature expressions which 
>>> are not uniformly all true or false, the behavior gets more 
>>> complicated, but it's always defined by the spec as recursive calls 
>>> to READ with a stack of *READ-SUPPRESS* bindings. I haven't looked 
>>> carefully at the cases that spawned this discussion to see if CCL's 
>>> behavior is compliant, but I don't believe there's any ambiguity in 
>>> the spec.
>>>
>>> Cheers,
>>> phoebe
>>>
>>>> On Mar 13, 2023, at 5:06 PM, Tim McNerney <mc at media.mit.edu> wrote:
>>>>
>>>> Would someone mind quoting “chapter and verse”?
>>>>
>>>> Somehow, even those of us with decades of Common Lisp experience, 
>>>> all trying to follow the drama of the X3J13 committee, a bunch of 
>>>> us “missed the memo” about this convenient, allegedly legal, but 
>>>> “buyer beware” syntax.
>>>>
>>>> Yes, I absolutely hate
>>>>      #+foo :keyword #+foo argument too.
>>>>
>>>> --Tim
>>>>
>>>>> On Mar 13, 2023, at 14:00, Robert Goldman <rpgoldman at sift.info> wrote:
>>>>>
>>>>>  On 13 Mar 2023, at 12:42, Ron Garret wrote:
>>>>> Oh, forgot to add...
>>>>> On Mar 13, 2023, at 10:20 AM, Arthur Cater <arthur.cater at ucd.ie> 
>>>>> wrote:
>>>>> It never previously occurred to me that read time conditionals 
>>>>> could be nested.
>>>>> Just because you can do something doesn't mean you should.
>>>>> rg
>>>>> I was surprised myself to see
>>>>> #+foo #+foo
>>>>>
>>>>> used to make two s-expressions (in)visible in the presence 
>>>>> (absence) of the :foo feature. But it's clearly dictated by the 
>>>>> spec, and is often handy when, for example one might want to have 
>>>>> something conditionally in a property list
>>>>> (list :prop1 12
>>>>> #+foo #+foo
>>>>> :foo 'bar)
>>>>>
>>>>> In my opinion, that's clearer than
>>>>> (list :prop1 12
>>>>> #+foo :foo #+foo 'bar)
>>>>>
>>>>> which obscures reading this as a property list and obscures the 
>>>>> fact that the two elements of the list are going to be swapped in 
>>>>> or out based on a single feature.
>>>>> (append (list :prop 12) #+foo (list :foo 'bar) )
>>>>>
>>>>> is not an alternative particularly pleasing to me, but YMMV. It's 
>>>>> certainly very busy.
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.clozure.com/pipermail/openmcl-devel/attachments/20230313/8aaf3a10/attachment.htm>


More information about the Openmcl-devel mailing list