Полностью разверните макрокоманду

Я хотел бы узнать внутренности 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)))))

Более полное решение будет рассматривать специальные формы специально, а не макрорасширение их неоцененных аргументов. Я мог бы при необходимости обновить такое решение.