Re: [racket-users] Using expr/c within one syntax class?

2020-12-18 Thread Ryan Culpepper
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  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 <
> rmculpepp...@gmail.com> 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  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 
>> 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.
>>
>>- (ve

Re: [racket-users] Using expr/c within one syntax class?

2020-12-17 Thread Sage Gerard
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  
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  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  
>> 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

Re: [racket-users] Using expr/c within one syntax class?

2020-12-17 Thread Ryan Culpepper
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  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 
> 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 racket-users+unsubscr...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/racket-users/syiNcw0zJpSyA16fO8OkClrQmXFOC4qZEwrBm3JwETX-bGJGlALnP6Apn4ttCbIzMZUoobO7AT4MyRDm9ID0oUA648nXXSAZ1nvaCaj2NbI%3D%40sagegerard.com
> 
> .
>
>
> --
> 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 racket-users+unsubscr...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/racket-users/nfmcqQmNc3_H9zeCyS49LELvXomDYQF2sJbmyrJchu0kiWG8CXJiyS932ZfQ_eSW3cnEYTTzOwqakNlKL4FF_KR4F7HnAARLdQLDVEGxSI0%3D%40sagegerard.com
> 

Re: [racket-users] Using expr/c within one syntax class?

2020-12-16 Thread Sage Gerard
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  
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 racket-users+unsubscr...@googlegroups.com.
> 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 racket-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/nfmcqQmNc3_H9zeCyS49LELvXomDYQF2sJbmyrJchu0kiWG8CXJiyS932ZfQ_eSW3cnEYTTzOwqakNlKL4FF_KR4F7HnAARLdQLDVEGxSI0%3D%40sagegerard.com.


[racket-users] Using expr/c within one syntax class?

2020-12-16 Thread Sage Gerard
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 racket-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/syiNcw0zJpSyA16fO8OkClrQmXFOC4qZEwrBm3JwETX-bGJGlALnP6Apn4ttCbIzMZUoobO7AT4MyRDm9ID0oUA648nXXSAZ1nvaCaj2NbI%3D%40sagegerard.com.