3
votes

I've just started diving into Racket macros, and am trying to make a terse simple-macro-defining macro. I would like to expand an expression like this:

(macro id
    (param) replacement1
    (params ...) replacement2)

Into something like this:

(define-syntax id
    (syntax-rules ()
        ((id param) replacement1)
        ((id params ...) replacement2)))

So the cddr of the original expression is turned into pairs of expressions (for use in the syntax-rules body), and the id is inserted into the car of each of these pairs.

I'm having trouble thinking recursively when using only the pattern-matching provided by syntax-rules (I keep wanting to manipulate the expression as though it were a normal list). What kind of pattern should I use? Or, can I somehow manipulate it as a normal list, and then unquote the result for use in the expansion?

Many thanks

Edit - tentative solution, informed by Taymon's answer

Part of my curiosity here was about getting rid of those pairing parentheses. I looked into syntax-case, but got a bit confused, so tried to do it purely with the pattern-matching sub-language. I ended up using Taymon's macro combined with another macro to 'pairize' the given templates (it acts kind of like an accumulator function):

(define-syntax-rule (macro-aux id ((param ...) expr) ...)
  (define-syntax id
    (syntax-rules ()
      ((id param ...) expr)
      ...)))

(define-syntax pairize
  (syntax-rules ()
   ((pairize id (pairs ...) p b) (macro-aux id pairs ... (p b)))
   ((pairize id (pairs ...) p b rest ...) (pairize id (pairs ... (p b)) rest ...))))

(define-syntax macro
  (syntax-rules ()
    ((macro id tpl-expr ...) (pairize id () tpl-expr ...))))
2

2 Answers

7
votes

It is possible to build a macro expander that manipulates the syntax expression as regular Racket data. However, that's not really necessary in this case.

One thing I would recommend is changing your syntax slightly, so that each pattern-replacement pair is enclosed in brackets. Like this:

(macro id
  [(param) replacement1]
  [(params ...) replacement2])

Once that's done, you can just use a regular pattern-matching macro. Here's my take on it:

(define-syntax-rule (macro id [(param ...) replacement] ...)
  (define-syntax id
    (syntax-rules ()
      [(id param ...) replacement] ...)))
2
votes

Taymon is right, but it is also possible to do it with ellipses without wrapping the pattern-replacement pairs in brackets, using ~seq from syntax/parse:

(require syntax/parse/define)
(define-simple-macro (macro id (~seq (param ...) replacement) ...)
  (define-syntax id
    (syntax-rules ()
      [(id param ...) replacement] ...)))

Which can be used like you originally wanted:

(macro id
  (param) replacement1
  (params ...) replacement2)