macroscommon-lisplisp-macros

How to convert this Common Lisp function into a macro?


I am using SBCL, Slime, and Emacs to develop in Common Lisp.

I have this function:

(defun build-cond-action-pairs (&rest var)
  (labels ((aux (xs-left accu)
             (cond ((null (cddr xs-left))
                    (append accu (list (list (first xs-left)
                                             (second xs-left)))))
                   (t (aux (cddr xs-left)
                           (append accu (list (list (first xs-left)
                                                    (second xs-left)))))))))
    (aux var nil)))

I also defined these two variables:

CL-USER>(defparameter var-a 1)
VAR-A

CL-USER> (defparameter var-b 1)
VAR-B

When I call the function with:

CL-USER> (build-cond-action-pairs "fish are cool" (incf var-a) "amphibians are cool" (incf var-b))

As expected, the arguments are evaluated:

(("fish are cool" 2) ("amphibians are cool" 2))

I want to transform this function into a macro. Hence, the arguments will not be evaluated.

The desired output result would be:

(("fish are cool" (incf var-a)) ("amphibians are cool" (incf var-b)))

I tried with:


CL-USER> (defmacro macro-build-cond-action-pairs (&rest var)
  `(labels ((aux (,xs-left ,accu)
             (cond ((null (cddr ,xs-left))
                    (append ,accu (list (list (first ,xs-left)
                                             (second ,xs-left)))))
                   (t (aux (cddr ,xs-left)
                           (append ,accu (list (list (first ,xs-left)
                                                    (second ,xs-left)))))))))
    (aux ,var nil)))

But, it does not work:

; in: DEFMACRO MACRO-BUILD-COND-ACTION-PAIRS
;     `(LABELS ((AUX (,XS-LEFT ,ACCU)
;                 (COND (# #) (T #))))
;        (AUX ,VAR NIL))
; --> SB-IMPL::|List| SB-IMPL::|List| SB-IMPL::|List| 
; ==>
;   (SB-IMPL::|List| XS-LEFT ACCU)
; 
; caught WARNING:
;   undefined variable: COMMON-LISP-USER::ACCU
; 
; caught WARNING:
;   undefined variable: COMMON-LISP-USER::XS-LEFT
; 
; compilation unit finished
;   Undefined variables:
;     ACCU XS-LEFT
;   caught 2 WARNING conditions
MACRO-BUILD-COND-ACTION-PAIRS

Feels like a package (or namespace problem). Maybe the root is the labels part. I do not know how to solve it.

How can I fix this?

Thanks.


Solution

  • @Gwang-JinKim presented a solution (thanks for the help!). However, his solution changes the recursive approach described in my original answer.

    I ended up finding a way to fix the macro keeping it very similar to the original question. Basically, it was necessary to remove some commas and to insert a (quote ...) before the tail call.

    Check it out:

    CL-USER> (defmacro macro-build-cond-action-pairs (&rest var)
            `(labels ((aux (xs-left accu)
                        (cond ((null (cddr xs-left))
                               (append accu (list (list (first xs-left)
                                                         (second xs-left)))))
                              (t (aux (cddr xs-left)
                                      (append accu (list (list (first xs-left)
                                                                (second xs-left)))))))))
               (aux (quote ,var) nil)))
    

    It works:

    CL-USER> (macro-build-cond-action-pairs "fish are cool" (incf var-a) "amphibians are cool" (incf var-b))
    
    (("fish are cool" (INCF VAR-A)) ("amphibians are cool" (INCF VAR-B)))