Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LISP: how to trace macros

This is probably a stupid question, but I'm walking through the PG lisp book, and I wanted to step through some example macros that he provides with actual values, for instance:

(defmacro our-let (binds &body body) 
    `(
        (lambda ,(
                mapcar #'(lambda (x) (if (consp x) (car x) x)) binds
            )
                ,@body
        )
        ,@(mapcar #'(lambda (x) (if (consp x) (cadr x) nil)) binds)
    )
)

I naively tried to run (trace our-let) and then (our-let ((x 1) (y 2)) (+ x y)) but I'm getting an error, can't use encapsulation to trace anonymous function #<FUNCTION (MACRO-FUNCTION OUR-LET) {22675BBB}>. Also not sure how to best put print statements into the lambdas. What's the best way to debug this macro/output how it's processing inputs?

EDIT(1): I had the incorrect formatting for macroexpand, which works.

like image 542
lispquestions Avatar asked Jan 01 '23 10:01

lispquestions


2 Answers

Actually being able to trace macros is not very common in Common Lisp implementations. Compilers will typically expand the macro forms during compilation.

A few implementations support it though - which makes sense when they also support a Lisp interpreter, which runs the actual source. Among those are LispWorks and CLISP.

Here using the code from Sylwester in CLISP:

  i i i i i i i       ooooo    o        ooooooo   ooooo   ooooo
  I I I I I I I      8     8   8           8     8     o  8    8
  I  \ `+' /  I      8         8           8     8        8    8
   \  `-+-'  /       8         8           8      ooooo   8oooo
    `-__|__-'        8         8           8           8  8
        |            8     o   8           8     o     8  8
  ------+------       ooooo    8oooooo  ooo8ooo   ooooo   8

Welcome to GNU CLISP 2.49.93+ (2018-02-18) <http://clisp.org/>

Copyright (c) Bruno Haible, Michael Stoll 1992-1993
Copyright (c) Bruno Haible, Marcus Daniels 1994-1997
Copyright (c) Bruno Haible, Pierpaolo Bernardi, Sam Steingold 1998
Copyright (c) Bruno Haible, Sam Steingold 1999-2000
Copyright (c) Sam Steingold, Bruno Haible 2001-2018

Type :h and hit Enter for context help.

[1]> (defmacro our-let ((&rest bindings) &body body) 
       (let ((names (mapcar #'(lambda (x) (if (consp x) (car x) x)) bindings))
             (exprs (mapcar #'(lambda (x) (if (consp x) (cadr x) nil)) bindings)))
          `((lambda ,names ,@body) ,@exprs)))
OUR-LET
[2]> (trace our-let)
;; Tracing macro OUR-LET.
(OUR-LET)
[3]> (dotimes (i 3)
       (our-let ((x (* i 10)))
         (+ x 3)))
1. Trace: (OUR-LET ((X (* I 10))) (+ X 3))
1. Trace: OUR-LET ==> ((LAMBDA (X) (+ X 3)) (* I 10))
1. Trace: (OUR-LET ((X (* I 10))) (+ X 3))
1. Trace: OUR-LET ==> ((LAMBDA (X) (+ X 3)) (* I 10))
1. Trace: (OUR-LET ((X (* I 10))) (+ X 3))
1. Trace: OUR-LET ==> ((LAMBDA (X) (+ X 3)) (* I 10))
NIL
[4]> 
like image 67
Rainer Joswig Avatar answered Jan 05 '23 16:01

Rainer Joswig


How you debug it:

(macroexpand-1 '(our-let ((x 1) (y 2)) (+ x y)))
; ==> ((lambda (X Y) (+ X Y)) 1 2) 
; ==> t

BTW your formatting is not good. Here is how it can look:

(defmacro our-let (binds &body body) 
  `((lambda ,(mapcar #'(lambda (x) (if (consp x) (car x) x)) binds)
      ,@body)
    ,@(mapcar #'(lambda (x) (if (consp x) (cadr x) nil)) binds)))

Or I would prefer:

(defmacro our-let ((&rest bindings) &body body) 
  (let ((names (mapcar #'(lambda (x) (if (consp x) (car x) x)) bindings))
        (exprs (mapcar #'(lambda (x) (if (consp x) (cadr x) nil)) bindings)))
    `((lambda ,names ,@body) ,@exprs)))
like image 20
Sylwester Avatar answered Jan 05 '23 16:01

Sylwester