ttsiodras
ttsiodras

Reputation: 11268

Macro calling a function works in interpreter, fails in compiler (SBCL + CMUCL)

As suggested in a macro-related question I recently posted to SO, I coded a macro called "fast" via a call to a function (here is the standalone code in pastebin):

(defun main ()
  (progn
    (format t "~A~%" (+ 1 2 (* 3 4) (+ 5 (- 8 6))))
    (format t "~A~%" (fast (+ 1 2 (* 3 4) (+ 5 (- 8 6)))))))

This works in the REPL, under both SBCL and CMUCL:

$ sbcl
This is SBCL 1.0.52, an implementation of ANSI Common Lisp.
...
* (load "bug.cl")
22
22

$

Unfortunately, however, the code no longer compiles:

$ sbcl
This is SBCL 1.0.52, an implementation of ANSI Common Lisp.
...
* (compile-file "bug.cl")
...
;   during macroexpansion of (FAST (+ 1 2 ...)). Use *BREAK-ON-SIGNALS* to
;   intercept:
;
;    The function COMMON-LISP-USER::CLONE is undefined.

So it seems that by having my macro "fast" call functions ("clone","operation-p") at compile-time, I trigger issues in Lisp compilers (verified in both CMUCL and SBCL).

Any ideas on what I am doing wrong and/or how to fix this?

Upvotes: 2

Views: 1225

Answers (3)

ttsiodras
ttsiodras

Reputation: 11268

Never mind, I figured it out: I had to move the functions invoked by the macro (and therefore required during compilation) in a separate file, "compile-file" it first, "load" it, then "compile-file" the one with the macro.

Upvotes: 2

Rainer Joswig
Rainer Joswig

Reputation: 139411

Some remarks about your code.

  • multiple tests of an object for equality can be replaced by MEMBER

  • backquote with a following comma does nothing. You can just remove that.

  • you can ensure that your functions are available for a macro by a) moving these functions to an additional file and compile/load that before use of the macro, by b) using EVAL-WHENto inform the compiler to evaluate the definition of the functions or by c) adding the functions to the macro as local functions

Example:

(defmacro fast (&rest sexpr)
  (labels ((operation-p (x)
             (member x '(+ - * /)))
           (clone (sexpr)
             (if (consp sexpr)
                 (destructuring-bind (head . tail) sexpr
                   (if (operation-p head)
                       `(the fixnum (,head ,@(clone tail)))
                     (cons (clone head) (clone tail))))
               sexpr)))
    (car (clone sexpr))))

Note that this and your version of FAST are not complete code walkers. They recognize only simple function calls (and not the other Lisp constructs like LAMBDA, LET, FLET, LABELS, etc.).

Upvotes: 4

Vatine
Vatine

Reputation: 21288

Macro-expansion tie happens (typically) during compile time.

That means that any functions used during the macro expansion (note, not necessarily in the macro-expansion, the return value as it were) must be defined when the macro is encountered during compilation.

Upvotes: 1

Related Questions