2014-02-18 4 views
6
generiert

Ich habe versucht, ein Makro zu schreiben, das unter anderem ein Compiler-Makro mit dem gleichen Namen generiert. Dies ist der minimale code Ich bin stecken:Makro, das Makroformular generiert in gebundene Variablen

(defmacro definline (name lambda-list &body body) 
    `(define-compiler-macro ,name ,lambda-list 
    `(let ,,(mapcar (lambda (v) ``(,',v ,,v)) lambda-list) 
     ,,@body))) 

Was ich will, ist so etwas wie diese:

(definline foobar (a b) (print "foobar") (+ a b)) 
;; Expands to 
(define-compiler-macro foobar (a b) 
    `(let ((a ,a) (b ,b)) 
    (print "foobar") (+ a b))) 

Aber ich kann nicht herausfinden, wie die let-Bindungen ((a ,a) (b ,b)) zu erzeugen. Das Problem, mit dem ich mich nicht herumschlagen konnte, ist, wie man das Compiler-Makro-Formular so generiert, dass der Inhalt der Lambda-Bindungen in der Erweiterung nicht notiert ist. Ich verstehe, wie man das manuell macht, aber ich bin nicht sicher, wie man das generisch für eine willkürliche Lambda-Liste tut.

Edit:

Nach etwas mehr Hantieren ich mit diesem kam. Was funktioniert. Aber, nun, es ist scheußlich.

(defmacro definline (name lambda-list &body body) 
    (read-from-string 
    (format nil "(define-compiler-macro ~S (~{~S~^ ~}) 
        `(let (~{(~S ,~S)~}) 
         ~{~S~}))" 
      name lambda-list (loop for l in lambda-list nconc (list l l)) body))) 

Antwort

3

Der Ansatz format mit funktioniert nicht, wenn ein Teil der I/O-verbundene Variablen geändert werden. Die Verwendung von format zum Generieren von Symbolnamen ist im Allgemeinen ziemlich spröde und nicht portabel. Diese ist ein hartes Problem zu lösen, und es könnte einfacher sein, es mit List-Konstruktion eher als Backquotes allein zu nähern. Zum Beispiel könnte in diesem Fall haben wir:

(defmacro definline (name variables &body body) 
    (list 'define-compiler-macro name variables 
     `(list* 'let (list ,@(mapcar (lambda (variable) 
             `(list (quote ,variable) ,variable)) 
           variables)) 
       ',body))) 

Dies funktioniert so, dass:

CL-USER> (pprint (macroexpand-1 '(definline foobar (a b) 
         (print "foobar") 
         (+ a b)))) 

(DEFINE-COMPILER-MACRO FOOBAR 
    (A B) 
    (LIST* 'LET (LIST (LIST 'A A) (LIST 'B B)) '((PRINT "foobar") (+ A B)))) 

was, es sei denn, ich bin etwas Verlesen, sollten die gleichen Ergebnisse wie:

(define-compiler-macro foobar (a b) 
    `(let ((a ,a) (b ,b)) 
    (print "foobar") 
    (+ a b))) 

Ich glaube nicht, dass es unbedingt möglich ist, die letztere Form nur mit Backquotes zu generieren. Das Problem ist, dass, da die Spezifikation nicht genau definieren, wie Backquote implementiert ist, wird es als so etwas wie

(define-compiler-macro foobar (a b) 
    (backquote (let ((a (unquote a)) 
        (b (unquote b))) 
       (print "foobar") 
       (+ a b))) 

nicht so einfach Wenn Ihre Implementierung nicht es so implementieren, dann könnte man eine Erweiterung schreiben das erzeugt diese Art von Ausgabe. Sofern Sie nicht eine solche Garantie von Ihrer Implementierung erhalten, glaube ich nicht, dass es eine Möglichkeit gibt, die "Komma-Variable", die Sie injizieren müssen, in die höhere Ebene zu bekommen. Es ist schwer, diesen Punkt klar zu machen, aber man könnte in diesem Versuch aussehen:

(defmacro definline (name variables &body body) 
    `(define-compiler-macro ,name ,variables 
    `(let ,',(mapcar (lambda (variable) 
         `(,variable (unquote ,variable))) 
         variables) 
     ,@',body))) 

, dass die Ergebnisse wie produzieren:

(DEFINE-COMPILER-MACRO FOOBAR 
    (A B) 
    '(LET ((A (UNQUOTE A)) (B (UNQUOTE B))) 
    (PRINT "foobar") 
    (+ A B))) 

Beachten Sie, dass SBCL schon klug genug ist, um die Backquote mit einem normalen zu ersetzen zitieren, da es nichts gibt, was innen nicht zitiert werden müsste. Das mapcar generiert das Formular, das in gespleißt wird, kann Code mit Kommas darin nicht generieren, da es nicht angegeben wird, wie diese Kommas implementiert werden, und laut 2.4.7 Comma "ist Komma ungültig, wenn es anders als in dem Körper eines backquote verwandt wird Ausdruck".Ich denke, das bedeutet, dass die beste Option ist so etwas wie:

(defmacro definline (name variables &body body) 
    `(define-compiler-macro ,name ,variables 
    `(let ,(mapcar 'list 
        ',variables 
        (list ,@variables)) 
     ,@',body))) 

Die Erweiterung dieser wird unter verschiedenen Implementierungen, anders sein, aber in SBCL es ist:

(DEFINE-COMPILER-MACRO FOOBAR (A B) 
    `(LET (SB-IMPL::BACKQ-COMMA (MAPCAR 'LIST '(A B) (LIST A B))) 
    (PRINT "foobar") 
    (+ A B))) 

In CCL erhalten Sie:

(DEFINE-COMPILER-MACRO FOOBAR (A B) 
    (LIST* 'LET 
     (LIST* (MAPCAR 'LIST '(A B) (LIST A B)) 
       '((PRINT "foobar") (+ A B))))) 

In CLISP:

(DEFINE-COMPILER-MACRO FOOBAR (A B) 
(CONS 'LET 
    (CONS (MAPCAR 'LIST '(A B) (LIST A B)) '((PRINT "foobar") (+ A B))))) 
+0

Aufrufen der generierten Compiler-Makrofunktion, z. "(funcall (compiler-macro-function 'foobar)' (foobar 10 11)())" kann ein nützliches Werkzeug sein, um die Ausgabe hier zusätzlich zu macroexpand-1 zu verifizieren. –

+0

Danke für die ausgezeichnete Erklärung! – asm

Verwandte Themen