Sam
Sam

Reputation: 796

A splicing syntax class that matches an optional pattern and binds attributes

A splicing syntax class that I have is defined as follows. The syntax class matches a sequence of two statements (first pattern), one of the statements (third and second patterns) and perhaps even none of those statements at all (last pattern).

As you can see there is quite a lot of "duplicate" code, because every pattern returns either the attributes of something captured in the pattern, or an empty thing otherwise. The problem I have is that currently the statement is never truly optional, since the last pattern must match something. In this case an empty set of brackets ().

The question is: how can I make the statement truly optional? As a side question - can the code be condensed by making better use of head patterns?

(define-splicing-syntax-class signal-subscriptions-and-declarations
  #:description "subscriptions to signals and signal declarations"

  ; Match the case where both a subscription and declaration statement is present
  (pattern (~seq subscribed:signal-subscriptions  declared:signal-declarations)
           #:with (subscription-collection  ...) #'(subscribed.signal-collection ...)
           #:with (subscription-signal-id   ...) #'(subscribed.signal-identifier ...)
           #:with (declaration-signal-id    ...) #'(declared.signal-identifier ...))

  ; Match the case where no declaration statement is present
  (pattern subscribed:signal-subscriptions
           #:with (subscription-collection ...) #'(subscribed.signal-collection ...)
           #:with (subscription-signal-id  ...) #'(subscribed.signal-identifier ...)
           #:with (declaration-signal-id   ...) #'())

  ; Match the case where no subscription statement is present
  (pattern declared:signal-declarations
           #:with (subscription-collection ...) #'()
           #:with (subscription-signal-id  ...) #'()
           #:with (declaration-signal-id   ...) #'(declared.signal-identifier ...))

  (pattern ()
           #:with (subscription-collection ...) #'()
           #:with (subscription-signal-id  ...) #'()
           #:with (declaration-signal-id   ...) #'()))

Upvotes: 4

Views: 147

Answers (1)

Alex Knauth
Alex Knauth

Reputation: 8373

It sounds like you have two separate things, both of which are optional. So it makes sense to have two separate syntax-classes, like this:

(define-splicing-syntax-class opt-signal-subscriptions
  ;; Match the case where a subscription is present
  [pattern (~seq subscribed:signal-subscriptions)
           #:with (subscription-collection  ...) #'(subscribed.signal-collection ...)
           #:with (subscription-signal-id   ...) #'(subscribed.signal-identifier ...)]
  ;; Match the case where no subscription is present
  [pattern (~seq)
           #:with (subscription-collection  ...) #'()
           #:with (subscription-signal-id   ...) #'()])

(define-splicing-syntax-class opt-signal-declarations
  ;; Match the case where a declaration statement is present
  [pattern (~seq declared:signal-declarations)
           #:with (declaration-signal-id    ...) #'(declared.signal-identifier ...)]
  ;; Match the case where no declaration statement is present
  [pattern (~seq)
           #:with (declaration-signal-id    ...) #'()])

Both of these use an empty (~seq) case (matches 0 terms) to make it optional, instead of (), which matches 1 term. Then a syntax-class similar to your original one can be defined like this:

(define-splicing-syntax-class signal-subscriptions-and-declarations
  #:description "subscriptions to signals and signal declarations"
  #:auto-nested-attributes
  [pattern (~seq :opt-signal-subscriptions :opt-signal-declarations)])

This is different from your original one because this can match 0, 1, or 2 terms, while yours will require at least 1 term, which will have to be () when neither option is present.

Upvotes: 5

Related Questions