Схема: Как расширить шаблон с несколькими переменными в правилах синтаксиса без скобок
Я пытаюсь написать макрос на схеме для стиля Picolisp let
выражения, назовем этот макрос let-slim
. Чтобы быть более сжатым (например, Picolisp), я хочу, чтобы их использование выглядело примерно так при объявлении только одной переменной
(let-slim var-name initial-value
(display var-name))
Или что-то вроде этого, чтобы объявить любое количество переменных (обратите внимание, что это псевдокод, на самом деле я бы не стал включать эллипсы)
(let-slim (var-name-1 initital-value-1
var-name-2 initital-value-2
...
var-name-n initital-value-n)
(+ var-name-1 var-name-2 ... var-name-n))
Первый вариант использования довольно тривиален для написания syntax-rules
шаблон соответствия для, но с последним я борюсь.
Это не работает, потому что только init
повторяется
(define-syntax let-slim
(syntax-rules ()
[(_ (var init ...) body ...)
(let ((var init) ...)
body ... )]))
Это не работает, потому что это считается неуместным эллипсисом.
(define-syntax let-slim
(syntax-rules ()
[(_ (var ... init ...) body ...)
(let ((var init) ...)
body ... )]))
И это не работает, потому что мне нужно использовать скобки в контрольной точке (что означает, что он абсолютно ничего не меняет по сравнению со встроенным let
)
(define-syntax let-slim
(syntax-rules ()
[(_ (var init) ...) body ...)
(let ((var init) ...)
body ... )]))
Итак, есть ли способ повторить 2 переменные в syntax-rules
без необходимости заключать их в скобки, или мне нужно использовать другую макросистему (т.е. syntax-case
или defmacro
)?
2 ответа
Это не оптимально делать с syntax-rules
, но поскольку это завершено по Тьюрингу, это можно сделать:
(define-syntax let-slim
(syntax-rules (pair)
((_ pair bindings () body)
(let bindings . body))
((_ pair (acc ...) (k v . rest) body)
(let-slim pair (acc ... (k v)) rest body))
((_ (elements ...) . body)
(let-slim pair () (elements ...) body))))
Это невозможно сделать за один раз с помощью синтаксических правил. ...
функция, но вы можете сделать это с помощью синтаксических правил, используя рекурсию:
(define-syntax let-slim
(syntax-rules ()
((let-slim (var-1 val-1 . rest) . body)
(let-slim var-1 val-1 (let-slim rest . body)))
((let-slim var val . body)
;; single binding case you already implemented
))
Единственная проблема заключается в том, что правила синтаксиса не могут сказать, что var должен быть символом. Вы не получите хороших сообщений об ошибках от такого макроса (например, если он используется с нечетным числом привязок var/val). Возможно, лучше реализовать этот макрос с помощью syntax-case. Причина, по которой это сложно реализовать, заключается в том, что это как бы нарушает идею использования пары скобок для каждого узла AST.