Home > Enterprise >  Invoking Common Lisp macros systematically with varying expressions
Invoking Common Lisp macros systematically with varying expressions

Time:01-30

I am learning Common Lisp (SBCL).
I want to create a facility to invoke two (or more) macros with several similar expressions that differ only in some parameters.

I would like to define the base of the expression, then modify it with the parameters I supply. For this a lambda function definition came to mind.

As far as I know, there is no analogue to funcall for macros, so I've also wrapped the macros in lambdas.

I feel like I'm overcomplicating with with all these lambda-s and funcall-s. Is there a more elegant way?

The macros are from an external lib, so I'd prefer not to modify them.
(Specifically, the fiveam testing library's finishes and signals.)

Here is a sample code:

(defmacro macro1 (body) ())
(defmacro macro2 (body) ())

(defun check-expr-with-args (do-m func args)
  (dolist (arg args)
    (format t "~a " arg)
    (funcall do-m (lambda () (funcall func arg)))))

(let ((test-expr 
        #'(lambda (val) (format t "~a" val)))
      (cases (list
               (list #'(lambda (func) ( macro1 (funcall func))) 
                     (list 1 2 3 4 5))
               (list #'(lambda (func) ( macro2 (funcall func)))
                     (list -4 -5 -6 -7 -8 -9)))))
  (dolist (c cases)
    (check-expr-with-args (first c) test-expr (second c))))

Originally I've tried to pass the macro names to my check-expr-with-args function, and the expressions in quoted form, relying on lexical scoping for the parameter insertion. That didn't work out.

CodePudding user response:

I think you can write a wrapper macro that produces code that invokes macro1 (and macro2). For example here I'm defining m1 that takes a test expression and another expression that is expected to evaluates at runtime to a list of values.

(defmacro m1 (test-expr input-expr)
  (let ((arg (gensym)))
    `(dolist (,arg ,input-expr)
       (macro1 ,test-expr ,arg))))

Both test-expr and input-expr are injected in a dolist expression, which binds a variable named arg (a fresh symbol introduced with gensym, to avoid capturing any existing one in the surrounding context).

For example:

(m1 (some-test-p) (list 1 2 3 4))

The above expands as:

(DOLIST (#:G1909 (LIST 1 2 3 4)) 
  (MACRO1 (SOME-TEST-P) #:G1909))

The resulting expression contains MACRO1, which will also be expanded. But it is now wrapped in an expression that iterates over some list computed at runtime (here, it could be a constant but you could replace it with any other expression).

  • Related