Схема: Как расширить шаблон с несколькими переменными в правилах синтаксиса без скобок

Я пытаюсь написать макрос на схеме для стиля 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.

Другие вопросы по тегам