Search code examples
parsingsyntaxschemeracketdefinition

Scheme/Racket: How to do repeating in defining syntax


I can define the infix '+' as below in Racket:

(require syntax/parse/define (only-in racket (#%app racket:#%app)))
(define-syntax-parser #%app
  [(_ Value1 {~datum +} Value2)
  #'(+ Value1 Value2)]

  [(_ Xs ...) 
  #'(racket:#%app Xs ...)]
)
(displayln (1 + 2))

I want to add multiple infix '+' using ~between but it doesn't work:

(require syntax/parse/define (only-in racket (#%app racket:#%app)))
(define-syntax-parser #%app
  [(_ {~between {Value1 {~datum +}} 1 100} Value2)
  #'(+ Value1 Value2)]

  [(_ Xs ...) 
  #'(racket:#%app Xs ...)]
)
(displayln (1 + 2))

The syntax in Racket is here: https://docs.racket-lang.org/syntax/stxparse-patterns.html with ~between but no ~repeat.

How to use ~between property to repeat items in syntax?

I have a work-around but it doesn't look pure multiple infix +, need to wrap every left entry in brackets:

(require syntax/parse/define (only-in racket (#%app racket:#%app)))
(define-syntax-parser #%app
  [(_ {Value1 {~datum +}} ... Value2)
  #'(+ Value1 ... Value2)]

  [(_ Xs ...) 
  #'(racket:#%app Xs ...)]
)
(displayln ({1 +} {2 +} 3))

Solution

  • What you want is a combination of ~seq and ...+.

    (define-syntax-parser #%app
      [(_ {~seq Value1 {~datum +}} ...+ Value2)
       #'(+ Value1 ... Value2)]
      [(_ Xs ...) 
       #'(racket:#%app Xs ...)])
    

    The ~seq matches a sequence of things without requiring them to be grouped by brackets as your workaround did.

    The ...+ is a repetition pattern to match one-or-more things, as opposed to zero-or-more. This makes sure that (f) isn't accidentally interpreted as (+ f).

    One more note, when you're defining #%app directly, as opposed to defining under a different name and then renaming the export, you need to be extra careful about implicit recursive uses. For instance (+ + x) is an infinite loop. To fix that you could use racket:#%app in both outputs, like #'(racket:#%app + Value1 ... Value2).

    (define-syntax-parser #%app
      [(_ {~seq Value1 {~datum +}} ...+ Value2)
       #'(racket:#%app + Value1 ... Value2)]
      [(_ Xs ...) 
       #'(racket:#%app Xs ...)])