Схема / Ракетка: Как добавить ключевое слово в define-syntax-parser - PullRequest
0 голосов
/ 13 февраля 2020

Я могу определить синтаксис для for l oop следующим образом:

(require syntax/parse/define)
(define-syntax-parser myfor
  [(_ Binding Form1 ...)
   #'(for (Binding) Form1 ...)]
)

(myfor (I (range 0 10))
  (displayln I)
)

Это работает, но когда я добавляю к нему ключевое слово, оно не следует условию в ключевом слове #:break :

(require syntax/parse/define)
(define-syntax-parser myfor
  [(_ Binding #:break Break Form1 ...)
   #'(for (Binding) Form1 ...)]
)

(myfor (I (range 0 10))
  #:break (= I 5)
  (displayln I)
)

Как добавить ключевые слова в define-syntax-parser правильным способом?

Ответы [ 2 ]

2 голосов
/ 13 февраля 2020

Это две части:

  1. Принятие #:break во входных данных в качестве необязательного ключевого слова
  2. Создание #: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, но может стать необходимой, если поведение ключевого слова более сложное Это означает, что нужно просто передать ключевое слово, если оно существует.

0 голосов
/ 13 февраля 2020

Я узнал, как, мне нужно добавить ~ необязательный и ~ seq в синтаксис:

(require syntax/parse/define)
(define-syntax-parser myfor
  [(_ Binding {~optional {~seq #:break Break} #:defaults [(Break #'#f)] } Form1 ...)
   #'(for (Binding #:break Break) Form1 ...)]
)

(myfor (I (range 0 10))
  #:break (= I 5)
  (displayln I)
)
...