2013-05-16 62 views
11

Tôi muốn tìm hiểu nội bộ của Lisp, vì vậy tôi muốn xem mọi thứ được triển khai như thế nào.Mở rộng một biểu mẫu macro hoàn toàn

Ví dụ,

(macroexpand '(loop for i upto 10 collect i)) 

mang lại cho tôi (trong 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))))))) 

Nhưng LOOP-BODY, VỚI-LOOP-LIST-THU-HEAD, vv vẫn là macro. Làm thế nào tôi có thể mở rộng một biểu mẫu macro hoàn toàn?

Trả lời

13

Để xem phần mở rộng đầy đủ, bạn cần phải đi theo biểu mẫu Lisp trên tất cả các cấp và mở rộng chúng. Đối với điều này nó là cần thiết mà cái gọi là mã walker hiểu cú pháp Lisp (và không chỉ là cú pháp s-expression). Ví dụ: (lambda (a b) (setf a b)), danh sách (a b) là danh sách tham số và không được mở rộng macro.

Triển khai Lisp phổ biến khác nhau cung cấp công cụ như vậy. Câu trả lời của 6502 đề cập đến MACROEXPAND-ALL do SBCL cung cấp.

Nếu bạn sử dụng một môi trường phát triển, người ta thường cung cấp như một lệnh:

  • SLIME: Mx chất nhờn-macroexpand-tất cả với Cc Mm

  • LispWorks: Menu Biểu thức>Đi bộ hoặc Biểu mẫu đi bộ Mx, ngắn hơn M-Sh -m.

+0

Khi tính di động là một mối quan tâm, hãy thử Johnnmlin's macroexpand-dammit, nó hoạt động bên ngoài SBCL và thực hiện điều tương tự (mặc dù định nghĩa macrolet dường như được thay thế bằng progn là không cần thiết). –

3

Bạn có thể thử sử dụng MACROEXPAND-ALL nhưng những gì bạn có thể nhận được không nhất thiết phải hữu ích.

Trong thứ gì đó như LOOP thịt thực là chính macro, chứ không phải mã được tạo.

1

(Lưu ý: Nếu bạn không quan tâm đến tính di động, cung cấp SBCL macroexpand-all, mà sẽ làm những gì bạn đang sau Nếu bạn sau khi một giải pháp di động, hãy đọc tiếp ....)

Các nhanh giải pháp-và-bẩn sẽ là macroexpand chính biểu mẫu, sau đó đệ quy macroexpand tất cả trừ phần tử đầu tiên của danh sách kết quả. Đây là một giải pháp không hoàn hảo; nó sẽ thất bại hoàn toàn vào thời điểm nó cố gắng xử lý các ràng buộc của let (đối số đầu tiên cho let, danh sách các ràng buộc, không có nghĩa là macro đã được mở rộng, nhưng mã này sẽ làm việc đó).

;;; Quick-and-dirty macroexpand-all 
(defun macroexpand* (form) 
    (let ((form (macroexpand form))) 
    (cons (car form) (mapcar #'macroexpand (cdr form))))) 

Một giải pháp hoàn chỉnh hơn sẽ xem xét các biểu mẫu đặc biệt một cách đặc biệt, chứ không phải giải quyết các đối số chưa được đánh giá của chúng. Tôi có thể cập nhật với một giải pháp như vậy, nếu muốn.

8

Các câu trả lời khác là tuyệt vời cho bạn nhưng bạn nói bạn muốn xem mọi thứ được triển khai như thế nào.

Nhiều macro (như bạn đã biết) được triển khai bằng macro và trong khi macroexpand-all rất hữu ích nhưng bạn có thể mất bối cảnh macro nào chịu trách nhiệm về thay đổi nào.

Một nền tảng trung bình đẹp (nếu bạn đang sử dụng chất nhờn) là sử dụng slime-expand-1 (C-c Enter) cho thấy việc mở rộng là một bộ đệm khác. Sau đó, bạn có thể sử dụng slime-expand-1 bên trong bộ đệm mới này để mở rộng macro tại chỗ. Điều này cho phép bạn đi bộ cây mở rộng khi bạn đọc và cũng để sử dụng hoàn tác để đóng mở rộng một lần nữa.

Đối với tôi, điều này đã giúp các bạn hiểu rõ hơn về các macro của người khác. Hy vọng điều này sẽ giúp bạn quá, vui chơi!

+1

Đây là một phương pháp tuyệt vời. Slimv cũng có luồng công việc này, chỉ cần fyi cho người dùng vim + lisp –

Các vấn đề liên quan