#scheme #racket
#схема #ракетка
Вопрос:
По какой-то причине следующий макрос не сможет работать с кавычками.
(define-syntax expand
(lambda (stx)
(syntax-case stx (break)
[(_ k () (ys ...)) (begin (println (syntax->datum #'(ys ...))) #'(ys ...))]
[(_ k ((break) xs ...) (ys ...)) #'(expand k (xs ...) (ys ... (k (void))))]
[(_ k ((es ...) xs ...) (ys ...)) #'(expand k (xs ...) (ys ... (expand k (es ...) ())))]
[(_ k (a xs ...) (ys ...)) #'(expand k (xs ...) (ys ... a))])))
(define-syntax loop
(syntax-rules ()
[(_ e es ...)
(call/cc (lambda (k)
(let l ()
(expand k (begin e es ...) ())
(l))))]))
(loop (list 1 (break)))
;; => works fine
(loop (quasiquote (1 (unquote (break)))))
;; => break: unbound identifier in module in: break
Я немного удивлен, увидев, почему второй случай завершается неудачей.
И следующая отладочная информация печатается для обоих случаев.
;; case 1
'(begin (expand k (list 1 (break)) ()))
'(list 1 (k (void)))
;; case 2
'(begin (expand k `(1 ,(break)) ()))
'`(expand k (1 ,(break)) ())
Пожалуйста, обратите внимание, что в выводе для случая 2 после quasiquote
расширения остальное (1 ,(break))
почему-то не расширяется.
Не уверен, почему это произойдет.
Спасибо
Комментарии:
1.вывод для случая 2 после
quasiquote
расширения я не вижу его в вашем вопросе. Последняя строка'`(expand k (1 ,(break)) ())
представляет состояние передquasiquote
расширением.
Ответ №1:
Проблема в том, что расширитель макросов не расширяет вызовы макросов, которые отображаются под quote
или quasiquote
. Например:
(define-syntax-rule (pipe) "|")
> (quote (pipe))
'(pipe) ; not "|"
> (quasiquote (pipe))
'(pipe) ; not "|"
Это можно решить, выполнив рекурсию для синтаксического объекта непосредственно во время компиляции, вместо того, чтобы выполнять рекурсию, возвращая синтаксический объект с вызовом макроса внутри него.
В общем, переведите код следующим образом:
(define-syntax expand
(lambda (stx)
(syntax-case stx literals
cases
[pattern #'(.... (expand stuff) ...)]
cases)))
В код, подобный этому:
(begin-for-syntax
(define (expand stx)
(syntax-case stx literals
cases
[pattern #`(.... #,(expand stuff) ...)]
cases)))
В вашем конкретном случае вы, вероятно, хотите expand
быть функцией с тремя аргументами, которая выполняется и полностью повторяется во время компиляции.
(begin-for-syntax
(define (expand k xs ys)
(with-syntax ([(ys ...) ys])
(syntax-case xs (break)
[() (begin (println (syntax->datum #'(ys ...))) #'(ys ...))]
[((break) xs ...) (expand k #'(xs ...) #'(ys ... (k (void))))]
[((es ...) xs ...) (expand k #'(xs ...) #`(ys ... #,(expand k #'(es ...) #'())))]
[(a xs ...) (expand k #'(xs ...) #'(ys ... a))]))))
Затем вы можете вызвать эту функцию времени компиляции в реализации loop
макроса:
(define-syntax loop
(lambda (stx)
(syntax-case stx ()
[(_ e es ...)
#`(call/cc (lambda (k)
(let l ()
#,(expand #'k #'(begin e es ...) #'())
(l))))])))
Однако это не лучший способ выполнить макрос с циклическим выполнением.
Я надеюсь, что описанная выше функция времени компиляции поможет вам понять, что возможно с макросами. Однако для loop
макроса это не должно быть необходимо. Синтаксический параметр предоставляет гораздо более простой способ сделать это.
(define-syntax-parameter break
(lambda (stx) (raise-syntax-error #f "cannot be used outside of loop" stx)))
(define-syntax loop
(syntax-rules ()
[(_ e es ...)
(call/cc (lambda (k)
(define (break-function) (k (void)))
(syntax-parameterize ([break (make-rename-transformer #'break-function)])
(let l ()
(begin e es ...)
(l)))))]))
Фактически, loop
макрос, подобный этому, является одним из примеров, используемых в статье, поддерживая его в чистоте с помощью параметров синтаксиса раздел 4, called forever
, где он вызывает параметр синтаксиса прерывания abort
.