Higepon IIRC a while ago you asked for some idea on
debugging for Scheme program; shamelessly copying the idea
(but not the code) from Ikarus, I am working on the
following code for future inclusion in Nausicaa.
#!r6rs
(import (rnrs)
(nausicaa language printf)
(nausicaa language parameters)
(nausicaa language pretty-print))
(define-syntax debug-root
(lambda (stx)
(syntax-case stx ()
((_ . ?body)
#'(with-exception-handler
(lambda (E)
(debug-root-handler E))
(lambda () . ?body))))))
(define-syntax debug-frame
(lambda (stx)
(syntax-case stx ()
((_ . ?body)
#'(with-exception-handler
(lambda (E)
(debug-frame-handler E '?body))
(lambda () . ?body))))))
(define *debug-frame-stack*
(make-parameter '()))
(define *max-printed-frames* 3)
(define (debug-frame-handler E body)
(*debug-frame-stack* (cons body (*debug-frame-stack*)))
(raise-continuable E))
(define (debug-root-handler E)
(let ((oport (current-error-port))
(frames (let ((frames (*debug-frame-stack*)))
(*debug-frame-stack* '())
frames)))
(debug-print-condition E oport)
(debug-print-frames frames oport *max-printed-frames*)
(debug-enter-repl E frames oport)))
(define (debug-print-condition E oport)
(if (condition? E)
(begin
(printf oport "Debugger detected raised exception.\n")
(when (who-condition? E)
(printf oport "\t&who:\t\t~a\n" (condition-who E)))
(when (message-condition? E)
(printf oport "\t&message:\t~s\n" (condition-message E)))
(when (irritants-condition? E)
(display "\t&irritants:\t" oport)
(pretty-print (condition-irritants E) oport)))
(begin
(display "Raised object:\n")
(pretty-print E)))
(newline oport))
(define (debug-print-condition-components E oport)
(display "\nCondition components:\n" oport)
(for-each (lambda (e)
(pretty-print e))
(simple-conditions E))
(newline oport))
(define (debug-print-frames frames oport max-printed-frames)
(unless (null? frames)
(let ((frame-counter 0))
(let next-frame ((frames frames))
(unless (null? (cdr frames))
(next-frame (cdr frames)))
(unless (<= max-printed-frames frame-counter)
(printf oport "*** Frame ~a:\n\n" (post-incr frame-counter))
(for-each (lambda (form)
(pretty-print form oport))
(car frames)))
(newline oport)))))
(define (debug-enter-repl E frames oport)
(let* ((iport (current-input-port))
(consume-newline (lambda () (read-char iport))))
(let loop ()
(printf oport "[Q]uit, [E]rror, [C]ondition components, [S]tack frames, [R]eturn\n")
(printf oport "debug> ")
(let ((datum (read iport)))
(when (equal? datum #\newline)
(loop))
(consume-newline)
(case datum
((Q q)
(exit))
((E e)
(debug-print-condition E oport)
(loop))
((C c)
(debug-print-condition-components E oport)
(loop))
((S s)
(debug-print-frames frames oport *max-printed-frames*)
(loop))
((R r)
(values))
(else
(printf oport "Unknown command.\n")
(loop)))))))
;;; --------------------------------------------------------------------
(define-syntax post-incr
(lambda (stx)
(syntax-case stx ()
((_ ?id)
(identifier? #'?id)
#'(let ((v ?id))
(set! ?id (+ ?id 1))
v))
((_ ?expr)
#'(+ ?expr 1))
(_
(syntax-violation 'post-incr
"invalid post-increment operation" (syntax->datum stx))))))
;;; --------------------------------------------------------------------
(debug-root
(debug-frame
(let ((a 1))
(debug-frame
(let ((b 2))
(debug-frame
(let ((c 3))
(debug-frame
(let ((d 4))
(+ a b c d "e"))))))))))
;;; end of file
The concept is to have the expander insert DEBUG-ROOT and
DEBUG-FRAME forms in "strategic" places; some example of
strategic places:
(debug-frame
(let ((var (debug-frame init)) ...)
(debug-frame ---)))
(debug-frame
(do ---))
(debug-frame
(cond ---))
(debug-frame
(case ---))
(lambda () (debug-frame ---))
(define-syntax the-macro
(debug-root ---))
(define-syntax the-macro
(lambda (stx)
(debug-root ---)))
a DEBUG-ROOT form must enclose the entry point of the whole
program. When the use of the debugger is off, DEBUG-ROOT
and DEBUG-FRAME should just expand into BEGIN forms:
(DEBUG-ROOT . ?body) ===> (begin . ?body)
(DEBUG-FRAME . ?body) ===> (begin . ?body)
I am still reasoning about what is possible and what is
not, but using this code as skeleton should allow some
interesting possibilities.
HTH
--
Marco Maggi
Interesting.
If I understand it.
Although it has some runtime penalty,
really useful I think.
Have you used these on "real debug"?
Cheers.
> --
> You received this message because you are subscribed to the Google Groups "Mosh Developer Disscus" group.
> To post to this group, send email to mosh-develo...@googlegroups.com.
> To unsubscribe from this group, send email to mosh-developer-di...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/mosh-developer-discuss?hl=en.
>
>
Current Mosh stack trace is written in C++.
And C++ world doesn't know about R6RS expander.
So we can show a few information on stack trace.
>> Have you used these on "real debug"?
> So far I used the debugging mode in Ikarus, which is
> almost the same and provides the stack traces of both the
> run time code and various levels of expansion, and it makes
> a true difference compared to having nothing at all. :-)
Great. I'll try Ikarus debugging mode.
> * Once the stack trace is available, it can be queried from
> the REPL being that each frame has a number; for example
> we could search all the frame S-exprs which match a
> pattern.
Yeah. It's quite useful.
I look forward to your debug library is working on Mosh.
If you find some performance problems or bugs, please let me know.
Cheers.