Racket macro that defines multiple top-level forms?
Asked Answered
E

2

5

I found myself defining syntax parameters with identical definitions except for their name so I decided to write a macro to make this simpler:

(define-syntax (test-case-parameter stx)
  (syntax-parse stx
    [(_ parameter:id)
     #'(define-syntax-parameter parameter
         (lambda (stx)
           (raise-syntax-error stx "Can only be used inside test-case.")))]))

(test-case-parameter a)
(test-case-parameter b)
(test-case-parameter c)

However instead of having to repeat the macro name I would like to be able to just write:

(test-case-parameter a b c)

But I don't see how to do this using the normal ellipses syntax, because I would need to wrap everything in a begin which would create a new scope, and I want all of the syntax parameters as if I had written them each of the top level. What's the right way to accomplish this?

Expansion answered 25/9, 2016 at 18:52 Comment(0)
F
7

The answer is to use begin. begin is weird, because it has different behavior at the top-level than it does in an expression context. At the top-level, it has the kind of splicing behavior you would want for this macro, but in an expression context is has the scoping behavior you're referring to.

So you can define your macro like this:

#lang racket
(require racket/stxparam (for-syntax syntax/parse))

(define-syntax (define-test-case-parameters stx)
  (syntax-parse stx
    [(_ parameter:id ...)
     #'(begin
         (define-syntax-parameter parameter
           (lambda (stx)
             (raise-syntax-error stx "Can only be used inside test-case.")))
         ...)]))

(define-test-case-parameters a b c)

You can see how the begin top-level splicing works in the Macro Stepper in DrRacket:

splicing-begin-macro-stepper

Fortieth answered 25/9, 2016 at 18:59 Comment(1)
I just realized I was a bit wrong when I wrote this. begin doesn't have scoping behavior in a pure expression context: it disallows definitions entirely in contexts where it can't splice definitions inFortieth
I
1

Make a new macro that accepts multiple identifiers and let it expand to a sequence of usages of your version that uses a single identifier.

#lang racket
(require (for-syntax syntax/parse)
         racket/stxparam)

(define-syntax (test-case-parameter-helper stx)
  (syntax-parse stx
    [(_test-case-parameter-helper parameter:id)
     (syntax/loc stx
       (define-syntax-parameter parameter
         (lambda (stx)
           (raise-syntax-error stx "Can only be used inside test-case."))))]))

(define-syntax (test-case-parameter stx)
  (syntax-parse stx
    [(_test-case-parameter parameter:id ...)
     (syntax/loc stx
       (begin
         (test-case-parameter-helper parameter)
         ...))]))

(test-case-parameter a b c)
Impractical answered 25/9, 2016 at 18:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.