mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-11 00:00:49 +02:00
149 lines
4.9 KiB
Scheme
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))))
|