3 ;; Author: Ray Comas (comas@math.lsa.umich.edu)
5 ;; Modifications and corrections by Tom Almy
6 ;; The program did not correctly handle RETURN (as reported by Martin
7 ;; Glanvill, mcg@waikato.ac.nz). In the process of fixing the the
8 ;; problem it was discovered that the nexting printout did not work
9 ;; properly for all return, return-from, throw, and many cases of go.
10 ;; This version has been fixed for hopefully all of the above, although
11 ;; go will still not produce proper printout if the jump is outside the
12 ;; most enclosing tagbody, and the tag arguments of catch/throw must
13 ;; either be symbols or quoted symbols. I'm making no attempt here to
14 ;; correctly handle tracing of unwind-protect, either!
15 ;; Modifications marked "TAA"
17 ;;-----------------------------------------
20 ;; Function : Eval-hook-function
22 ;; Modifcation :- MCG 5/5/93
24 ;; "is-brk-in-form" function added to look in advance
25 ;; to see if any break points are in the current form.
26 ;; If not, then the stepper will step over the form
27 ;; without evaluating the sub-forms within the current form
28 ;; (as original did); if break point found then it steps into
30 ;; The Advantage is when you have a break point at the end of
31 ;; a prog with massive amounts of DO loops, you don't want to waste
32 ;; time stepping into the do loop!
33 ;; Also I've modified it for use on COMMON LISP and XLISP
34 ;; See notes at bottom.
35 ;; Problems: in CL, step into LOOP's/ PROGN's before
36 ;; excuting the "g" command!
37 ;; Future Updates : further investigation of LOOPS and PROGn's as above.
39 ;; Modification: TAA 5/5/93
41 ;; I made the Common Lisp vs. Xlisp choice automatic via conditional
42 ;; compilation (Gee, I was hoping to find a good use for this feature!)
44 ;; Modifications for use with XLISP-STAT:
45 ;; moved step to XLISP package
46 ;; removed conditionals
51 (export '(step *stepper-depth
* *stepper-length
*))
53 (defpackage "TOOLS" (:use
"XLISP"))
58 (defmacro while
(test &rest forms
) `(do () ((not ,test
)) ,@forms
))
60 (defparameter *hooklevel
* 0) ;create the nesting level counter.
61 (defvar *stepper-depth
* 3) ;create depth counter
62 (defvar *stepper-length
* 3) ;create length counter
63 (defparameter *fcn
* '*all
*) ;create "one-shot" breakpoint specifier
64 (defvar *steplist
* nil
) ;create breakpoint list
65 (defparameter *steptrace
* '(t . t
)) ;create stepping flags
66 (defparameter *callist
* nil
) ;create call list for backtrace
69 ; this macro invokes the stepper - MCG 5/5/93 step -> usr-step , CL mod.
70 (defmacro xlisp
::step
(form &aux
(val (gensym)))
72 (setq *hooklevel
* 0 ;init nesting counter
73 *fcn
* '*all
* ;init break-point specifier
75 (setq *callist
* (list (car ',form
))) ;init call list
78 (princ *hooklevel
* *debug-io
*)
79 (princ " >==> " *debug-io
*)
80 (prin1 ',form
*debug-io
*) ;print the form
81 (setq ,val
(evalhook ',form
;eval, and kick off stepper
86 (princ *hooklevel
* *debug-io
*) ;print returned value
87 (princ " <==< " *debug-io
*)
88 (prin1 ,val
*debug-io
*)
92 (defun eval-hook-function (form env
&aux val cmd
)
93 (setq *hooklevel
* (1+ *hooklevel
*)) ;incr. the nesting level
94 (cond ((consp form
) ;if interpreted function ...
95 (step-add-level form env
) ;; add to *call-list* TAA
97 (loop ;repeat forever ...
98 ;check for a breakpoint
99 (when (and (not (equal *fcn
* '*all
*))
100 (not (equal *fcn
* (car form
)))
101 (not (and (numberp *fcn
*) (>= *fcn
* *hooklevel
*))))
102 (unless (and *fcn
* (member (car form
) *steplist
*))
104 ;no breakpoint reached -- continue
105 (setf (cdr *steptrace
*) nil
)
106 (when (car *steptrace
*)
107 (setf (cdr *steptrace
*) t
)
108 (step-print-compressed form
))
111 ((is-brk-in-form form
*steplist
*)
116 (t (setq val
(list form nil nil env
))))
121 ;breakpoint reached -- fix things & get a command
122 (step-print-compressed form
)
123 (setf (cdr *steptrace
*) t
)
124 (setq *fcn
* '*all
*) ;reset breakpoint specifier
125 (princ " :" *debug-io
*) ;prompt user
127 (setq cmd
;get command from user
129 ;process user's command
131 ((or (eql cmd
#\n) (eql cmd
#\Space
)) ;step into function
137 ((or (eql cmd
#\s
) ;step over function
140 ) ;; Added check for control-M TAA
141 (setq val
(list form nil nil env
))
143 ((eql cmd
#\g
) ;go until breakpt. reached
150 ((eql cmd
#\w
) ;backtrace
152 ((eql cmd
#\h
) ;display help
154 ((eql cmd
#\p
) ;pretty-print form
156 (pprint form
*debug-io
*))
157 ((eql cmd
#\f) ;set function breakpoint
158 (princ "Go to fn.: " *debug-io
*)
159 (setq *fcn
* (read *debug-io
*))
161 ((eql cmd
#\u
) ;go up one level
162 (setq *fcn
* (1- *hooklevel
*)))
163 ((eql cmd
#\b) ;set breakpoint
164 (princ "Bkpt.: " *debug-io
*)
165 (step-set-breaks (read *debug-io
*))
167 ((eql cmd
#\c
) ;clear a breakpoint
168 (princ "Clear: " *debug-io
*)
169 (step-clear-breaks (read *debug-io
*))
171 ((eql cmd
#\t) ;toggle trace mode
172 (setf (car *steptrace
*)
173 (not (car *steptrace
*)))
174 (princ "Trace = " *debug-io
*)
175 (prin1 (car *steptrace
*) *debug-io
*))
176 ((eql cmd
#\q
) ;quit stepper
178 ((eql cmd
#\x
) ;evaluate a form
179 (princ "Eval: " *debug-io
*)
180 (step-do-form (read *debug-io
*) env
)
182 ((eql cmd
#\r) ;return given expression
183 (princ "Return: " *debug-io
*)
184 (setq val
(list (read *debug-io
*) nil nil env
))
187 ((eql cmd
#\
#) ;set new compress level
188 (princ "Depth: " *debug-io
*)
189 (step-set-depth (read *debug-io
*))
192 (princ "Len.: " *debug-io
*)
193 (step-set-length (read *debug-io
*))
195 ((eql cmd
#\e
) ;print environment
196 (step-print-env env
))
197 (t (princ "Bad command. Type h for help\n" *debug-io
*))))
200 ;; call of evalhook was done prior to "go next" in the loop above.
201 ;; now it's done outside the loop to solve problems handling
204 (setq val
(apply #'evalhook val
))
206 (when (cdr *steptrace
*)
208 (step-spaces *hooklevel
*)
209 (princ *hooklevel
* *debug-io
*)
210 (princ " <==< " *debug-io
*) ;print the result
211 (prin1 val
*debug-io
*))
212 (step-prune-level))) ;; step-prune-level replaces inline code TAA
214 ;not an interpreted function -- just trace thru.
215 (t (unless (not (symbolp form
))
216 (when (car *steptrace
*)
218 (step-spaces *hooklevel
*) ;if form is a symbol ...
219 (princ " " *debug-io
*)
220 (prin1 form
*debug-io
*) ;... print the form ...
221 (princ " = " *debug-io
*)))
222 (setq val
(evalhook form nil nil env
)) ;eval it
223 (setq *hooklevel
* (1- *hooklevel
*)) ;decrement level
224 (unless (not (symbolp form
))
225 (when (car *steptrace
*)
226 (prin1 val
*debug-io
*))))) ;... and the value
227 val
) ;and return the value
230 ;; Made compress local function
231 ;; and changed name fcprt to step-print-compressed TAA
233 ;compress and print a form
234 (defun step-print-compressed (form)
236 (step-spaces (min 20 *hooklevel
*))
237 (princ *hooklevel
* *debug-io
*)
238 (princ " >==> " *debug-io
*)
239 (let ((*print-level
* *stepper-depth
*)
240 (*print-length
* *stepper-length
*))
241 (prin1 form
*debug-io
*))
242 (princ " " *debug-io
*))
244 ;a non-recursive fn to print spaces (not as elegant, easier on the gc)
245 (defun step-spaces (n) (dotimes (i n
) (princ " " *debug-io
*)))
247 ;and one to clear the input buffer
248 (defun step-flush () (while (not (eql (read-char *debug-io
*) #\newline
))))
253 (format *debug-io
* "Stepper Commands~%" )
255 (format *debug-io
* "----------------~%" )
257 (format *debug-io
* " n or space - next form~%" )
259 (format *debug-io
* " s or <cr> - step over form~%" )
261 (format *debug-io
* " f FUNCTION - go until FUNCTION is called~%" )
263 (format *debug-io
* " b FUNCTION - set breakpoint at FUNCTION~%" )
265 (format *debug-io
* " b <list> - set breakpoint at each function in list~%" )
267 (format *debug-io
* " c FUNCTION - clear breakpoint at FUNCTION~%" )
268 (format *debug-io
* " c <list> - clear breakpoint at each function in list~%" )
269 (format *debug-io
* " c *all* - clear all breakpoints~%" )
270 (format *debug-io
* " g - go until a breakpoint is reached~%" )
271 (format *debug-io
* " u - go up; continue until enclosing form is done~%" )
274 (format *debug-io
*" w - where am I? -- backtrace~%" )
275 (format *debug-io
*" t - toggle trace on/off~%" )
276 (format *debug-io
* " q - quit stepper, continue execution~%" )
279 (format *debug-io
* " p - pretty-print current form (uncompressed)~%" )
280 (format *debug-io
* " e - print environment~%" )
281 (format *debug-io
* " x <expr> - execute expression in current environment~%" )
282 (format *debug-io
* " r <expr> - execute and return expression~%" )
284 (format *debug-io
* " # nn - set print depth to nn~%" )
285 (format *debug-io
* " . nn - set print length to nn~%" )
287 (format *debug-io
* " h - print this summary~%" )
291 ;evaluate a form in the given environment
292 (defun step-do-form (f1 env
)
293 (step-spaces *hooklevel
*)
294 (princ *hooklevel
* *debug-io
*)
295 (princ " res: " *debug-io
*)
296 (prin1 (evalhook f1 nil nil env
) *debug-io
*)) ;print result
299 (defun step-set-depth (cf)
301 (setq *stepper-depth
* (truncate cf
)))
302 (t (setq *stepper-depth
* 3))))
304 ;set new print length
305 (defun step-set-length (cf)
307 (setq *stepper-length
* (truncate cf
)))
308 (t (setq *stepper-length
* 3))))
311 (defun step-print-env (env)
313 (step-spaces *hooklevel
*)
314 (princ *hooklevel
* *debug-io
*)
315 (princ " env: " *debug-io
*)
316 (prin1 env
*debug-io
*)
320 (defun step-set-breaks (l)
322 ((symbolp l
) (setq *steplist
* (cons l
*steplist
*)))
324 (step-set-breaks (car l
))
325 (step-set-breaks (cdr l
)))))
328 (defun step-clear-breaks (l)
330 ((eql l
'*all
*) (setq *steplist
* nil
))
331 ((symbolp l
) (delete l
*steplist
*))
333 (step-clear-breaks (car l
))
334 (step-clear-breaks (cdr l
)))))
337 (defun step-baktrace (&aux l n
)
344 (princ " " *debug-io
*)
345 (if (consp (car l
)) ;; must handle case where item is list TAA
346 (format *debug-io
* "~s ~s" (caar l
) (cdar l
))
347 (prin1 (car l
) *debug-io
*))
352 ;; Added function step-add-level for clarity, since function has
353 ;; become more complex. TAA
355 (defun step-add-level (form env
)
356 (setq *callist
* ;; Modified so that callist entry can be
357 ;; list where cadr is a tag saved for later
358 ;; match. This us used for block, return-from,
360 (cons (case (car form
)
362 (cons (car form
) (cadr form
)))
363 ((catch throw
) ;; we may need to eval symbol
364 (if (symbolp (cadr form
))
366 (evalhook (cadr form
) nil nil env
))
367 (if (eq (caadr form
) 'quote
) ;; quoted tag
368 (cons (car form
) (cadadr form
))
369 nil
))) ;; out of luck!
371 *callist
*))) ;add fn. to call list
373 ;; Added function step-prune-level for clarity TAA
375 (defun step-prune-level ()
376 (setq *hooklevel
* (1- *hooklevel
*))
377 (setq *callist
* (cdr *callist
*)))
379 ;; Deleted fix-go, replaced with step-fix-levels which handles go, return,
380 ;; and return-from. TAA
382 (defun step-fix-levels ()
383 (cond ((eq (car *callist
*) 'go
) ;; go -- prune back to tagbody
385 (when (null *callist
*) (return)) ;; we are lost!
386 (when (member (car *callist
*)
387 '(loop do do
* dolist dotimes prog prog
* tagbody
))
392 ((or (eq (car *callist
*) 'return
) ;; return -- prune back before block
393 (and (consp (car *callist
*)) ;; return-from nil is same
394 (eq (caar *callist
*) 'return-from
)
395 (null (cdar *callist
*))))
398 (when (null *callist
*) (return)) ;; we are lost!
399 (when (member (car *callist
*)
400 '(loop do do
* dolist dotimes prog prog
*))
403 ((and (consp (car *callist
*)) ;; return-from - prune back before block
404 (eq (caar *callist
*) 'return-from
))
405 (let ((target (cdar *callist
*)))
408 (when (null *callist
*) (return)) ;; we are lost!
409 (when (or (eq target
(car *callist
*))
410 (and (consp (car *callist
*))
411 (eq (caar *callist
*) 'block
)
412 (eq (cdar *callist
*) target
)))
415 ;; Added step-fix-throw TAA
417 (defun step-fix-throw () ;; fix levels after evalhook for throw
418 (when (and (consp (car *callist
*))
419 (eq (caar *callist
*) 'throw
))
420 (let ((target (cdar *callist
*)))
423 (when (null *callist
*) (return)) ;; we are lost!
424 (when (and (consp (car *callist
*))
425 (eq (caar *callist
*) 'catch
)
426 (eq (cdar *callist
*) target
))
429 ;;-- Modification MCG 5/5/93
431 (defun is-brk-in-form (form brklst
)
433 (mapcar #'(lambda (x)
435 ((listp x
) (if (is-brk-in-form x brklst
) (return t
)))
436 ((and (or (symbolp x
) (functionp x
))
443 ;; Use this function for common LISP
446 (while (or (null val
) (eq val
#\newline
))
447 (setq val
(read-char))
449 (char-downcase val
)))