<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    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.<br>
    <br>
    (See <font face="monospace">#'sb-impl::sharp-plus-minus</font> in
    the SBCL source.)<br>
    <br>
    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.<br>
    <br>
    <font face="monospace">(in-package :ccl)<br>
      (defun read-feature (stream)<br>
        (let* ((f (let* ((*package* *keyword-package*)<br>
                         (*read-suppress* nil)) ;;; <-- This is the
      change<br>
                    (read stream t nil t))))<br>
          (labels ((eval-feature (form)<br>
                     (cond ((atom form) <br>
                            (member form *features*))<br>
                           ((eq (car form) :not) <br>
                            (not (eval-feature (cadr form))))<br>
                           ((eq (car form) :and) <br>
                            (dolist (subform (cdr form) t)<br>
                              (unless (eval-feature subform) (return))))<br>
                           ((eq (car form) :or) <br>
                            (dolist (subform (cdr form) nil)<br>
                              (when (eval-feature subform) (return t))))<br>
                           (t (%err-disp $XRDFEATURE form)))))<br>
            (if (eval-feature f) #\+ #\-))))<br>
      <br>
    </font>
    <div class="moz-cite-prefix">-SS<br>
      <br>
      On 3/13/23 4:07 PM, phoebe Goldman wrote:<br>
    </div>
    <blockquote type="cite"
      cite="mid:5C1F621B-3C5D-498C-A4B2-CE17BBDD4406@goldman-tribe.org">
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      Yes, you're right, good catch; in the second case, at step 2, I
      meant for the feature expression to be false.
      <div><br>
      </div>
      <div>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.<br>
        <div><br>
          <blockquote type="cite">
            <div>On Mar 13, 2023, at 6:05 PM, Arthur Cater
              <a class="moz-txt-link-rfc2396E" href="mailto:arthur.cater@ucd.ie"><arthur.cater@ucd.ie></a> wrote:</div>
            <br class="Apple-interchange-newline">
            <div>
              <meta http-equiv="Content-Type" content="text/html;
                charset=UTF-8">
              <div style="word-wrap: break-word; -webkit-nbsp-mode:
                space; line-break: after-white-space;">Thank you for
                that thorough explanation, I appreciate it.
                <div>Permit me to make a minor correction, I believe in
                  the “:FOO not in” part, step 2, you mean evaluate
                  feature expression to false. 
                  <div><br>
                  </div>
                  <div>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</div>
                  <div>(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.</div>
                  <div>(b) I was just curious about what such syntax
                    would mean in a mixed case.</div>
                  <div><br>
                  </div>
                  <div>Arthur<br>
                    <div><br>
                      <blockquote type="cite">
                        <div>On 13 Mar 2023, at 21:30, phoebe Goldman
                          <<a href="mailto:phoebe@goldman-tribe.org"
                            moz-do-not-send="true"
                            class="moz-txt-link-freetext">phoebe@goldman-tribe.org</a>>
                          wrote:</div>
                        <br class="Apple-interchange-newline">
                        <div>
                          <meta http-equiv="content-type"
                            content="text/html; charset=UTF-8">
                          <div style="overflow-wrap: break-word;
                            -webkit-nbsp-mode: space; line-break:
                            after-white-space;">2.4.8.17 Sharpsign
                            Plus, <a
                              href="http://www.lispworks.com/documentation/HyperSpec/Body/02_dhq.htm"
                              moz-do-not-send="true"
                              class="moz-txt-link-freetext">http://www.lispworks.com/documentation/HyperSpec/Body/02_dhq.htm</a> ,
                            says:
                            <div><br>
                            </div>
                            <div>
                              <blockquote type="cite">#+ 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.</blockquote>
                            </div>
                            <div><br>
                            </div>
                            <div>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.</div>
                            <div><br>
                            </div>
                            <div>Assume that *READ-SUPPRESS* is globally
                              NIL. Consider reading:</div>
                            <div><br>
                            </div>
                            <div>#+foo #+foo a b</div>
                            <div><br>
                            </div>
                            <div>If :FOO is in *FEATURES*, the reader
                              will:</div>
                            <div><br>
                            </div>
                            <div>1. Read the first #+FOO, evaluate its
                              feature expression to true, and call READ
                              recursively with no new *READ-SUPPRESS*
                              binding.</div>
                            <div>  2. Read the second #+FOO, evaluate
                              its feature expression to true, and call
                              READ recursively with no new
                              *READ-SUPPRESS* binding.</div>
                            <div>    3. Read A with the root
                              *READ-SUPPRESS* binding of NIL, and return
                              the symbol A.</div>
                            <div>4. Your initial call to READ has now
                              returned, but there's more input. Call
                              READ again to get another object.</div>
                            <div>5. Read B the root *READ-SUPPRESS*
                              binding of NIL, and return the symbol B.</div>
                            <div><br>
                            </div>
                            <div>If :FOO is not in *FEATURES*, the
                              reader will:</div>
                            <div><br>
                            </div>
                            <div>1. Read the first #+FOO, evaluate its
                              feature expression to false, and call READ
                              recursively with *READ-SUPPRESS* bound to
                              T.</div>
                            <div>  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.</div>
                            <div>    3. Within both bindings, read A
                              with *READ-SUPPRESS* true, and return
                              nothing.</div>
                            <div>  4. Within the outer binding, read A
                              with *READ-SUPPRESS* true, and return
                              nothing.</div>
                            <div>5. Your initial call to READ has now
                              returned, and you didn't get anything.</div>
                            <div><br>
                            </div>
                            <div>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.</div>
                            <div><br>
                            </div>
                            <div>Cheers,</div>
                            <div>phoebe</div>
                            <div><span id="x-apple-selection:end"></span><span><br>
                                <blockquote type="cite">On Mar 13, 2023,
                                  at 5:06 PM, Tim McNerney <<a
                                    href="mailto:mc@media.mit.edu"
                                    moz-do-not-send="true"
                                    class="moz-txt-link-freetext">mc@media.mit.edu</a>>
                                  wrote:<br>
                                  <br class="Apple-interchange-newline">
                                  Would someone mind quoting “chapter
                                  and verse”? <br>
                                  <br>
                                  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. <br>
                                  <br>
                                  Yes, I absolutely hate <br>
                                       #+foo :keyword #+foo argument
                                  too. <br>
                                  <br>
                                  --Tim<br>
                                  <br>
                                  <blockquote type="cite">On Mar 13,
                                    2023, at 14:00, Robert Goldman <<a
                                      href="mailto:rpgoldman@sift.info"
                                      moz-do-not-send="true"
                                      class="moz-txt-link-freetext">rpgoldman@sift.info</a>>
                                    wrote:<br>
                                    <br>
                                  </blockquote>
                                  <blockquote type="cite">
                                    On 13 Mar 2023, at 12:42, Ron Garret
                                    wrote:<br>
                                    Oh, forgot to add...<br>
                                    On Mar 13, 2023, at 10:20 AM, Arthur
                                    Cater <<a
                                      href="mailto:arthur.cater@ucd.ie"
                                      moz-do-not-send="true"
                                      class="moz-txt-link-freetext">arthur.cater@ucd.ie</a>>
                                    wrote:<br>
                                    It never previously occurred to me
                                    that read time conditionals could be
                                    nested.<br>
                                    Just because you can do something
                                    doesn't mean you should.<br>
                                    rg<br>
                                    I was surprised myself to see<br>
                                    #+foo #+foo<br>
                                    <br>
                                    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<br>
                                    (list :prop1 12<br>
                                    #+foo #+foo<br>
                                    :foo 'bar)<br>
                                    <br>
                                    In my opinion, that's clearer than<br>
                                    (list :prop1 12<br>
                                    #+foo :foo #+foo 'bar)<br>
                                    <br>
                                    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.<br>
                                    (append (list :prop 12) #+foo (list
                                    :foo 'bar) )<br>
                                    <br>
                                    is not an alternative particularly
                                    pleasing to me, but YMMV. It's
                                    certainly very busy.<br>
                                  </blockquote>
                                </blockquote>
                              </span><br>
                            </div>
                          </div>
                        </div>
                      </blockquote>
                    </div>
                    <br>
                  </div>
                </div>
              </div>
            </div>
          </blockquote>
        </div>
        <br>
      </div>
    </blockquote>
    <br>
  </body>
</html>