1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-11 00:00:49 +02:00
guile/module/slib/break.scm
2001-04-14 11:24:45 +00:00

149 lines
4.9 KiB
Scheme

;;;; "break.scm" Breakpoints for debugging in Scheme.
;;; Copyright (C) 1991, 1992, 1993, 1995 Aubrey Jaffer.
;
;Permission to copy this software, to redistribute it, 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 warrantee 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)
;;;; 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 debug: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 debug: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 breakpoint debug:breakpoint)
(define bkpt debug:breakpoint)
(define continue debug:continue)
(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 debug: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.
(require 'alist)
(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))))