;;;; "break.scm" Breakpoints for debugging in Scheme. ;;; Copyright (C) 1991, 1992, 1993, 1995, 2003 Aubrey Jaffer ; ;Permission to copy this software, to modify it, to redistribute it, ;to distribute modified versions, and to use it for any purpose is ;granted, subject to the following restrictions and understandings. ; ;1. Any copy made of this software must include this copyright notice ;in full. ; ;2. I have made no warranty or representation that the operation of ;this software will be error-free, and I am under no obligation to ;provide any services, by way of maintenance, update, or otherwise. ; ;3. In conjunction with products arising from the use of this ;material, there shall be no use of my name in any advertising, ;promotional, or sales literature without prior written consent in ;each case. (require 'qp) (require 'alist) ;;;; BREAKPOINTS ;;; Typing (init-debug) at top level sets up a continuation for ;;; breakpoint. When (breakpoint arg1 ...) is then called it returns ;;; from the top level continuation and pushes the continuation from ;;; which it was called on breakpoint:continuation-stack. If ;;; (continue) is later called, it pops the topmost continuation off ;;; of breakpoint:continuation-stack and returns #f to it. (define breakpoint:continuation-stack '()) ;@ (define breakpoint (let ((call-with-current-continuation call-with-current-continuation) (apply apply) (qpn qpn) (cons cons) (length length)) (lambda args (if (provided? 'trace) (print-call-stack (current-error-port))) (apply qpn "BREAKPOINT:" args) (let ((ans (call-with-current-continuation (lambda (x) (set! breakpoint:continuation-stack (cons x breakpoint:continuation-stack)) (debug:top-continuation (length breakpoint:continuation-stack)))))) (cond ((not (eq? ans breakpoint:continuation-stack)) ans)))))) ;@ (define continue (let ((null? null?) (car car) (cdr cdr)) (lambda args (cond ((null? breakpoint:continuation-stack) (display "; no break to continue from") (newline)) (else (let ((cont (car breakpoint:continuation-stack))) (set! breakpoint:continuation-stack (cdr breakpoint:continuation-stack)) (if (null? args) (cont #f) (apply cont args)))))))) (define debug:top-continuation (if (provided? 'abort) (lambda (val) (display val) (newline) (abort)) (begin (display "; type (init-debug)") #f))) ;@ (define (init-debug) (call-with-current-continuation (lambda (x) (set! debug:top-continuation x)))) ;@ (define breakf (let ((null? null?) ;These bindings are so that (not not) ;breakf will not break on parts (car car) (cdr cdr) ;of itself. (eq? eq?) (+ +) (zero? zero?) (modulo modulo) (apply apply) (display display) (breakpoint breakpoint)) (lambda (function . optname) ;; (set! trace:indent 0) (let ((name (if (null? optname) function (car optname)))) (lambda args (cond ((and (not (null? args)) (eq? (car args) 'debug:unbreak-object) (null? (cdr args))) function) (else (breakpoint name args) (apply function args)))))))) ;;; the reason I use a symbol for debug:unbreak-object is so ;;; that functions can still be unbreaked if this file is read in twice. ;@ (define (unbreakf function) ;; (set! trace:indent 0) (function 'debug:unbreak-object)) ;;;;The break: functions wrap around the debug: functions to provide ;;; niceties like keeping track of breakd functions and dealing with ;;; redefinition. (define break:adder (alist-associator eq?)) (define break:deler (alist-remover eq?)) (define *breakd-procedures* '()) (define (break:breakf fun sym) (cond ((not (procedure? fun)) (display "WARNING: not a procedure " (current-error-port)) (display sym (current-error-port)) (newline (current-error-port)) (set! *breakd-procedures* (break:deler *breakd-procedures* sym)) fun) (else (let ((p (assq sym *breakd-procedures*))) (cond ((and p (eq? (cdr p) fun)) fun) (else (let ((tfun (breakf fun sym))) (set! *breakd-procedures* (break:adder *breakd-procedures* sym tfun)) tfun))))))) (define (break:unbreakf fun sym) (let ((p (assq sym *breakd-procedures*))) (set! *breakd-procedures* (break:deler *breakd-procedures* sym)) (cond ((not (procedure? fun)) fun) ((not p) fun) ((eq? (cdr p) fun) (unbreakf fun)) (else fun)))) ;;;; Finally, the macros break and unbreak ;@ (defmacro break xs (if (null? xs) `(begin ,@(map (lambda (x) `(set! ,x (break:breakf ,x ',x))) (map car *breakd-procedures*)) (map car *breakd-procedures*)) `(begin ,@(map (lambda (x) `(set! ,x (break:breakf ,x ',x))) xs)))) (defmacro unbreak xs (if (null? xs) (slib:eval `(begin ,@(map (lambda (x) `(set! ,x (break:unbreakf ,x ',x))) (map car *breakd-procedures*)) '',(map car *breakd-procedures*))) `(begin ,@(map (lambda (x) `(set! ,x (break:unbreakf ,x ',x))) xs))))