As any other macro.
cl-user> (setf *print-right-margin* 80)
80
cl-user> (pprint (macroexpand '(do ((i 1 (1+ i))
(j 10 (1- j)))
((< j i) (print 'done))
(print i))))
(block nil
(let ((i 1) (j 10))
(tagbody (go #1=#:g3905)
#2=#:g3904 (tagbody (print i))
(psetq i (1+ i) j (1- j))
#1# (unless (< j i) (go #2#)))
(print 'done))); No value
But as you can see, to understand do you have to understand more, and
more complex, lower level constructs.
Another thing you could do, is to take the definition of the macro, and
evaluate it with my stepper, so that you can step into it.
cl-user> (ql:quickload :com.informatimago.common-lisp.lisp.stepper)
To load "com.informatimago.common-lisp.lisp.stepper":
Load 1 ASDF system:
com.informatimago.common-lisp.lisp.stepper
; Loading "com.informatimago.common-lisp.lisp.stepper"
[package com.informatimago.common-lisp.lisp.stepper.internal]
[package com.informatimago.common-lisp.lisp.stepper].
..
(:com.informatimago.common-lisp.lisp.stepper)
cl-user> (defpackage :testdo (:use :cl-stepper) (:shadow :do))
#<Package "TESTDO">
cl-user> (in-package :testdo)
#<Package "TESTDO">
testdo> (eval-when (:compile-toplevel :load-toplevel :execute)
(defun do-loop (binder setter env var-init-steps end-test result body)
(let ((toptag (gensym))
(testtag (gensym)))
(multiple-value-bind (forms decls) (ccl::parse-body body env nil)
`(block nil
(,binder ,(ccl::do-let-vars var-init-steps)
,@decls
(tagbody ; crocks-r-us.
(go ,testtag)
,toptag
(tagbody
,@forms)
(,setter ,@(ccl::do-step-vars var-init-steps))
,testtag
(unless ,end-test
(go ,toptag)))
,@result)))))
)
do-loop
testdo> (defmacro do (&environment env var-init-steps (&optional end-test &rest result) &body body)
"DO ({(Var [Init] [Step])}*) (Test Exit-Form*) Declaration* Form*
Iteration construct. Each Var is initialized in parallel to the value of the
specified Init form. On subsequent iterations, the Vars are assigned the
value of the Step form (if any) in parallel. The Test is evaluated before
each evaluation of the body Forms. When the Test is true, the Exit-Forms
are evaluated as a PROGN, with the result being the value of the DO. A block
named NIL is established around the entire expansion, allowing RETURN to be
used as an alternate exit mechanism."
(do-loop 'let 'psetq env var-init-steps end-test result body))
do
testdo> (step (progn (do ((i 1 (1+ i))
(j 10 (1- j)))
((< j i) (print 'done))
(print i)))
:trace)
(Will evaluate (progn (do (# #) (# #) (print i)))
(Will evaluate (do ((i 1 #) (j 10 #)) ((< j i) (print #)) (print i))
(Will evaluate (block nil (let (# #) (tagbody # #:g4994 # # #:g4995 #) (print #)))
(Will evaluate (let ((i 1) (j 10)) (tagbody (go #1=#:g4995) #:g4994 (tagbody #) (psetq i # j #) #1# (unless # #)) (print 'done))
(--> 1)
(--> 10)
(Bind i to 1)
(Bind j to 10)
(Will evaluate (tagbody (go #1=#:g4995) #2=#:g4994 (tagbody (print i)) (psetq i (1+ i) j (1- j)) #1# (unless (< j i) (go #2#)))
(Will evaluate (go #:g4995)
(Passed tag #:g4995)
(Will evaluate (unless (< j i) (go #:g4994))
(Will evaluate (go #:g4994)
(Passed tag #:g4994)
(Will evaluate (tagbody (print i))
(Will evaluate (print i)
(i ==> 1)
1
Evaluation of (print i) returned one result
==> 1)
Evaluation of (tagbody (print i)) returned one result
==> nil)
(Will evaluate (psetq i (1+ i) j (1- j))
Evaluation of (psetq i (1+ i) j (1- j)) returned one result
==> nil)
(Passed tag #:g4995)
(Will evaluate (unless (< j i) (go #:g4994))
(Will evaluate (go #:g4994)
(Passed tag #:g4994)
(Will evaluate (tagbody (print i))
(Will evaluate (print i)
(i ==> 2)
2
Evaluation of (print i) returned one result
==> 2)
Evaluation of (tagbody (print i)) returned one result
==> nil)
(Will evaluate (psetq i (1+ i) j (1- j))
Evaluation of (psetq i (1+ i) j (1- j)) returned one result
==> nil)
(Passed tag #:g4995)
(Will evaluate (unless (< j i) (go #:g4994))
(Will evaluate (go #:g4994)
(Passed tag #:g4994)
(Will evaluate (tagbody (print i))
(Will evaluate (print i)
(i ==> 3)
3
Evaluation of (print i) returned one result
==> 3)
Evaluation of (tagbody (print i)) returned one result
==> nil)
(Will evaluate (psetq i (1+ i) j (1- j))
Evaluation of (psetq i (1+ i) j (1- j)) returned one result
==> nil)
(Passed tag #:g4995)
(Will evaluate (unless (< j i) (go #:g4994))
(Will evaluate (go #:g4994)
(Passed tag #:g4994)
(Will evaluate (tagbody (print i))
(Will evaluate (print i)
(i ==> 4)
4
Evaluation of (print i) returned one result
==> 4)
Evaluation of (tagbody (print i)) returned one result
==> nil)
(Will evaluate (psetq i (1+ i) j (1- j))
Evaluation of (psetq i (1+ i) j (1- j)) returned one result
==> nil)
(Passed tag #:g4995)
(Will evaluate (unless (< j i) (go #:g4994))
(Will evaluate (go #:g4994)
(Passed tag #:g4994)
(Will evaluate (tagbody (print i))
(Will evaluate (print i)
(i ==> 5)
5
Evaluation of (print i) returned one result
==> 5)
Evaluation of (tagbody (print i)) returned one result
==> nil)
(Will evaluate (psetq i (1+ i) j (1- j))
Evaluation of (psetq i (1+ i) j (1- j)) returned one result
==> nil)
(Passed tag #:g4995)
(Will evaluate (unless (< j i) (go #:g4994))
Evaluation of (unless (< j i) (go #:g4994)) returned one result
==> nil)
Evaluation of (tagbody (go #1=#:g4995) #2=#:g4994 (tagbody (print i)) (psetq i (1+ i) j (1- j)) #1# (unless (< j i) (go #2#))) returned one result
==> nil)
(Will evaluate (print 'done)
('done ==> done)
done
Evaluation of (print 'done) returned one result
==> done)
Evaluation of (let ((i 1) (j 10)) (tagbody (go #1=#:g4995) #:g4994 (tagbody #) (psetq i # j #) #1# (unless # #)) (print 'done)) returned one result
==> done)
Evaluation of (block nil (let (# #) (tagbody # #:g4994 # # #:g4995 #) (print #))) returned one result
==> done)
Evaluation of (do ((i 1 #) (j 10 #)) ((< j i) (print #)) (print i)) returned one result
==> done)
Evaluation of (progn (do (# #) (# #) (print i))) returned one result
==> done)
done
testdo>
--
__Pascal Bourguignon__
http://www.informatimago.com/
A bad day in () is better than a good day in {}.