Podczas pisania makra korzystającego z syntax/parse
utworzyłem klasę składni łączenia, która przechwytuje opcje, które mogą być dostarczone do makra. Te opcje są opcjonalne i mogą być dostarczane w dowolnej kolejności. Korzystanie z głowy wzór ~optional
wielokropka czyni to dość proste:Jak grupować opcjonalne atrybuty przechwycone przy pomocy składni-parsowania?
(define-splicing-syntax-class opts
(pattern (~seq (~or (~optional (~seq #:a a))
(~optional (~seq #:b b))
(~optional (~seq #:x x))
(~optional (~seq #:y y)))
...))
Istnieje jednak pewien haczyk: Chcę, aby móc grupy tych opcji na dwie grupy: grupa zawierająca a
i b
, a grupa zawierające x
i y
. Jednak użytkownik nadal może określić opcje w dowolnej kolejności, więc na tym przykładzie wejścia:
(foobar #:b 3 #:y 7 #:a 2)
Chcę być w stanie przedstawić następujące atrybuty:
first-opts: (#:a 2 #:b 3)
second-opts: (#:y 7)
tej pory mam udało się to zrobić ręcznie przy użyciu #:with
, ale to nie jest całkiem:
(define-splicing-syntax-class opts
#:attributes ([first-opts 1] [second-opts 1])
(pattern (~seq (~or (~optional (~seq #:a a))
(~optional (~seq #:b b))
(~optional (~seq #:x x))
(~optional (~seq #:y y)))
...)
#:with (first-opts ...)
#`(#,@(if (attribute a) #'(#:a a) #'())
#,@(if (attribute b) #'(#:b b) #'()))
#:with (second-opts ...)
#`(#,@(if (attribute x) #'(#:x x) #'())
#,@(if (attribute y) #'(#:y y) #'()))))
ten można uprościć trochę korzystając template
z syntax/parse/experimental/template
:
(define-splicing-syntax-class opts
#:attributes ([first-opts 1] [second-opts 1])
(pattern (~seq (~or (~optional (~seq #:a a))
(~optional (~seq #:b b))
(~optional (~seq #:x x))
(~optional (~seq #:y y)))
...)
#:with (first-opts ...)
(template ((?? ([email protected] #:a a))
(?? ([email protected] #:b b))))
#:with (second-opts ...)
(template ((?? ([email protected] #:a x))
(?? ([email protected] #:b y))))))
Jednak jest to naprawdę tylko niektóre cukru na powyższe, a nie faktycznie rozwiązać problem konieczności wyliczać każdą opcję w każdym punkcie. Jeśli na przykład dodałem opcję #:c
, muszę pamiętać o dodaniu jej do grupy first-opts
, w przeciwnym razie zostanie ona całkowicie zignorowana.
Chcę to deklaratywny sposób grupowania tych zestawów wartości opcjonalnych. Na przykład, chciałbym składni takiego:
(define-splicing-syntax-class opts
#:attributes ([first-opts 1] [second-opts 1])
(pattern (~seq (~or (~group first-opts
(~optional (~seq #:a a))
(~optional (~seq #:b b)))
(~group second-opts
(~optional (~seq #:x x))
(~optional (~seq #:y y))))
...)))
Albo, jeszcze lepiej, byłoby miło, gdybym mógł wykorzystać istniejące prymitywów, coś takiego:
(define-splicing-syntax-class opts
#:attributes ([first-opts 1] [second-opts 1])
(pattern (~seq (~or (~and first-opts
(~seq (~optional (~seq #:a a))
(~optional (~seq #:b b))))
(~and second-opts
(~seq (~optional (~seq #:x x))
(~optional (~seq #:y y)))))
...)))
Jednak żaden z te prace. Czy jest jakiś sposób, aby to zrobić za pomocą wbudowanych funkcji dostarczonych przez syntax/parse
? Jeśli nie, czy istnieje prosty sposób zdefiniowania czegoś takiego, jak ja sam?
Nie działa to dla mnie, ponieważ nie mam domyślnej wartości do użycia - potrzebuję argumentów słów kluczowych, które nie są dostarczane do * nie * pojawiają się w rozszerzeniu. –