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/CANy33q%3DD73GWbOqpu24L26dswZZgDz_5nkZFzw2jKszyAQ89Sw%40mail.gmail.com.

Reply via email to