Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Racket macro that defines multiple top-level forms?

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?

like image 325
Joseph Garvin Avatar asked Sep 25 '16 18:09

Joseph Garvin


2 Answers

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

like image 181
Alex Knauth Avatar answered Oct 23 '22 11:10

Alex Knauth


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)
like image 37
soegaard Avatar answered Oct 23 '22 10:10

soegaard