Combining syntax classes together as multiple variants
define-syntax-class/  or*
8.12

Combining syntax classes together as multiple variants🔗ℹ

 (require syntax/parse/syntax-class-or)
  package: syntax-class-or

syntax

(define-syntax-class/or* head
  #:attributes [attr-arity-decl ...]
  reified-classes-expr)
 
head = name-id
  | (name-id parameters)
     
parameters = parameter ...
  | parameter ... . rest-id
     
parameter = param-id
  | [param-id default-expr]
  | #:keyword param-id
  | #:keyword [param-id default-expr]
     
attr-arity-decl = attr-name-id
  | [attr-name-id depth]
 
  reified-classes-expr : (listof reified-syntax-class?)
Normally, there needs to be one syntax-class containing all the variants, in one centralized place. Even when you divide the the work into helper syntax classes, you’re limited to the variants that were already written that the parsing can depend on. The number of variants is limited by the syntax at compile time.

The syntax-parse reflection interface (~reflect) allows you to fill in a syntax class at runtime, which lets you leave a variant to be filled in from somewhere else. However, the ~reflect pattern only allows one syntax class, and that syntax class must include all the non-built-in variants, still limiting it to what some centralized parser can depend on. And still the number of new variants is limited to fixed number at compile time.

The define-syntax-class/or* form allows you to define a syntax class that combines a list of arbitrarily many variants into one parser. The list of variants can be computed at run time (relative to the parser) or can be passed in as arguments.

Examples:
> (require syntax/parse
           syntax/parse/experimental/reflect
           syntax/parse/syntax-class-or)
> (define-syntax-class addition
    #:datum-literals [+]
    [pattern [{~and a {~not +}} ... {~seq + {~and b {~not +}} ...} ...+]
      #:with op #'+
      #:with [sub ...] #'[[a ...] [b ...] ...]])
> (define-syntax-class multiplication
    #:datum-literals [*]
    [pattern [{~and a {~not *}} ... {~seq * {~and b {~not *}} ...} ...+]
      #:with op #'*
      #:with [sub ...] #'[[a ...] [b ...] ...]])
> (define-syntax-class exponentiation
    #:datum-literals [^]
    [pattern [{~and a {~not ^}} ... ^ b ...]
      #:with op #'expt
      #:with [sub ...] #'[[a ...] [b ...]]])
> (define-syntax-class/or* infix
    #:attributes [op [sub 1]]
    (list (reify-syntax-class addition)
          (reify-syntax-class multiplication)
          (reify-syntax-class exponentiation)))
> (define (parse stx)
    (syntax-parse stx
      [e:infix #`(e.op #,@(map parse (attribute e.sub)))]
      [[a] #'a]))
> (parse #'[x])

#<syntax:eval:8:0 x>

> (parse #'[a * x ^ 2 + b * x + c])

#<syntax:eval:7:0 (+ (* a (expt x 2)) (* b x) c)>

> (parse #'[a ^ b * c + 2 * d + 3])

#<syntax:eval:7:0 (+ (* (expt a b) c) (* 2 d) 3)>

> (parse #'[2 ^ 10 ^ x + -1 * y])

#<syntax:eval:7:0 (+ (expt 2 (expt 10 x)) (* -1 y))>