Right, I would generally recommend against using `raise-syntax-error`
within syntax classes. syntax-parse has heuristics to convert the "most
specific" pattern failure into a syntax error, and it's usually better to
work within that framework (as your code using `~fail` does). To make that
work, you should also generally avoid catch-all clauses like the one in
your second example.

Ryan


On Fri, Dec 18, 2020 at 3:47 AM Sage Gerard <[email protected]> wrote:

> Fantastic, thank you for getting me on my way. I followed your advice
> where I do not use string-producing expressions and ended up with a follow
> up question.
>
> New syntax classes are below for those reading.
>
> (define (unbounded? v)
>   (equal? "*" v))
>
> (define-syntax-class racket-version-or-*
>   (pattern (~var bound string)
>            #:when (let ([v (syntax-e #'bound)])
>                     (or (unbounded? v)
>                         (valid-version? v)))))
>
> (define-syntax-class racket-version-selection
>   #:attributes (min max)
>   (pattern (~and (min:racket-version-or-* max:racket-version-or-*)
>                  (~fail #:unless (let ([min-v (syntax-e #'min)] [max-v
> (syntax-e #'max)])
>                                  (or (unbounded? min-v)
>                                      (unbounded? max-v)
>                                      (version<=? min-v max-v)))
>                         "minimum Racket version cannot exceed maximum
> Racket version")))
>   (pattern (~and (~var v racket-version-or-*)
>                  (~bind [min #'v] [max #'v]))))
>
>
> Note the condition set in racket-version-selection that checks for
> backwards ranges. From what I understand, it only raises a pattern failure,
> which means I only see the ~fail error message for:
>
>  (syntax-parse #'(("8.4" "8.3"))
>   [(v:racket-version-selection ...) #t])
>
> but not
>
>  (syntax-parse #'(("8.4" "8.3"))
>   [(v:racket-version-selection ...) #t]
>   [_ #f])
>
> In this case, is it better form to use raise-syntax-error in a #:when
> pattern directive for a syntax class, or beneath a clause of syntax-parse?
> I suspect that syntax classes should not have an opinion about flow control.
>
> *~slg*
>
>
> ‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
> On Thursday, December 17, 2020 4:52 AM, Ryan Culpepper <
> [email protected]> wrote:
>
> Based on what you have written so far, the `versions` macro has no
> sub-expressions, so you shouldn't use `expr/c` at all. It requires version
> bounds to be in the form of literal strings. So you could describe the
> macro using a grammar as follows:
>
>   Expression ::= .... | (versions Version ...)
>   Version ::= VersionBound | (VersionBound VersionBound)
>   VersionBound ::= String
>
> I think what you want to do is refine VersionBound so that it only accepts
> strings of a certain form. The best way to do that is with a separate
> syntax class that matches a string and then puts additional side-conditions
> on it (using `#:when`, etc). That is, you check the `valid-version?`
> predicate at compile-time.
>
> By the way, you should also avoid treating the literal strings that your
> macro receives as if they were also expressions. A syntax object containing
> a literal string is *not necessarily* a string-valued expression. Once your
> macro views and validates something as a literal string, the proper way to
> convert it to a run-time expression is to explicitly quote it. Consider the
> following test case:
>
>     (let-syntax ([#%datum (lambda (stx) #'(exit '0))]) (versions ("7.0"
> "7.7.0.5") "6.5"))
>
> If your macro produces eg (list (make-version-range (quote "7.0") (quote
> "7.7.0.5")) (quote "6.5")), then it's fine; if it produces (list
> (make-version-range "7.0" "7.7.0.5") "6.5"), then it would exit. This
> particular example is unlikely to happen in practice, but I think it is
> useful to think clearly about how interpret each argument of a macro. Treat
> it as a literal string or as an expression, but not both.
>
> A different design would be to say that VersionBound is an expression that
> produces a string. That would cause problems with your current grammar,
> because you couldn't tell whether `(f "1.2.3")` was a single version (whose
> value is produced by a function call) or by a range (whose lower bound is
> the variable f). But you could change the grammar to avoid that problem.
> Then you could use `expr/c` to wrap the expressions to check that at run
> time they produced strings of the proper form.
>
> Ryan
>
>
> On Thu, Dec 17, 2020 at 12:55 AM Sage Gerard <[email protected]> wrote:
>
>> Typos:
>>
>> - "*" remove a bound ==> "*" removes a bound
>> - All examples should read (versions ...), not (version ...)
>>
>> *~slg*
>>
>>
>> ‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
>> On Wednesday, December 16, 2020 6:27 PM, Sage Gerard <[email protected]>
>> wrote:
>>
>> I'm trying to learn how to write syntax classes. My intended macro
>> expresses a set of Racket versions, either as inclusive intervals or as
>> exact versions. In an interval, "*" remove a bound.
>>
>>    - (version "6.5") means exactly version "6.5", as does (version
>>    ("6.5" "6.5"))
>>    - (versions ("7.0" "7.7.0.5")) means the inclusive interval between
>>    version 7.0 and 7.7.0.5
>>    - (versions ("7.0" "7.7.0.5") "6.5"): union of the above two items
>>    - (versions ("6.0" "*")): all Racket versions >= 6.0
>>    - (versions "*"), (versions ("*" "*")): all Racket versions
>>
>> I was able to define the syntax class without much issue:
>>
>> (define-syntax-class racket-version-selection
>>   #:attributes (min max)
>>   (pattern (min:string max:string))
>>   (pattern (~and (~var v string)
>>                  (~bind [min #'v]
>>                         [max #'v]))))
>>
>> Now I want each attribute-bound expression V to satisfy (or
>> (valid-version? V) (equal? V "*")). Where I'm stuck is how I can use
>> #:declare with (expr/c) here. From what I understand, expr/c does not
>> really mean much because it accepts an expression (as in the expr
>> syntax-class), not attributes.
>>
>> The only way I can think to fix this is to perform an additional
>> syntax-parse so that I can use the attributes in an expression for expr/c
>> to consume. But is it possible to do everything I'm thinking of in just one
>> syntax class?
>>
>> *~slg*
>>
>>
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "Racket Users" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected].
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/racket-users/syiNcw0zJpSyA16fO8OkClrQmXFOC4qZEwrBm3JwETX-bGJGlALnP6Apn4ttCbIzMZUoobO7AT4MyRDm9ID0oUA648nXXSAZ1nvaCaj2NbI%3D%40sagegerard.com
>> <https://groups.google.com/d/msgid/racket-users/syiNcw0zJpSyA16fO8OkClrQmXFOC4qZEwrBm3JwETX-bGJGlALnP6Apn4ttCbIzMZUoobO7AT4MyRDm9ID0oUA648nXXSAZ1nvaCaj2NbI%3D%40sagegerard.com?utm_medium=email&utm_source=footer>
>> .
>>
>>
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "Racket Users" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected].
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/racket-users/nfmcqQmNc3_H9zeCyS49LELvXomDYQF2sJbmyrJchu0kiWG8CXJiyS932ZfQ_eSW3cnEYTTzOwqakNlKL4FF_KR4F7HnAARLdQLDVEGxSI0%3D%40sagegerard.com
>> <https://groups.google.com/d/msgid/racket-users/nfmcqQmNc3_H9zeCyS49LELvXomDYQF2sJbmyrJchu0kiWG8CXJiyS932ZfQ_eSW3cnEYTTzOwqakNlKL4FF_KR4F7HnAARLdQLDVEGxSI0%3D%40sagegerard.com?utm_medium=email&utm_source=footer>
>> .
>>
>
> --
> You received this message because you are subscribed to the Google Groups
> "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/racket-users/TKXh6HhBr0T2G5HEf2SE1BpmQBGdSEA016aiywEZqe1tbtfz4l_I68rPdW0S7mq0y_9IJuQYaS0Vrjh0RNP3G9t5ZSSgI1_wAJVETTmbpBc%3D%40sagegerard.com
> <https://groups.google.com/d/msgid/racket-users/TKXh6HhBr0T2G5HEf2SE1BpmQBGdSEA016aiywEZqe1tbtfz4l_I68rPdW0S7mq0y_9IJuQYaS0Vrjh0RNP3G9t5ZSSgI1_wAJVETTmbpBc%3D%40sagegerard.com?utm_medium=email&utm_source=footer>
> .
>

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/CANy33qneZC0%2BtY5h3YAnv5i61cqJ-si6b1Y%3D%3Den4aTiTiSQBFg%40mail.gmail.com.

Reply via email to