Полностью разверните макрокоманду
Я хотел бы узнать внутренности Lisp, поэтому я хочу посмотреть, как все реализовано.
Например,
(macroexpand '(loop for i upto 10 collect i))
дает мне (в SBCL)
(BLOCK NIL
(LET ((I 0))
(DECLARE (TYPE (AND NUMBER REAL) I))
(SB-LOOP::WITH-LOOP-LIST-COLLECTION-HEAD (#:LOOP-LIST-HEAD-1026
#:LOOP-LIST-TAIL-1027)
(SB-LOOP::LOOP-BODY NIL
(NIL NIL (WHEN (> I '10) (GO SB-LOOP::END-LOOP)) NIL)
((SB-LOOP::LOOP-COLLECT-RPLACD
(#:LOOP-LIST-HEAD-1026 #:LOOP-LIST-TAIL-1027)
(LIST I)))
(NIL (SB-LOOP::LOOP-REALLY-DESETQ I (1+ I))
(WHEN (> I '10) (GO SB-LOOP::END-LOOP)) NIL)
((RETURN-FROM NIL
(SB-LOOP::LOOP-COLLECT-ANSWER
#:LOOP-LIST-HEAD-1026)))))))
Но LOOP-BODY, WITH-LOOP-LIST-COLLECTION-HEAD и т.д. все еще являются макросами. Как я могу полностью расширить форму макроса?
Ответы
Ответ 1
Чтобы увидеть полное расширение, нужно пройти форму Lisp на всех уровнях и развернуть их. Для этого необходимо, чтобы этот так называемый ходок кода понимал синтаксис Lisp (а не только синтаксис s-выражения). Например, в (lambda (a b) (setf a b))
список (a b)
является списком параметров и не должен быть расширен макросом.
Различные общие реализации Lisp предоставляют такой инструмент. Ответ 6502 означает MACROEXPAND-ALL
, который предоставляется SBCL.
Если вы используете среду разработки, она обычно предоставляется в виде команды:
-
SLIME: M-x slime-macroexpand-all с C-c M-m
-
LispWorks: меню Expression > Прогулка или M-x Walk Form, короче M-Sh-m.
Ответ 2
Другие ответы отлично подходят для вас, но вы говорите, что хотите увидеть, как все реализовано.
Многие макросы (как вы уже знаете) реализованы с использованием макросов, а macroexpand-all очень полезен, но вы можете потерять контекст того, какой макрос отвечает за какие изменения.
Один хороший средний уровень (если вы используете слизь) - использовать slime-expand-1 (C-c Enter), который показывает, что расширение - это еще один буфер. Затем вы можете использовать slime-expand-1 внутри этого нового буфера для расширения макросов на месте.
Это позволяет вам ходить по дереву, когда вы читаете, а также использовать отмену, чтобы снова закрыть расширения.
Для меня это был бог-посыл в понимании макросов других людей. Надеюсь, это тоже поможет вам, получайте удовольствие!
Ответ 3
Вы можете попробовать использовать MACROEXPAND-ALL
, но то, что вы можете получить, не обязательно полезно.
В чем-то вроде LOOP
реальное мясо - это сам макрос, а не сгенерированный код.
Ответ 4
(Примечание. Если вы не заинтересованы в переносимости, SBCL предоставляет macroexpand-all
, который будет делать то, что вам нужно. Если вы после портативного решения, прочитайте дальше...)
Быстрое и грязное решение будет macroexpand
самой формой, а затем рекурсивно macroexpand
всем, кроме первого элемента результирующего списка. Это несовершенное решение; он будет полностью сбой в момент, когда он попытается обработать привязки let
(первый аргумент let
, список привязок, не предназначен для макрорасширения, но этот код все равно сделает это.)
;;; Quick-and-dirty macroexpand-all
(defun macroexpand* (form)
(let ((form (macroexpand form)))
(cons (car form) (mapcar #'macroexpand (cdr form)))))
Более полное решение будет рассматривать специальные формы специально, а не макрорасширение их неоцененных аргументов. Я мог бы при необходимости обновить такое решение.