Введение именованной переменной с правилами синтаксиса
Я пытаюсь написать супер-крошечную объектно-ориентированную систему с синтаксическими правилами, в основном, просто чтобы изучить ее. Во всяком случае, я пытаюсь ввести переменную "это". Вот что я хотел бы сделать:
(oo-class Counter
(
(attr value 0)
(attr skip 1)
)
(
(method (next) (set! value (+ value skip)) value)
(method (nextnext) (this 'next) (this 'next))
(method (set-value newval) (set! value newval))
(method (set-skip newskip) (set! skip newskip))
)
)
(define c (Counter))
((c 'set-value) 23)
((c 'next))
((c 'nextnext))
Я могу заставить все работать, кроме "этого". Кажется, что синтаксические правила не позволяют вводить переменные. Я думал, что смогу получить это, определив его как один из литералов в синтаксических правилах, но это, похоже, не работает.
Ниже моя объектно-ориентированная система:
(define-syntax oo-class
(syntax-rules (attr method this)
(
(oo-class class-name
((attr attr-name initial-val) ...)
((method (meth-name meth-arg ...) body ...) ...))
(define class-name
(lambda ()
(letrec
(
(this #f)
(attr-name initial-val)
...
(funcmap
(list
(cons (quote meth-name) (cons (lambda (meth-arg ...) body ...) '()))
...
)
)
)
(set! this (lambda (methname)
(cadr (assoc methname funcmap))
))
this
)
)
)
)
)
)
Это работает для всего, кроме "nextnext", которое выдает ошибку, когда пытается ссылаться на "this".
Это правильный способ сделать это? Есть ли другой способ сделать это? Я понимаю, что это немного негигиенично, но разве это не является частью определения литералов?
Я пробовал это в Chicken Scheme, а также в DrRacket в режиме R5RS (другие режимы жалуются на "это").
Ниже весь файл. Вы можете запустить его на Chicken с помощью всего лишь "csi object.scm"
https://gist.github.com/johnnyb/211e105882248e892fa485327039cc90
Я также пытался использовать let-синтаксис и использовать (this) в качестве спецификатора синтаксиса для ссылки на (this) переменную. Но, насколько я мог судить, это не позволяло мне напрямую получить доступ к переменной моего собственного создания в рамках переписывания синтаксиса.
БОНУСНЫЙ ВОПРОС: Какой простой способ увидеть результат преобразования правил синтаксиса для отладки? Есть ли какой-нибудь способ заставить цыпленка (или что-то еще) сделать преобразование и выплюнуть результат? Я попробовал кое-что на DrRacket, но он не работает в режиме R5RS.
1 ответ
Я понимаю, что это немного негигиенично, но разве это не является частью определения литералов?
Нет, литералы существуют, поэтому вы можете сопоставлять буквально по ключевым словам, как, например, =>
или else
в cond
пункт. Это все еще гигиенично, потому что если =>
или же else
лексически связан с некоторым значением, которое имеет приоритет:
(let ((else #f))
(cond (else (display "hi!\n")))) ;; Will not print
Теперь вы можете написать очень утомительный макрос, который соответствует this
на любом возможном месте и уровне вложенности в расширении, но это никогда не будет полным, и оно также не будет лексическим.
Можно сделать то, что вы пытаетесь сделать, используя то, что стало известно как извлечение Петрофски, но это полный и полный взлом и злоупотребление синтаксическими правилами, и он не работает (последовательно) при наличии модулей в разных реализациях (например, именно в CHICKEN у нас была жалоба на то, что мы случайно "сломали" эту функцию).
Я бы предложил написать макрос синтаксических правил, который принимает идентификатор на входе, который будет привязан к текущему объекту, а затем написать один тривиальный негигиеничный макрос, который вызывает этот другой макрос с жестко закодированным идентификатором. this
в качестве ввода.
Какой простой способ увидеть результат преобразования правил синтаксиса для отладки? Есть ли какой-нибудь способ заставить цыпленка (или что-то еще) сделать преобразование и выплюнуть результат? Я попробовал кое-что на DrRacket, но он не работает в режиме R5RS.
В CSI, вы можете использовать ,x (macro-call)
, но он сделает только один уровень расширения.
Обычный трюк, который работает в каждой реализации Scheme, - это изменение определения макроса для цитирования его выходных данных. Таким образом, это расширяется, чтобы не (foo)
но '(foo)
, Таким образом, вы можете просто вызвать макрос в REPL и сразу увидеть его результат.