Это две части:
- Принятие
#:break
во входных данных в качестве необязательного ключевого слова - Создание
#:break
в выходных данных при необходимости
Существует три подхода: многоотраслевой, необязательный и синтаксический класс.
многоотраслевой
Самый простой способ решить эту проблему - использовать две ветви в синтаксисе: синтаксический анализатор. Одна ветвь с ключевым словом, другая ветвь без.
Две ветви означают два шаблона для сопоставления при вводе и два шаблона для вывода.
(define-syntax-parser myfor
[(_ Binding #:break Break Form1 ...)
#'(for (Binding #:break Break) Form1 ...)]
[(_ Binding Form1 ...)
#'(for (Binding) Form1 ...)])
Это довольно просто и понятно, но имеет тот недостаток, что вы повторяете себя, указывая for
, Binding
и Form1 ...
несколько раз для каждой ветви, и он становится более подробным, если вы добавляете больше ключевых слов.
Необязательно и ~?
Другой способ заключается в использовании ~optional
и ~seq
в шаблоне для сопоставления с вводом, а при использовании ~?
и ~@
в шаблоне для вывода.
(define-syntax-parser myfor
[(_ Binding {~optional {~seq #:break Break}} Form1 ...)
#'(for (Binding {~? {~@ #:break Break}}) Form1 ...)])
Обратите внимание, что ~?
в шаблоне - это где ~optional
было в шаблоне, а ~@
в шаблоне - там, где ~seq
было в шаблоне.
Эта стратегия лучше всего подходит при четкой симметрии между шаблоном ввода и шаблоном вывода.
Синтаксис-класс
(begin-for-syntax
(define-splicing-syntax-class maybe-break-clause
[pattern {~seq} #:with (out ...) '()]
[pattern {~seq #:break Break} #:with (out ...) #'(#:break Break)]))
(define-syntax-parser myfor
[(_ Binding mbc:maybe-break-clause Form1 ...)
#'(for (Binding mbc.out ...) Form1 ...)])
Эта стратегия излишня в этом случае для #:break
, но может стать необходимой, если поведение ключевого слова более сложное Это означает, что нужно просто передать ключевое слово, если оно существует.