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.

