take the MzTake out of mztake

svn: r8755
This commit is contained in:
Greg Cooper 2008-02-21 16:45:21 +00:00
parent f39b12a555
commit 3e204e0641
22 changed files with 3 additions and 2311 deletions

View File

@ -384,15 +384,4 @@
is-tail?)
annotated))
(values (top-level-annotate stx) (hash-table-map breakpoints (lambda (k v) k)))))
#;
(define (tests)
(run/single-stepping-annotation
(current-custodian) "a.ss"
(map string->path '("/home/gmarceau/projects/mztake/collects/mztake/a.ss"
"/home/gmarceau/projects/mztake/collects/mztake/b.ss"))
(lambda (fn pos)
(printf "break?: ~a ~a~n" fn pos) #t)
(lambda (bp-info) (printf "break: ~a~n" bp-info) #f)))
)
(values (top-level-annotate stx) (hash-table-map breakpoints (lambda (k v) k))))))

View File

@ -116,7 +116,7 @@
(if (is-a? source editor<%>)
source
(cond
[(send (group:get-the-frame-group) locate-file source)
[(and source (send (group:get-the-frame-group) locate-file source))
=>
(lambda (frame)
(let ([defss (map (lambda (t) (send t get-defs)) (send frame get-tabs))])
@ -587,7 +587,7 @@
(lambda (defs)
(send (send defs get-tab)
add-top-level-binding var val))]
[else (printf "record-top-level failed~n")])
[else (void) #;(printf "record-top-level failed~n")])
#;
(printf "top-level binding: ~a ~a ~a~n" mod var val))
source)])

View File

@ -1,85 +0,0 @@
#| This script tests a priority queue (heap) that is correctly implemented, but incorrectly used.
It is not commented because it uses a some advanced FrTime concepts that can easily be looked
up in the help desk, and both the description and motivation of the example can be found in
"A Dataflow Language for Scriptable Debugging" (Marceau, Cooper, Krishnamurthi, Reiss),
available at:
http://www.cs.brown.edu/~sk/Publications/Papers/Published/mckr-dataflow-lang-script-debug/
This script uses the concept of maintaining a local model of the heap being debugged, as a simple,
and very slow, list. The difference is that a fancy heap used can be naively implemented as a list,
simply removing only the smallest element each time. Models are external to your program, you don't
have to add any test code to your program to use them. By adding and removing the items to our local
"model" (the values come from the heap code we used), we can compare the results and assert whether
it is working correctly or not. Our model shows the values we should be getting from the program,
but clearly are not.
To provide some context for this demo, and what debugging problem MzTake helps us explore, I offer
the following, out of context, taken directly from the paper:
We find that the queue's elements are not in sorted order while those in the model
are. More revealingly, the queue's elements are not the same as those in the model.
A little further study shows that the bug is in our usage of the priority queue:
we have failed to account for the fact that the assignment to dest.weight
in relax (figure 1) updates the weights of nodes already in the queue. Because
the queue is not sensitive to these updates, what it returns is no longer the
smallest element in the queue.
On further reading, we trace the error to a subtle detail in the description of
Dijkstra's algorithm in Cormen, et al.'s book [9, page 530]. The book permits
the use of a binary heap (which is how we implemented the priority queue) for
sparse graphs, but subsequently amends the pseudocode to say that the assignment
to dest.weight must explicitly invoke a key-decrement operation. Our error,
therefore, was not in the implementation of the heap, but in using the (faster)
binary heap implementation without satisfying its (stronger) contract. |#
(require (lib "mztake.ss" "mztake")
(lib "useful-code.ss" "mztake")
"dijkstra-solver.ss"
(lib "match.ss"))
(define inserts (trace (loc '(lib "heap.ss" "frtime") '(let* ((sorter _) _) _))
item))
(define removes (trace (loc/r '(dv:ref (t-data _) _))))
#| The following code
merely observes the insertions and removals
from the heap. We notice whether any of the removals are out
of order based on the last item removed, as long as there are
no insertions between the two events. We can keep track of the
last 2 using history-e. |#
(define (not-in-order e)
(filter-e
(match-lambda
[('reset _) false]
[(_ 'reset) false]
[(previous current) (> previous current)]
[else false])
(history-e e 2)))
(define inserts-and-removes-e (merge-e (removes . ==> . node-weight)
(inserts . -=> . 'reset)))
(define violations (not-in-order inserts-and-removes-e))
(printf-b "all inserts and removes: ~a" (history-b inserts-and-removes-e))
(printf-b "all violations: ~a" (history-b violations))
(printf-b "latest-violation: ~a" (hold violations))
#| This output indicates that the queue has yielded nodes whose weights are out of order.
This confirms our suspicion that the problem somehow involves the priority queue. |#
#| Implementation of the local model follows... |#
(define ((insert-in-model item) model)
(printf "~nInserting ~a into model containing:~n~a~n" item (value-now model))
(cons item model))
(define ((remove-from-model item) model)
(printf "~nRemoving ~a from model containing:~n~a~n" item (value-now model))
(filter (lambda (i) (not (equal? i item))) model))
(define inserters (accum-b (inserts . ==> . insert-in-model) empty))
(define removers (accum-b (removes . ==> . remove-from-model) inserters))
(set-main! "dijkstra.ss")
(set-running-e! (violations . -=> . false))

View File

@ -1,49 +0,0 @@
(module dijkstra-solver mzscheme
(require (lib "heap.ss" "frtime")
(lib "list.ss")
"graph.ss")
(provide (all-defined))
(define (make-node label x y weight) (vector label x y weight))
(define (node-label n) (vector-ref n 0))
(define (node-x n) (vector-ref n 1))
(define (node-y n) (vector-ref n 2))
(define (node-weight n) (vector-ref n 3))
(define (set-node-weight! n v) (vector-set! n 3 v))
(define (node< a b) (< (node-weight a) (node-weight b)))
(define (sqr x) (* x x))
(define (distance-to a b)
(sqrt (+ (sqr (- (node-x a) (node-x b)))
(sqr (- (node-y a) (node-y b))))))
(define (hash-table-pairs hash)
(hash-table-map hash (lambda (key val) (list key val))))
(define (relax backtrace heap origin dest)
(let ([candidate-weight
(+ (node-weight origin)
(distance-to origin dest))])
(when (candidate-weight . < . (node-weight dest))
(set-node-weight! dest candidate-weight)
;;(heap-resort heap dest)
(hash-table-put! backtrace dest origin))))
(define (solve graph nodes source)
(let ([backtrace (make-hash-table)]
[heap (make-heap node< eq?)])
(set-node-weight! source 0)
(for-each (lambda (node) (heap-insert heap node))
nodes)
(let loop ()
(unless (heap-empty? heap)
(let* ([node (heap-pop heap)]
[successors (graph-succs graph node)])
(for-each
(lambda (succ) (relax backtrace heap node succ))
successors))
(loop)))
(hash-table-pairs backtrace))))

View File

@ -1,32 +0,0 @@
(module dijkstra mzscheme
(require "dijkstra-solver.ss"
"graph.ss"
(lib "list.ss"))
(define g (make-graph 'directed))
(define (m-node label x y) (make-node label x y +inf.0))
(define nodes
(list
(m-node 'J 200 100)
(m-node 's 100 125)
(m-node '1 150 100)
(m-node '2 150 150)
(m-node '4 250 100)
(m-node '5 300 100)
(m-node '6 300 150)))
(for-each (lambda (n) (graph-node-add! g n)) nodes)
(define (n-ref label)
(first (filter (lambda (n) (eq? label (node-label n))) nodes)))
(define edges
(list (list (n-ref 's) (n-ref '1))
(list (n-ref 's) (n-ref '2))
(list (n-ref '1) (n-ref 'J))
(list (n-ref '4) (n-ref '5))
(list (n-ref 'J) (n-ref '4))
(list (n-ref 'J) (n-ref '6))))
(for-each (lambda (e) (graph-edge-add! g (first e) (second e)))
edges)
(printf "~n~n---output from dijkstra.ss:~n~a~n---~n"
(solve g (reverse nodes) (n-ref 's))))

View File

@ -1,100 +0,0 @@
; -*- Scheme -*-
; Shriram Krishnamurthi (shriram@cs.rice.edu)
; Tue Jul 25 23:20:45 EDT 1995
; (define-structure (dv:vector length size contents))
(module dv mzscheme
(provide dv:make dv:make-w/-init dv:length dv:contents dv:append
dv:remove-last dv:legitimate-index dv:ref dv:set!)
(define dv:vector?
(lambda (obj)
(if (vector? obj)
(if (= (vector-length obj) 4)
(eq? (vector-ref obj 0) 'dv:vector)
#f)
#f)))
(define dv:vector-length
(lambda (obj) (vector-ref obj 1)))
(define dv:vector-size
(lambda (obj) (vector-ref obj 2)))
(define dv:vector-contents
(lambda (obj) (vector-ref obj 3)))
(define dv:set-vector-length!
(lambda (obj newval) (vector-set! obj 1 newval)))
(define dv:set-vector-size!
(lambda (obj newval) (vector-set! obj 2 newval)))
(define dv:set-vector-contents!
(lambda (obj newval) (vector-set! obj 3 newval)))
(define dv:make-vector
(lambda (length size contents)
((lambda () (vector 'dv:vector length size contents)))))
(define dv:make
(let* ((default-initial-size 8)
(default-initial-vector (make-vector default-initial-size)))
(lambda arg
(cond
((null? arg)
(dv:make-vector 0 default-initial-size default-initial-vector))
((= 1 (length arg))
(let ((l (car arg)))
(dv:make-vector 0 l (make-vector l))))
(else
(error 'dv:make "wrong number of arguments"))))))
(define dv:make-w/-init
(lambda values
(let ((l (length values)))
(dv:make-vector l l (list->vector values)))))
(define dv:append
(lambda (dv item)
(let ((length (dv:vector-length dv))
(size (dv:vector-size dv))
(contents (dv:vector-contents dv)))
(if (< length size)
(begin
(vector-set! contents length item)
(dv:set-vector-length! dv (+ length 1)))
(begin
(let ((new-vector (make-vector (* size 2))))
(let loop
((i 0))
(when (< i size)
(vector-set! new-vector i (vector-ref contents i))
(loop (+ i 1))))
(dv:set-vector-contents! dv new-vector)
(dv:set-vector-size! dv (* size 2))
(dv:append dv item)))))))
(define dv:remove-last
(lambda (dv)
(dv:set-vector-length! dv (- (dv:vector-length dv) 1))
(vector-set! (dv:vector-contents dv) (dv:vector-length dv) 0)))
(define dv:legitimate-index
(lambda (dv index)
(< index (dv:vector-length dv))))
(define dv:ref
(lambda (dv index)
(if (dv:legitimate-index dv index)
(vector-ref (dv:vector-contents dv) index)
(error 'dv:ref "index too large"))))
(define dv:set!
(lambda (dv index value)
(if (dv:legitimate-index dv index)
(vector-set! (dv:vector-contents dv) index value)
(error 'dv:set! "index too large"))))
(define dv:contents dv:vector-contents)
(define dv:length dv:vector-length)
)

View File

@ -1,542 +0,0 @@
;; -*- compile-command: "mzscheme -M errortrace -u graph.ss" -*-
(module graph mzscheme
(require (lib "base-gm.ss" "frtime")
(lib "etc.ss")
(lib "list.ss"))
(provide make-graph
;; --- Constructors :
graph?
graph-directed?
graph-make-similar
graph-copy
graph-add-all!
;; --- Functions on nodes:
graph-nodes
graph-nodes-size
graph-make-node!
graph-node-add!
graph-node-mem?
graph-node-set!
graph-node-remove!
graph-node-collapse!
graph-node-has-label?
graph-node-label
graph-for-each-node
graph-fold-nodes
;; --- Functions on neighbors:
graph-succs
graph-preds
graph-adjs
graph-for-each-adjs
;; --- Functions on edges:
graph-edges
graph-edges-size
graph-edge-add!
graph-edge-mem?
graph-edge-set!
graph-edge-remove!
graph-edge-has-label?
graph-edge-label
graph-for-each-edge
graph-fold-edges
;; --- Simple graph algorithms:
graph-dfs-from-node
graph-dfs-all
graph-components
graph-strongly-connected-components
graph-topological-sort
;; --- Debugging:
graph-to-list
graph-to-string
graph-test
)
(define-struct t (flags nNodes nEdges nodes successors predessessors))
;; Flags can be: 'equal 'directed 'unique-node 'unique-edge 'nodes-must-exists 'safe
;; 'safe is a short for '(unique-node unique-edge nodes-must-exists)
(define (make-graph . flags)
(let ((flag-hash (make-hash)))
(set! flags (expands-safe-flag flags))
(for-each (lambda (flag) (hash-put! flag-hash flag true)) flags)
(if (member 'equal flags)
(make-t flag-hash 0 0 (make-hash 'equal) (make-hash 'equal) (make-hash 'equal))
(make-t flag-hash 0 0 (make-hash) (make-hash) (make-hash)))))
(define (graph? graph) (t? graph))
(define no-value (box empty))
;; Makes a hash with the same 'equal as the graph
(define (graph-make-hash graph)
(if (graph-has-flag? graph 'equal)
(make-hash 'equal)
(make-hash)))
(define (expands-safe-flag flags)
(let loop ((cur flags) (acc empty))
(cond [(empty? cur) acc]
[(eq? (first cur) 'safe) (loop (rest cur) (append '(unique-node unique-edge nodes-must-exists) flags))]
[true (loop (rest cur) (cons (first cur) acc))])))
;; Make a graph with mostly the same flags as another graph
(define (graph-make-similar graph plus-flags minus-flags)
(set! plus-flags (expands-safe-flag plus-flags))
(set! minus-flags (expands-safe-flag minus-flags))
(apply make-graph
(append plus-flags
(filter (lambda (i) (not (member i minus-flags)))
(hash-keys (t-flags graph))))))
(define (graph-copy graph)
(let* ((rtn-nodes (graph-make-hash graph))
(rtn-successors (graph-make-hash graph))
(rtn-predessessors (graph-make-hash graph))
(rtn (make-t (t-flags graph) (t-nNodes graph) (t-nEdges graph) rtn-nodes rtn-successors rtn-predessessors)))
(hash-add-all! rtn-nodes (t-nodes graph))
(hash-add-all! rtn-successors (t-successors graph))
(hash-add-all! rtn-predessessors (t-predessessors graph))
rtn))
(define (graph-add-all! dest-graph src-graph)
(graph-for-each-node
src-graph
(lambda (node)
(if (graph-node-has-label? src-graph node)
(graph-node-add! dest-graph node (graph-node-label src-graph node))
(graph-node-add! dest-graph node))))
(graph-for-each-edge
src-graph
(lambda (from to)
(if (graph-edge-has-label? src-graph from to)
(graph-edge-add! dest-graph from to (graph-edge-label src-graph from to))
(graph-edge-add! dest-graph from to)))))
(define (graph-has-flag? graph flag)
(hash-mem? (t-flags graph) flag))
(define (graph-directed? graph)
(hash-mem? (t-flags graph) 'directed))
;;; =====================================================================
;;; Nodes
(define (graph-nodes graph) (hash-keys (t-nodes graph)))
(define (graph-nodes-size graph) (t-nNodes graph))
(define graph-make-node!
(case-lambda
[(graph) (graph-make-node! graph no-value)]
[(graph val)
(let ((sym (string->symbol (string-append "node" (number->string (t-nNodes graph))))))
(graph-node-add! graph sym val)
sym)]))
;; Add a node to the graph. If the node already exists,
;; sets its label, unless the graph has the 'unique-node property,
;; in which case this will assert.
(define graph-node-add!
(case-lambda
[(graph node) (graph-node-add! graph node no-value)]
[(graph node val)
(if (hash-mem? (t-nodes graph) node)
(assert (not (graph-has-flag? graph 'unique-node)))
(begin
(set-t-nNodes! graph (+ 1 (t-nNodes graph)))
(hash-put! (t-successors graph) node (graph-make-hash graph))
(if (graph-directed? graph)
(hash-put! (t-predessessors graph) node (graph-make-hash graph)))))
(hash-put! (t-nodes graph) node val)]))
(define (graph-node-mem? graph node)
(hash-mem? (t-nodes graph) node))
(define (graph-node-set! graph node val)
(assert (hash-mem? (t-nodes graph) node))
(hash-put! (t-nodes graph) node val))
(define (graph-node-remove! graph node)
(assert (graph-node-mem? graph node))
(for-each (lambda (i) (graph-edge-remove! graph node i))
(hash-keys (hash-get (t-successors graph) node)))
(if (graph-directed? graph)
(for-each (lambda (i) (graph-edge-remove! graph i node))
(hash-keys (hash-get (t-predessessors graph) node))))
(hash-remove! (t-nodes graph) node)
(hash-remove! (t-successors graph) node)
(if (graph-directed? graph)
(hash-remove! (t-predessessors graph) node))
(set-t-nNodes! graph (- (t-nNodes graph) 1)))
(define graph-node-collapse!
(case-lambda
[(graph node with-self-loop) (graph-node-collapse! graph node with-self-loop (lambda (pred-label succ-label) no-value))]
[(graph node with-self-loop label-fn)
(let ((is-directed (graph-directed? graph)))
(for-each
(lambda (pred)
(for-each
(lambda (succ)
(unless (or (and (not is-directed) (eq? pred succ))
(graph-edge-mem? graph pred succ))
(let* ((label-pred (hash-get (hash-get (t-successors graph) pred) node))
(label-succ (hash-get (hash-get (t-successors graph) node) succ))
(new-label (label-fn (if (eq? label-pred no-value) false label-pred)
(if (eq? label-succ no-value) false label-succ))))
(when (or with-self-loop (not (eq? pred succ)))
(hash-put! (hash-get (t-successors graph) pred) succ new-label)
(if is-directed
(hash-put! (hash-get (t-predessessors graph) succ) pred new-label)
(hash-put! (hash-get (t-successors graph) succ) pred new-label))))))
(hash-keys (hash-get (t-successors graph) node))))
(hash-keys (hash-get
(if is-directed (t-predessessors graph) (t-successors graph))
node))))
(graph-node-remove! graph node)]))
(define (graph-node-has-label? graph node)
(not (eq? (hash-get (t-nodes graph) node) no-value)))
(define (graph-node-label graph node)
(let ((r (hash-get (t-nodes graph) node)))
(if (eq? r no-value) (error "graph-node-label: no value for node" node)
r)))
(define (graph-succs graph node)
(assert (graph-directed? graph))
(hash-keys (hash-get (t-successors graph) node)))
(define (graph-preds graph node)
(assert (graph-directed? graph))
(hash-keys (hash-get (t-predessessors graph) node)))
(define (graph-adjs graph node)
(if (graph-directed? graph)
(append (hash-keys (hash-get (t-successors graph) node))
(hash-keys (hash-get (t-predessessors graph) node)))
(hash-keys (hash-get (t-successors graph) node))))
(define (graph-for-each-adjs graph node fn)
(for-each (hash-keys (hash-get (t-successors graph) node))
(lambda (succ) (fn node succ)))
(when (graph-directed? graph)
(for-each (hash-keys (hash-get (t-predessessors graph) node))
(lambda (pred) (fn pred node)))))
(define (graph-for-each-node graph fn)
(for-each fn (hash-keys (t-nodes graph))))
(define (graph-fold-nodes graph init fn)
(let ((acc init))
(graph-for-each-node
graph
(lambda (node) (set! acc (fn node acc))))
acc))
;;; =====================================================================
;;; Edges
(define (graph-edges graph)
(let ((rtn empty))
(graph-for-each-edge graph (lambda (from to) (set! rtn (cons from to))))
rtn))
(define (graph-edges-size graph) (t-nEdges graph))
;; Add an edge to the graph. If the edge already exists,
;; sets its label, unless the graph has the 'unique-edge property,
;; in which case this will assert.
(define graph-edge-add!
(case-lambda
[(graph from to) (graph-edge-add! graph from to no-value)]
[(graph from to val)
(if (graph-edge-mem? graph from to)
(assert (not (graph-has-flag? graph 'unique-edge)))
(set-t-nEdges! graph (+ (t-nEdges graph) 1)))
(if (graph-has-flag? graph 'nodes-must-exists)
(assert (and (graph-node-mem? graph from) (graph-node-mem? graph to)))
(begin (if (not (graph-node-mem? graph from)) (graph-node-add! graph from))
(if (not (graph-node-mem? graph to)) (graph-node-add! graph to))))
(hash-put! (hash-get (t-successors graph) from) to val)
(if (graph-directed? graph)
(hash-put! (hash-get (t-predessessors graph) to) from val)
(hash-put! (hash-get (t-successors graph) to) from val))]))
(define (graph-edge-mem? graph from to)
(if (graph-has-flag? graph 'nodes-must-exists)
(assert (and (graph-node-mem? graph from)
(graph-node-mem? graph to))))
(and (hash-mem? (t-successors graph) from)
(hash-mem? (hash-get (t-successors graph) from) to)))
(define (graph-edge-set! graph from to val)
(assert (graph-edge-mem? graph from to))
(hash-put! (hash-get (t-successors graph) from) to val)
(if (graph-directed? graph)
(hash-put! (hash-get (t-predessessors graph) to) from val)
(hash-put! (hash-get (t-successors graph) to) from val)))
(define (graph-edge-remove! graph from to)
(assert (graph-edge-mem? graph from to))
(hash-remove! (hash-get (t-successors graph) from) to)
(if (graph-directed? graph)
(hash-remove! (hash-get (t-predessessors graph) to) from)
(hash-remove! (hash-get (t-successors graph) to) from)))
(define (graph-edge-has-label? graph from to)
(not (eq? (hash-get (hash-get (t-successors graph) from) to) no-value)))
(define (graph-edge-label graph from to)
(let ((r (hash-get (hash-get (t-successors graph) from) to)))
(if (eq? r no-value) (error "graph-edge-label: no value for edge" (cons from to)))
r))
(define (graph-for-each-edge graph fn)
(graph-for-each-node
graph
(lambda (from)
(for-each (lambda (to) (fn from to))
(hash-keys (hash-get (t-successors graph) from))))))
(define (graph-fold-edges graph init fn)
(let ((acc init))
(graph-for-each-edge
graph
(lambda (from to) (set! acc (fn from to acc))))
acc))
;;; =====================================================================
;;; Algos
(define (graph-dfs-from-node-with-log graph node dealt-with pre-fn post-fn backward)
(assert (or (not backward) (graph-directed? graph)))
(if (not (hash-mem? dealt-with node))
(begin (hash-put! dealt-with node true)
(pre-fn node)
(for-each (lambda (n) (graph-dfs-from-node-with-log graph n dealt-with pre-fn post-fn backward))
(if backward
(hash-keys (hash-get (t-predessessors graph) node))
(hash-keys (hash-get (t-successors graph) node))))
(post-fn node))))
(define graph-dfs-from-node
(case-lambda
[(graph node pre-fn) (graph-dfs-from-node graph node pre-fn (lambda (i) i))]
[(graph node pre-fn post-fn)
(graph-dfs-from-node-with-log graph node (graph-make-hash graph) pre-fn post-fn false)]))
(define graph-dfs-all
(case-lambda
[(graph pre-fn) (graph-dfs-all graph pre-fn (lambda (i) i))]
[(graph pre-fn post-fn)
(let ((dealt-with (graph-make-hash graph)))
(graph-for-each-node graph (lambda (n) (if (not (hash-mem? dealt-with n))
(graph-dfs-from-node-with-log graph n dealt-with pre-fn post-fn false)))))]))
(define (graph-components graph)
(let ((dealt-with (graph-make-hash graph)))
(graph-fold-nodes
graph
empty
(lambda (node acc)
(if (hash-mem? dealt-with node) acc
(let ((cur-component
(let loop ((cur node) (acc empty))
(if (hash-mem? dealt-with cur) acc
(begin (hash-put! dealt-with cur true)
(foldl (lambda (adj acc) (loop adj acc)) (cons cur acc)
(graph-adjs graph cur)))))))
(cons cur-component acc)))))))
(define (graph-strongly-connected-components graph)
(assert (graph-directed? graph))
(let ((finish-times empty)
(dealt-with (graph-make-hash graph)))
(graph-for-each-node
graph
(lambda (n) (graph-dfs-from-node-with-log
graph n dealt-with
(lambda (i) i)
(lambda (i) (set! finish-times (cons i finish-times)))
false)))
(set! dealt-with (graph-make-hash graph))
(let ((component-graph (graph-make-similar graph empty '(safe equal)))
(node2supernode (make-hash)))
(for-each
(lambda (n)
(if (not (hash-mem? dealt-with n))
(let ((super-node (graph-make-node! component-graph empty)))
(graph-dfs-from-node-with-log
graph n dealt-with
(lambda (i)
(graph-node-set! component-graph super-node (cons i (graph-node-label component-graph super-node)))
(hash-put! node2supernode i super-node))
(lambda (i) i)
true))))
finish-times)
(graph-for-each-edge graph
(lambda (from to)
(graph-edge-add! component-graph
(hash-get node2supernode from)
(hash-get node2supernode to))))
(cons component-graph node2supernode))))
(define (graph-topological-sort graph)
(assert (graph-directed? graph))
(let ((rtn empty))
(graph-dfs-all graph (lambda (i) i) (lambda (node) (set! rtn (cons node rtn))))
rtn))
;;; =====================================================================
;;; Utils
(define graph-to-list
(case-lambda
[(graph) (graph-to-list graph false)]
[(graph with-labels)
(hash-map (t-nodes graph)
(lambda (node node-val)
(let ((node-rep (if (and with-labels (graph-node-has-label? graph node))
(cons node (graph-node-label graph node))
node)))
(cons node-rep
(hash-fold (hash-get (t-successors graph) node) empty
(lambda (succ edge-val acc)
(if (and with-labels (graph-edge-has-label? graph node succ))
(cons (cons succ (graph-edge-label graph node succ)) acc)
(cons succ acc))))))))]))
(define (graph-to-string-prv graph with-labels to-string)
(let ([the-to-string (or to-string
(lambda (item) (format "~a" item)))])
(string-append (if (graph-directed? graph) "[di-graph: " "[undirected-graph:")
(the-to-string (map (lambda (n)
(cons (first n) (cons '--> (rest n))))
(graph-to-list graph with-labels)))
"]")))
(define (graph-to-string graph . to-string)
(graph-to-string-prv graph false (if (empty? to-string) false (first to-string))))
(define (graph-to-string-with-labels graph . to-string)
(graph-to-string-prv graph true (if (empty? to-string) true (first to-string))))
;;; =====================================================================
;;; Tests
(define (graph-test)
(define graph (make-graph 'safe 'directed))
(graph-node-add! graph 'a)
(graph-node-add! graph 'b 2)
(graph-node-add! graph 'c 3)
(graph-node-add! graph 'd)
(graph-edge-add! graph 'a 'c)
(graph-edge-add! graph 'a 'd "asd")
(graph-edge-add! graph 'b 'c "dfg")
(graph-edge-add! graph 'b 'd)
(graph-edge-add! graph 'd 'a)
(display (graph-node-mem? graph 'a))
(display (graph-edge-mem? graph 'a 'c))
(newline)
(display (graph-node-mem? graph 'v))
(display (graph-edge-mem? graph 'c 'a))
(display (graph-edge-mem? graph 'a 'b))
(newline)
(print-each (graph-to-list graph true))
(graph-for-each-edge graph (lambda (a b) (print-each "A " a b)))
(graph-dfs-from-node graph 'a (lambda (i) (display i)))
(newline)
(graph-dfs-from-node graph 'b (lambda (i) (display i)))
(newline)
(graph-dfs-from-node graph 'c (lambda (i) (display i)))
(newline)
(graph-dfs-from-node graph 'd (lambda (i) (display i)))
(newline)
(let ((star (make-graph 'directed)))
(graph-edge-add! star 1 'x)
(graph-edge-add! star 'x 1)
(graph-edge-add! star 2 'x)
(graph-edge-add! star 'x 3)
(graph-edge-add! star 'x 4)
(graph-edge-add! star 'x 5)
(graph-node-collapse! star 'x false)
(print-each "collapsed:" (graph-to-list star)))
(let ((strong-graph (make-graph 'directed)))
(graph-edge-add! strong-graph 'e 'a)
(graph-edge-add! strong-graph 'a 'b)
(graph-edge-add! strong-graph 'b 'e)
(graph-edge-add! strong-graph 'e 'f)
(graph-edge-add! strong-graph 'b 'f)
(graph-edge-add! strong-graph 'b 'c)
(graph-edge-add! strong-graph 'f 'g)
(graph-edge-add! strong-graph 'g 'f)
(graph-edge-add! strong-graph 'c 'g)
(graph-edge-add! strong-graph 'c 'd)
(graph-edge-add! strong-graph 'd 'c)
(graph-edge-add! strong-graph 'g 'h)
(graph-edge-add! strong-graph 'd 'h)
(graph-edge-add! strong-graph 'h 'h)
(graph-edge-add! strong-graph 'xa 'xb)
(graph-edge-add! strong-graph 'xb 'xc)
(graph-edge-add! strong-graph 'xc 'xa)
(print-each "strong-graph" strong-graph)
(print-each "component" (graph-components strong-graph))
(let ((components (graph-strongly-connected-components strong-graph)))
(print-each "strong-components" components)
(print-each "toposort" (graph-topological-sort (first components)))))
(let ((u-graph (make-graph)))
(graph-edge-add! u-graph 'a 'b)
(graph-edge-add! u-graph 'b 'c)
(graph-edge-add! u-graph 'c 'd)
(graph-edge-add! u-graph 'd 'a)
(graph-edge-add! u-graph 'd 'e)
(graph-edge-add! u-graph 'e 'c)
(graph-edge-add! u-graph 'xa 'xb)
(graph-edge-add! u-graph 'xa 'xc)
(graph-edge-add! u-graph 'xb 'xd)
(newline)
(print-each "u-graph" u-graph)
(graph-edge-remove! u-graph 'b 'a)
(graph-node-remove! u-graph 'd)
(print-each "u-graph" u-graph)
(print-each "component" (graph-components u-graph)))
)
;(graph-test)
)

View File

@ -1,9 +0,0 @@
(require (lib "mztake.ss" "mztake"))
(set-main! '(lib "heap.ss" "frtime"))
(define start (current-milliseconds))
(set-running! #t)
(exited?)
(- (hold (map-e (lambda (e) (current-milliseconds))
(changes (exited?)))) start)

View File

@ -1,10 +0,0 @@
(require (lib "mztake.ss" "mztake")
(lib "useful-code.ss" "mztake"))
(set-main! "exception.ss")
(printf-b "exception.ss exited? ~a" (exited?))
(printf-b "last exception seen: ~a" (exceptions))
(set-running! true)

View File

@ -1,2 +0,0 @@
(module exception mzscheme
(thread (lambda () (raise 'exn:oops-made-a-mztake!))))

View File

@ -1,23 +0,0 @@
(require (lib "mztake.ss" "mztake")
(lib "animation.ss" "frtime")
(lib "useful-code.ss" "mztake"))
(define/bind (loc "highway.ss" 4) speed)
(printf-b "current speed: ~a" speed)
(define (make-speed-gauge speed)
(let ([center (make-posn 200 200)])
(list (make-circle center 170 "black")
(make-circle center 160 "white")
(make-rect (make-posn 0 202) 1000 1000 "white")
(make-line (make-posn 30 201) (make-posn 370 201) "black")
(make-line center
(posn+ center (make-posn (- (* 150 (cos (/ speed 30))))
(- (* 150 (sin (/ speed 30))))))
"red"))))
(display-shapes (make-speed-gauge speed))
(set-running! (< speed 55))

View File

@ -1,6 +0,0 @@
(module highway mzscheme
(let ([nap-time 0.8])
(let loop ([speed 0])
(sleep nap-time)
;; Generate some fake speeds readings:
(loop (+ speed 4)))))

View File

@ -1,29 +0,0 @@
(module picture mzscheme
(require (lib "graphics.ss" "graphics")
(lib "math.ss"))
(open-graphics)
(define d 400)
(define viewport (open-viewport "Blah" (* 2 d) (* 2 d)))
(values 1 2 3)
(define-syntax for
(syntax-rules (=)
[(_ (var = init) condn delta proc ...)
(let loop ([var init])
(when condn
proc ...
(loop (delta var))))]))
(for (i = (- d)) (< i d) add1
(for (j = (- d)) (< j d) add1
(when (<= (+ (sqr i) (sqr j)) (sqr d))
((draw-pixel viewport) (make-posn (+ d i) (+ d j))
(if (or (zero? i) (zero? j))
(make-rgb 0 0 1)
(let ([i (- 1 (/ (abs (gcd i j)) (+ (abs i) (abs j))))])
(make-rgb i i 1))))))))

View File

@ -1,28 +0,0 @@
(require (lib "mztake.ss" "mztake" )
(lib "match.ss")
(lib "base-gm.ss" "frtime")
(only mzscheme hash-table-map))
(set-main! "picture.ss")
(define (hash-table-increment! h k)
(let ([old (hash-get h k (lambda () 0))])
(hash-put! h k (add1 old))))
(define pings (make-hash 'equal))
(for-each-e! (where)
(match-lambda [(line function context rest ...)
(hash-table-increment! pings (list function context))]
[_ (void)]))
(define clicks (changes (quotient milliseconds 50)))
(set-running-e! (merge-e (clicks . -=> . false)
(clicks . -=> . true)))
(define (hash-pairs ht)
(hash-table-map ht (lambda (k v) (list k v))))
(define (show-profile)
(sort (hash-pairs pings) (lambda (a b) (> (second a) (second b)))))

View File

@ -1,519 +0,0 @@
============================================================
About MzTake
_MzTake_ is a _scripted debugger_ for PLT Scheme. It helps
programmers monitor the execution of a target program as it
unfolds (and optionally pause or resume its execution). MzTake
gives you the power to easily write real programs that debug real
programs. You are no longer limited to a tool chest of buttons
like "add breakpoint", "step-next", "step-into", and "step-over".
MzTake scripts are written in the FrTime programming
language, which is bundled with DrScheme. FrTime supports the
implementation of reactive systems in a functional style.
The key abstraction it adds is a type of value called a 'signal',
which can change over time. FrTime infers dataflow dependencies
between signals and automatically recomputes them when necessary. In
order to use MzTake, you will need to familiarize yourself with the
FrTime language by reading its own documentation.
With signals it is possible to respond to outside events concisely,
without using callbacks. Consider a MzTake script to monitor the
behavior of the program "highway-mztake.ss", in the demos directory of
the MzTake collection:
(require (lib "mztake.ss" "mztake"))
(define/bind (loc "highway.ss" 4) speed)
(printf-b "current speed: ~a" speed)
;; more code
(set-running! true)
This code executes a target module in the file "highway.ss"
after installing a _trace point_ (also known as a _watch
point_) just before the Scheme expression on the fourth line
of "highway.ss". SPEED is a FrTime behavior that always
contains the *current* value of the variable named SPEED in
the target program.
PRINTF-B works like Scheme's PRINTF function, consuming a
format-string and fill-values, printing the result in
DrScheme's interaction pane. Whereas PRINTF accumulates
outdated text on the screen, PRINTF-B will replace old text
with updated text if any of the fill-values change. In this
invocation, it prints the current speed to screen, throughout
the execution of "highway.ss". The last line invokes SET-RUNNING!,
which lunches the execution of highway.ss
MzTake scripts are also powerful tools for building external
test suites. Whereas typical test cases may only assert that
the result of a computation is correct, MzTake scripts
can dynamically break open an execution, record inner state,
and *compute* with it. This allows you to confirm that the
intermediate steps which lead to a correct answer were
also correct. In the highway example, perhaps knowing the last ten speeds
would prove useful. You could PRINTF the value
onto a new line each time, but after ten updates the screen
is already starting to fill up with information -- we are
only interested in the last ten speeds, after all.
One possible solution:
(printf-b "last ten speeds: ~a" (history-b (changes speed) 10))
HISTORY-B consumes an event stream (CHANGES SPEED) and an
optional number n, returning a FrTime behavior containing a
FIFO ordered list of the n values emitted on that event
stream. In this case, HISTORY-B maintains a list of the ten
most recent SPEEDS seen on SPEED.
We might want to pause the program when something goes awry. We do
this by exploiting the fact that the SET-RUNNING! function consumes a
FrTime behavior. The value of a behavior can change over time, and
SET-RUNNING! monitors these changes. Whenever the behavior is true,
the target program runs, and whenever it is false, the target program
pauses. We can indicate to MzTake to pause when the speed exceeds 55
as follow:
(printf-b "last ten speeds: ~a" (history-b (changes speed) 10))
(set-running! (< speed 55))
Once paused, it is possible to interactively explore the state of the
paused process. You may enter names of variables bound in the target
program, and MzTake will look up and return their values. For
example, typing 'nap-time' in the REPL while the program is paused
yields the value 0.8.
You can resume execution with "(set-running #t)", or
some other behavior, or end the execution altogether with "(kill)".
Finally, FrTime provides a rich animation library. Combined
with the MzTake debugger, it takes only a few lines to animate
your algorithms and see them in action, easily letting you
confirm (or refute!) that they are working correctly.
(require (lib "animation.ss" "frtime"))
(display-shapes (make-speed-gauge (hold speed)))
============================================================
Demos
The demos directory contains a sub-directory for each of the demos.
For instance, the highway directory contains "highway.ss" and
"highway-mztake.ss". To run this demo, switch to the "FrTime" language
level from the "Experimental Languages" section of DrScheme's language
dialog, load "highway-mztake.ss", and click "Run". What you see is
generated by the debugging script. Each demo directory contains
the following two files: one is the program being debugged
(named after the directory), and the other is a file ending
in "...-mztake.ss" (the MzTake script).
The demos are (starting with the simplest one):
./highway/highway-mztake.ss - The program simulates a very simple
speedometer, and the MzTake script
monitors it.
./sine/sine-mztake.ss - Plots values extracted from a program
which generates coordinates for a
single sine wave.
./random/random-mztake.ss - Tests the quality of Scheme's random
number generator with a histogram.
./exception/exception-mztake.ss - Demonstrates how MzTake catches exceptions.
./djikstra/dijkstra-mztake.ss - Debugs a buggy implementation of
Dijkstra's algorithm
The demos demonstrate many ways to debug with MzTake using
FrTime, even if you are not very familiar with the language.
That said, in order to become more proficient in using MzTake,
you will want to learn more about the FrTime language.
You can refer to FrTime's own documentation by searching for
"FrTime" in DrScheme's Help window. It explains how to use
time-varying behaviors and event streams in greater depth, and
also describes the many useful functions FrTime provides to work
with them.
============================================================
_Debugging with MzTake_
MzTake is a library for the FrTime languages which provides functions
that execute a target program (or many), and "connect" to points in
its code. MzTake then provides the running FrTime script with
interesting information (such as a variable's current value) which it
derives from these "connections". FrTime then handles the rest.
MzTake defines the following functions and macros:
_Installing Trace Points_
> (loc require-spec line-number)
> (loc require-spec line-number column-number)
Creates a LOC structure containing the target file, the target line
number, and (optionally) the target column number. LOC structures
are consumed by TRACE and by DEFINE/BIND. The first argument to LOC
is a file specification suitable for require, provided as a
datum. For instance, to install a trace point on the tenth line of
the MzLib's list library, use:
(trace (loc '(lib "list.ss") 10) <body> )
> (loc require-spec pattern)
The LOC function can also accept a pattern. MzTake will scan the
text of the target source file and install a breakpoint at every
location where the text matches the pattern. The pattern language
is very simple. Every symbol in the pattern is taken literally,
with the exception of the underscore. In a pattern, and underscore
stand for zero, one, or many items. Unlike the patterns used by
match.ss, MzTake does not make use of an ellipsis to indicate
repetition. The repetition is always implicit. The following
expression inserts a tracepoint on every lambda expression that has
exactly one argument, called "l" :
(trace (loc '(lib "list.ss") '(lambda (l) _)) <body> )
> (trace loc body ...)
Install a trace point at the location indicated by the LOC value.
The result is a FrTime event stream containing one value each time
the target reaches the location specified. To get the value event,
TRACE evaluates its body (once per event. During the evaluation of
the body, the target process is paused, and the body can inspect
the state of the paused program.
The body is optional. If no body is provided, the value #t is used
by default.
Unless SET-MAIN! is used, the first call to trace sets the file
name that will be run when SET-RUNNING! is invoked the first time.
> (trace* process loc thunk)
Like TRACE, but takes an explicit process argument, and a thunk
rather than a body.
> (bind (name ...) body ...)
When the target process is paused (or during the execution of a
trace body), BIND reaches in the lexical context at the point of
the pause (or of the trace point) and finds the values for the
variables whose names are given. These values are then bound in the
body (in the MzTake script) to the variables of the same name.
It is an error to call BIND while the target process is running.
You can use BIND to look up values of identifiers in the target
program that are shadowed by identifiers in the script.
> (bind* process symbol)
In the given process, find the variable whose name has the given
symbol, and returns its value.
> (define/bind loc name ...)
Define the NAMEs to behaviors reflecting the values of the
given names in the target program, at the given
location. DEFINE/BIND is short for:
(define name (hold (trace loc (bind (name) name))))
> (define/bind-e loc name ...)
Same as DEFINE/BIND, but binds event streams to the names instead
of behaviors.
> (exceptions)
> (exceptions process)
Returns an event stream containing one EXN structure for each
exception raised in the target process and not caught.
> (exited?)
> (exited? process)
Returns a behavior which starts as #f and take on the value #t when
the target process exits.
> (set-running! val)
> (set-running! val process)
> (set-running! event)
> (set-running! event process)
Launches the execution of the target process. Execution continues
as long as the given behavior is true (aka, any value beside #f),
or until an event comes on the given event stream with the value
#f. When execution pauses, the target remains on the line where
the pause occured. You can then inspect the state of the program,
or resume execution with another call to SET-RUNNING!.
> (set-main! require-spec)
> (set-main! require-spec process)
Sets the file where execution begins when SET-RUNNING! is called
for the first time. When SET-MAIN! is not used explicitly,
execution begins with the file specified in the first call to
TRACE. It is an error to call SET-RUNNING! without first calling
either TRACE or SET-MAIN!.
> (where)
> (where process)
Returns an event stream that contains one event for each expression
evaluated in the target process. Combined with HISTORY-B, this makes
it possible to record entire execution traces for the target program.
> (kill)
Kills the target process and releases all resources
it used -- you cannot resume after a KILL.
This will not stop evaluation of the MzTake script, however. In
particular, if the script depends on input the varies independently
of the target process, FrTime will continue to update them. You can
use "Kill" command from DrScheme's "Scheme" menu to stop both the
MzTake script and its target process at once.
Also, note that closing a FrTime animation/graphics window does *not*
kill a running MzTake process.
> (kill-all)
When using more than one target process at a time, KILL-ALL invokes
KILL on all of them at once.
> (current-process)
> (current-process process)
The CURRENT-PROCESS parameter gets or sets the process manipulated
by the MzTake function when they are not provided with a process
argument. The CURRENT-PROCESS parameter is initialized with a blank
process, and you can create additional processes using the
CREATE-DEBUG-PROCESS function. Using more than one process at a
time lets your MzTake run multiple programs different at once and
compare their output using a single script.
> (create-debug-process)
Creates a fresh blank debug process. Each debug process has its own
set of trace points, its own run trigger (set via SET-RUNNING!),
its own exceptions stream, etc. Each debug process run
independently from the others, and they can be paused and killed
individually. All debug processes in a single MzTake script share
the same FrTime event space, and so it is possible to compare
output and traces between each of them.
> (current-policy)
> (current-policy policy)
Every file executed under MzTake can run either in fast mode or in
debuggable mode. The CURRENT-POLICY decides which.
- debuggable mode: the file is instrumented with MzTake debugging
information. It can be the target of tracepoint and it generate
events on the WHERE stream. Execution can also be paused in the middle
of code running in debuggable mode. The instrumentation overhead
is considerable, however, of the order of 10x-20x slowdown.
- fast mode: the file is not instrumented, and runs at its normal
speed, but cannot be debugged. Inserting trace points into fast
mode files after the beginning of the execution has no
effect. Also, pausing while executing a fast mode file will be
delayed until execution reaches a debuggable mode file.
Files that are the target of a trace point when first lunching the
process run in debuggable mode, so is the main file set by
SET-MAIN!. Otherwise, the current policy is consulted to decide
between fast and debuggable mode. In this case, if the policy does
not decide, MzTake raises an error.
Policies have the following contract:
(listof (list/c (symbols 'fast 'debuggable)
(union (symbols 'everything-else)
path?
string?
(listof (union path? string?)))))
A policy consist of a list of entries. Each entry is a pair
specifying either fast mode or debuggable mode, then a directory,
or a list of directories. Files in these directories, or their
subdirectories will run under the given mode. The special symbol
'everything-else can be used instead of a directory, and this will
match any file. The policy is checked in order, and the first entry
that applies to the given filename assign a mode the file.
The default policy run files of the directories specified by
CURRENT-LIBRARY-COLLECTIONS-PATHS in fast mode, and runs everything
else in debuggable mode. This poloicy is set as follow:
(current-policy `((fast ,(current-library-collection-paths))
(debuggable everything-else)))
You can change this policy by calling the
CURRENT-POLICY function with a new policy as an argument. The
policy is assigned to a process when the process lunches.
_Useful Functions for Time-Varying Values_
Note: FrTime uses a naming convention where functions which
return behaviors have names that end in "-b", and
functions that return event streams end in "-e".
Tips: When you have a behavior that you want to turn into
an event, use "(changes behavior)".
When you have an event that you want to be a
behavior, use "(hold event)"
MzTake defines a few functions on time-varying values
that are particularly useful when debugging. You can require these
functions with (require (lib "useful-code.ss" "mztake"))
> (history-e stream)
> (history-b stream)
Keeps a complete history of all the values seen
on an event stream as a list, oldest events last.
Use with BINDs: (history-b x-trace)
> (history-e stream n)
> (history-b stream n)
Keeps a list of the last n values of a behavior
Returns a list of at most n elements, where the
elements are the n last values seem on the stream,
in order, oldest first.
> (count-b stream)
Counts number of events seen on an eventstream.
Often used directly on ENTRY traces, counting how many
times ENTRY occured: (count-b entry-trace)
Also useful to count how many times a BIND changed
by calling: (count-b (changes bind-trace))
> (largest-val-b stream)
> (smallest-val-b stream)
Keeps track of the largest/smallest values seen on a stream.
Use with BINDs: (largest-val-b (changes bind-trace)).
> (sequence-match? seq stream)
Matches a sequence of items in a list to the history
of event pings, on the event stream evs. Returns #t
when it matches, and #f otherwise. Use when you expect
a certain pattern to show up, and want to know when:
(sequence-match? '(0 1 2 1 0) (changes bind-trace))
> (printf-b format-string arg ...)
Displays the value of the behaviors with the given format,
using "~a" just like in Scheme's FORMAT function.
============================================================
Known Problems
* In general, you should not REQUIRE in your MzTake script any
functions that act on the structures of your target program.
ORIGINAL FILE:
(define-struct foo (a b))
(let ([x (make-foo 1 2)])
x)
MZTAKE SCRIPT:
(require "original-file.ss")
(define/bind (loc "original-file.ss" 3) x)
(foo-a x) ;; this will fail!
The target program and the MzTake will have different
instances of the struct, and the call to FOO-A will fail.
Instead, use BIND to bring the function from the target program to
the script:
(define/bind (loc "original-file.ss" 3) x foo-a)
(foo-a x) ;; this succeeds
* The break button will *not* kill runaway client processes.
You must type (kill) or (kill-all).
* Some legal syntax locations (used in setting trace points)
are unreachable during program execution (they do not get
triggered and produce empty eventstreams). For instance,
the name clause of a LET is never the current point of execution:
(define x 12)
(let ([x (add1 x)]) x)
^
Recommended syntax locations to use for trace points:
(define x 12)
(let ([x (add1 x)]) x)
^ ^^ ^ ^
* Watch out: when you change target code, your line/col locations in
the script will drift out of align.
* Error handling is not perfect -- e.g., the little "bug"
buttons on syntax errors don't reference the correct code.
However, the messages that are printed are as accurate as
possible.
* You can add trace points to the body first-class functions, and they
will send trace update from anywhere they are passed to and invoked.
============================================================
Authors and Thanks
MzTake is an experimental debugger. It should enable new
debugging approaches that were not possible (easily) before.
Please send feedback to the PLT-Scheme mailing list:
http://www.plt-scheme.org/maillist/
We are eager to hear about how you are using MzTake!
Jonathan Spiro
Guillaume Marceau
Gregory Cooper
John Clements
Shriram Krishnamurthi
Please send bug reports to: jspiro@cs.brown.edu
---
Icons for MzTake come from the Gnome Project: Nautilus Emblems.
These are provided under the GPL license.
http://jimmac.musichall.cz/ikony.php3

View File

@ -1,445 +0,0 @@
(module engine mzscheme
(require "marks.ss"
(lib "etc.ss")
(lib "list.ss")
(lib "match.ss")
(prefix frp: (lib "lang-ext.ss" "frtime"))
(rename (lib "frp-core.ss" "frtime")
frp:signal-thunk signal-thunk)
"useful-code.ss"
(lib "base-gm.ss" "frtime")
"mztake-structs.ss"
"load-sandbox.ss"
"annotator.ss"
(lib "match.ss"))
(provide process:set-main!
current-policy
all-debug-processes
create-debug-process
process:->dead
process:new->running
process:running->finished
process:running->paused
process:paused->running
pause
resume
trace*
read-all-syntax
pattern->pos)
;Keeps track of all debugging processes
(define all-debug-processes null)
; produces a nested list of (line column offset span) for all addressable syntax
(define (unwrap-syntax stx)
(let ([elt (list (syntax-line stx)
(syntax-column stx)
(sub1 (syntax-position stx))
(syntax-span stx))])
(syntax-case stx ()
[(item ...) (cons elt (map unwrap-syntax (syntax->list stx)))]
[x elt])))
(define (read-all-syntax filename)
(parameterize ([port-count-lines-enabled #t])
(let ([port (open-input-file filename)])
(begin0
(let loop ([stx (read-syntax filename port)])
(if (eof-object? stx) '()
(cons stx
(loop (read-syntax filename port)))))
(close-input-port port)))))
;; returns a memoized function that takes (line column) -> (list/c position span)
;; line-col->pos : (debug-file? . -> . (number? number? . -> . (union void? number?)))
(define (line-col->pos stx)
(let ([pos-list (flatten (map unwrap-syntax stx))])
(lambda (line maybe-col)
(let loop ([lst pos-list])
(cond
[(empty? lst)
(error 'loc
"No syntax found for trace at line/column ~a:~a in `~a'"
line maybe-col (syntax-source stx))]
[(and (<= line (first (first lst)))
(or (not maybe-col)
(<= maybe-col (second (first lst)))))
(list (third (first lst)) (fourth (first lst)))]
[else (loop (rest lst))])))))
(define (pattern->pos stx-lst)
(define (quote-symbols lst)
(match lst
[(? symbol? s) `(quote ,s)]
[() empty]
[('_ tl ...) `(_ ... ,@(quote-symbols tl))]
[(hd tl ...) (cons (quote-symbols hd)
(quote-symbols tl))]
[item item]))
(define (collect-locations h stx lst)
(let loop ([stx stx] [lst lst])
(cond
[(empty? stx) (void)]
[(pair? stx)
(loop (first stx) (first lst))
(loop (rest stx) (rest lst))]
[else
(when (syntax-line stx)
(hash-put! h lst (list (sub1 (syntax-position stx)) (syntax-span stx))))
(when (pair? (syntax-e stx))
(loop (first (syntax-e stx)) (first lst))
(loop (rest (syntax-e stx)) (rest lst)))])))
(define stx2line-col (make-hash))
(define lst (map syntax-object->datum stx-lst))
(for-each (lambda (s l) (collect-locations stx2line-col s l))
stx-lst lst)
(lambda (pattern)
(let ([pred (eval `(begin (require (lib "match.ss"))
(lambda (v)
(match v
[,(quote-symbols pattern) #t]
[_ #f]))))])
(let loop ([lst lst])
(let ([sub (if (pair? lst)
(append (loop (first lst))
(loop (rest lst)))
empty)])
(if (and (hash-mem? stx2line-col lst) (pred lst))
(cons (hash-get stx2line-col lst) sub)
sub))))))
(define (find-client process modpath)
(cond
[(memf (lambda (c) (equal? (debug-client-modpath c) modpath))
(debug-process-clients process)) => first]
[else false]))
(define (find-client/create process modpath)
(or (find-client process modpath)
(create-debug-client process modpath)))
(define (process:set-main! p reqspec)
(let* ([modpath (reqspec->modpath reqspec)]
[client (find-client/create p modpath)])
(set-debug-process-main-client! p client)))
(define (break? process client)
(let ([tracepoints (and client (debug-client-tracepoints client))])
(lambda (pos)
(or (debug-process-pause-requested? process)
(and tracepoints
(hash-get tracepoints (sub1 pos) (lambda () false)))))))
(define (traces->events traces vals)
(map (lambda (t)
(list (trace-struct-evnt-rcvr t)
(apply (trace-struct-thunk t) vals)))
traces))
(define (receive-result process client byte-offset top-mark rest-marks vals)
(let* ([traces (hash-get (debug-client-tracepoints client) byte-offset (lambda () empty))]
[no-traces? (empty? traces)]
[has-single-trace? (and (not no-traces?) (empty? (rest traces)))]
[no-where? (not (debug-process-where process))]
[no-events? (and no-traces? no-where?
(not (debug-process-pause-requested? process)))])
(unless no-events?
(let* ([marks (cons top-mark (continuation-mark-set->list rest-marks debug-key))])
(set-debug-process-marks! process marks)
(if no-where?
;; No where event to generate
(cond [has-single-trace?
;; fast-path
(let* ([t (first traces)]
[e (apply (trace-struct-thunk t) vals)])
(frp:send-synchronous-event (trace-struct-evnt-rcvr t) e))]
[no-traces? (void)]
[else (frp:send-synchronous-events (traces->events traces vals))])
;; With a where event to generate
(let ([where-event (debug-process-where process)]
[w (map (compose syntax-local-infer-name mark-source) marks)])
(if no-traces?
(frp:send-synchronous-event where-event w)
(let* ([where-event (list where-event w)]
[trace-events (traces->events traces vals)])
(frp:send-synchronous-events (cons where-event trace-events))))))
;; Now that we processed the trace, do we want to pause or continue
(when (debug-process-pause-requested? process)
(let loop ()
(unless (debug-process-resume-requested? process)
(semaphore-wait (debug-process-run-semaphore process))
(loop)))
(set-debug-process-pause-requested?! process false)
(set-debug-process-resume-requested?! process false))
(set-debug-process-marks! process false)))))
(define ((break-after process client) top-mark marks . vals)
(let* ([stx (mark-source top-mark)]
[byte-offset (+ (syntax-position stx) (syntax-span stx) -2)])
(receive-result process client byte-offset top-mark marks vals)
(apply values vals))) ; TODO: allow modification of the return value
(define ((break-before process client) top-mark marks)
(let ([byte-offset (sub1 (syntax-position (mark-source top-mark)))])
(receive-result process client byte-offset top-mark marks empty) ; TODO: allow substitute value
false))
(define (unbuild-path path)
(let-values ([(base name _) (split-path path)])
(if base
(append (unbuild-path base) (list name))
empty)))
(define (head lst n)
(if (= n 0)
empty
(cons (first lst) (head (rest lst) (sub1 n)))))
(define (dir-contains? dir filename)
(let ([dir-lst (unbuild-path dir)]
[file-lst (unbuild-path filename)])
(and (< (length dir-lst) (length file-lst))
(equal? dir-lst (head file-lst (length dir-lst))))))
(define (map-policy-tag tag)
(cond [(eq? tag 'fast) false]
[(eq? tag 'debuggable) true]
[else (error 'map-policy-tag "unknown policy tag ~a" tag)]))
(define (policy-requests-annotatation? policy filename)
(if (empty? policy)
true
(let ([tag (first (first policy))]
[collect-paths (second (first policy))])
(map-policy-tag tag) ;; complains if the tag doesn't exists
(if (or (eq? collect-paths 'everything-else)
(ormap (lambda (dir) (dir-contains? dir filename))
(if (list? collect-paths)
collect-paths
(list collect-paths))))
(map-policy-tag tag)
(policy-requests-annotatation? (rest policy) filename)))))
(define (process-has-file? process filename)
(and
(memf (lambda (c) (equal? (debug-client-modpath c)
(path->string filename)));; TODO: harmonize path & string
(debug-process-clients process))
true))
(define ((record-top-level-id process) module-name var-name val)
(let* ([modules (debug-process-top-level process)]
[bindings (hash-get modules module-name (lambda () (make-hash)))])
(unless (hash-mem? modules module-name)
(hash-put! modules module-name bindings))
(hash-put! bindings var-name val)))
(define (launch-sandbox process)
(unless (debug-process-main-client process)
(error 'launch-sandbox
"No main file specified. Use TRACE or SET-MAIN! to indicate where to start execution"))
(parameterize ([current-inspector (make-inspector)])
(require/sandbox+annotations
(debug-process-custodian process)
;; error-display-handler :
(let ([orig-err-disp (error-display-handler)])
(lambda (msg exn)
(frp:send-event (debug-process-exceptions process) exn)
(orig-err-disp msg exn)))
;; target file
`(file ,(debug-client-modpath (debug-process-main-client process)))
;; annotate-module?
(lambda (filename module-name)
#;(print-each "annotate-module?"
filename
(process-has-file? process filename)
(policy-requests-annotatation? (debug-process-policy process) filename))
(or (process-has-file? process filename)
(policy-requests-annotatation? (debug-process-policy process) filename)))
;; annotator
(lambda (stx)
(if (not (syntax-source stx))
stx
(let*-values ([(client)
(find-client/create
process
(path->string (syntax-source stx)))]
[(annotated-stx pos-list)
(annotate-for-single-stepping
stx
(lambda (_) (break? process client))
(break-before process client)
(break-after process client)
(lambda (kind bound binding) (void))
(record-top-level-id process))])
annotated-stx))))))
(define (process:new->running process)
(printf "mztake: starting ~a~n" (debug-client-modpath (debug-process-main-client process)))
(set-debug-process-run-semaphore! process (make-semaphore))
(set-debug-process-policy! process (current-policy))
(thread (lambda ()
(launch-sandbox process)
(process:running->finished process))))
(define (process:running->finished process)
(process:->dead process))
(define (process:->dead process)
(printf "mztake: finished ~a~n" (debug-client-modpath (debug-process-main-client process)))
(set! all-debug-processes (remq process all-debug-processes))
(custodian-shutdown-all (debug-process-custodian process))
(frp:set-cell! (debug-process-exited? process) true))
(define (process:running->paused process)
(set-debug-process-pause-requested?! process true))
(define (process:paused->running process)
(set-debug-process-resume-requested?! process true)
(semaphore-post (debug-process-run-semaphore process)))
(define (pause process)
(when (and (debug-process-run-semaphore process)
(not (frp:value-now (debug-process-exited? process)))
(not (debug-process-pause-requested? process)))
(process:running->paused process)))
(define (resume process)
(cond
[(not (debug-process-run-semaphore process)) (process:new->running process)]
[(and (not (frp:value-now (debug-process-exited? process)))
(debug-process-pause-requested? process)
(not (debug-process-resume-requested? process)))
(process:paused->running process)]))
(define current-policy (make-parameter `((fast ,(current-library-collection-paths))
(debuggable everything-else))))
(define (create-debug-process)
(letrec ([running-e (frp:new-cell frp:never-e)]
[run-manager (running-e . frp:==> .
(lambda (r)
(if r (resume process) (pause process))))]
[process (make-debug-process (make-custodian)
false ; run-semaphore - false so we know it has never started
running-e ; running-e
run-manager ; run-manager
false ; pause-requested?
false ; resume-requested?
false ; policy
(frp:new-cell false) ; exited?
(frp:event-receiver) ; exceptions
false ; main-client
empty ; clients
(make-hash 'equal ) ; top-level
false ; where
false)]) ; marks
(set! all-debug-processes (cons process all-debug-processes))
process))
; Creates a debugger client
; (debug-process? require-path. -> . debug-file?)
(define (create-debug-client process modpath)
; throwaway namespace so the module-name-resolver doesn't load an unannotated module
(parameterize ([current-namespace (make-namespace)])
(let ([client (create-empty-debug-client)]
[stx (read-all-syntax modpath)])
(for-each (lambda (c)
(when (equal? modpath (debug-client-modpath c))
(raise-syntax-error 'mztake:script-error:create-debug-client
(format "A client for `~a' is already defined for this process." modpath))))
(debug-process-clients process))
(set-debug-client-modpath! client modpath)
(set-debug-client-process! client process)
(set-debug-client-line-col->pos! client (line-col->pos stx))
(set-debug-client-pattern->pos! client (pattern->pos stx))
(set-debug-process-clients! process
(append (list client) (debug-process-clients process)))
; set the main module if it has not been set
; this implies that the first client created is always the main module
(unless (debug-process-main-client process)
(set-debug-process-main-client! process client))
client)))
(define (reqspec->modpath filename)
(define (build-module-filename str) ; taken from module-overview.ss
(let ([try (lambda (ext)
(let ([tst (string-append str ext)])
(and (file-exists? tst) tst)))])
(or (try ".ss") (try ".scm") (try "") str)))
(let ([modpath (symbol->string ((current-module-name-resolver) filename #f #f))])
(build-module-filename
(if (regexp-match #rx"^," modpath)
(substring modpath 1 (string-length modpath))
modpath))))
(define (loc->positions client loc)
(let ([pos&spans
(if (loc/lc? loc)
(list ((debug-client-line-col->pos client) (loc/lc-line loc) (loc/lc-col loc)))
((debug-client-pattern->pos client) (loc/p-pattern loc)))])
(if (loc-after? loc)
(map (lambda (p&s) (+ (first p&s) (second p&s) -1)) pos&spans)
(map first pos&spans))))
(define (trace* p loc thunk)
(let* ([modpath (reqspec->modpath (loc-reqspec loc))]
[client (find-client/create p modpath)]
[trace-hash (debug-client-tracepoints client)]
[trace (make-trace-struct (frp:event-receiver) thunk)]
[positions (loc->positions client loc)])
; add the trace to the list of traces for these byte-offsets
(for-each (lambda (pos)
(hash-put! trace-hash pos
(append (hash-get trace-hash pos (lambda () '()))
(list trace))))
positions)
(trace-struct-evnt-rcvr trace)))
(define (syntax-local-infer-name stx)
(or (syntax-property stx 'inferred-name)
(let ([s (syntax-source stx)])
(and s
(let ([s (cond
[(path? s) (path->string s)]
[else s])]
[l (syntax-line stx)]
[c (syntax-column stx)])
(if l
(string->symbol (format "~a:~a:~a" s l c))
(let ([p (syntax-position stx)])
(string->symbol (format "~a::~a" s p)))))))))
)

View File

@ -1,84 +0,0 @@
(module mztake-structs mzscheme
(require (lib "match.ss")
(lib "etc.ss")
(lib "base-gm.ss" "frtime"))
(provide (all-defined))
(define require-spec?
(match-lambda
[(? string?) true]
[('file (? string?)) true]
[('lib (? string?) (? string?) ...) true]
[('planet . arg) true]
[else false]))
; ;;;;; ; ;
; ; ; ; ;
; ; ; ;
; ; ;;;;;; ;;; ; ; ;;; ;;;;; ;;;;
; ; ; ;; ; ; ; ; ; ; ;
; ;;; ; ; ; ; ; ; ;
; ;;; ; ; ; ; ; ; ;
; ; ; ; ; ; ; ; ;;;;
; ; ; ; ; ; ; ; ;
; ; ; ; ; ; ; ; ;
; ; ; ; ; ; ;; ; ; ; ; ;
; ;;;;; ;;;; ;;;; ; ;;; ;;; ;;;;
(define-struct trace-struct (evnt-rcvr thunk)) ; frp:event-receiver
(define-struct debug-client (modpath ; complete-path of the module
tracepoints ; hash-table of traces
line-col->pos ; memoized O(n) function to map line/col -> byte offset
pattern->pos
process)) ; parent debug-process
(define-struct debug-process (custodian ; If you shutdown-all it will kill the debugger process
run-semaphore ; When you post to this the debuggee will continue executing
running-e ; #t on this event resumes, #f pauses
run-manager ; saves behavior that actually pauses/resumes from GC
pause-requested?
resume-requested?
policy
exited? ; FrTime cell receives #t when the target exits
exceptions ; (an event stream) Exceptions thrown during the evaluation of the target
main-client ; the main client module that will be run
clients ; list of all the clients attached to this process
top-level
where ; a behavior signaling each position where we pause
marks)) ; while paused, the marks at the point of the pause (else false)
(define-struct loc (reqspec after?))
(define-struct (loc/lc loc) (line col))
(define-struct (loc/p loc) (pattern))
;###########################################################################################################
; ;;;;; ; ; ;;;;; ;
; ; ; ; ; ;; ; ;
; ; ; ; ; ;
; ; ;;;;;; ;;; ; ; ;;; ;;;;; ; ; ;;; ;;; ;;;; ;;;;; ;;;; ; ;;; ;;;;
; ; ; ;; ; ; ; ; ; ; ;; ; ; ; ; ; ; ; ;; ; ;
; ;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
; ;;; ; ; ; ; ; ; ; ; ; ; ;;;;; ; ; ; ; ;
; ; ; ; ; ; ; ; ; ; ;;;;;;; ; ; ; ; ; ; ;;;;
; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
; ; ; ; ; ; ;; ; ; ; ;; ; ; ; ; ; ;; ; ; ; ; ; ;
; ;;;;; ;;;; ;;;; ; ;;; ;;; ;;;;; ; ;;;; ;;;; ; ;;; ;;;; ; ;;;;
(define (create-empty-debug-client)
(make-debug-client null ; modpath
(make-hash) ; tracepoints
null ; line-col->pos function
null
null)) ; process
;###########################################################################################################
)

View File

@ -1,176 +0,0 @@
(module mztake mzscheme
(require (lib "contract.ss")
(lib "match.ss")
(prefix frp: (lib "lang-ext.ss" "frtime"))
(rename (lib "frtime.ss" "frtime") frp:list list)
(rename (lib "frtime.ss" "frtime") frp:value-nowable? value-nowable?)
(rename (lib "frtime.ss" "frtime") frp:behaviorof behaviorof)
"mztake-structs.ss"
(lib "base-gm.ss" "frtime")
(lib "etc.ss")
(lib "list.ss")
"marks.ss"
"engine.ss")
;; Turn struct printing on for MzTake users.
(print-struct true)
(provide (rename loc loc$)
debug-process-running-e
loc/r
trace
bind
define/bind
define/bind-e
[rename loc/opt-col loc]
[rename mztake-top #%top])
(provide/contract [exceptions (() (debug-process?) . opt-> . frp:event?)]
[exited? (() (debug-process?) . opt-> . frp:behavior?)]
[kill (() (debug-process?) . opt-> . void?)]
[kill-all (-> void?)]
[set-running-e! ((frp:event?) (debug-process?) . opt-> . any)]
[set-running! ((frp:value-nowable?) (debug-process?) . opt-> . any)]
[where (() (debug-process?) . opt-> . frp:event?)]
[current-policy (case-> (-> any)
(any/c . -> . void?))]
[current-process (case-> (-> debug-process?)
(debug-process? . -> . void?))]
[current-reqspec (case-> (-> string?)
(string? . -> . void?))]
[create-debug-process (-> debug-process?)]
[set-main! ((require-spec?) (debug-process?) . opt-> . void?)]
[trace* (debug-process? loc? (-> any) . -> . frp:event?)]
[bind* (debug-process? symbol? . -> . any)])
(define (loc* after?)
(define (set-r r) (current-reqspec r))
(match-lambda*
[(arg) ((loc* after?) (current-reqspec) arg)]
[((and (not (? require-spec?)) arg) args ...) (apply (loc* after?) (current-reqspec) arg args)]
[((? require-spec? r) (? number? line)) (set-r r) (make-loc/lc r after? line false)]
[((? require-spec? r) (? number? line) (? number? col)) (set-r r) (make-loc/lc r after? line col)]
[((? require-spec? r) pattern) (set-r r) (make-loc/p r after? pattern)]))
(define loc/r (loc* true))
(define loc/opt-col (loc* false))
(define exceptions
(opt-lambda ([p (current-process)])
(debug-process-exceptions p)))
(define exited?
(opt-lambda ([p (current-process)])
(debug-process-exited? p)))
(define kill
(opt-lambda ([p (current-process)])
(unless (debug-process-exited? p)
(process:->dead p))))
(define (kill-all)
(unless (empty? all-debug-processes)
(for-each (lambda (p) (kill p)) all-debug-processes)
(display "All debug processes have been killed.")))
(define set-running-e!
(opt-lambda (e [process (current-process)])
(resume process)
(frp:set-cell! (debug-process-running-e process) e)))
(define set-running!
(opt-lambda (b [process (current-process)])
(if (frp:value-now b) (resume process) (pause process))
(frp:set-cell! (debug-process-running-e process) (frp:changes b))))
(define where
(opt-lambda ([p (current-process)])
(unless (debug-process-where p)
(set-debug-process-where! p (frp:event-receiver)))
(debug-process-where p)))
(define current-process (make-parameter (create-debug-process)))
(define current-reqspec (make-parameter false))
(define set-main!
(opt-lambda (reqspec [p (current-process)])
(current-reqspec reqspec )
(process:set-main! p reqspec)))
(define-syntax trace
(syntax-rules (=>)
[(_ loc)
(let ([loc* loc])
(if (loc-after? loc*)
(trace* (current-process) loc* identity)
(trace* (current-process) loc* (lambda () true))))]
[(_ loc => proc)
(trace* (current-process) loc proc)]
[(_ loc body ...)
(trace* (current-process) loc (lambda () body ...))]))
(define (mztake-top* name thunk)
(if (debug-process-marks (current-process))
(with-handlers
([exn:fail?
(lambda (exn)
(with-handlers
([exn:fail? (lambda (exn2) (raise exn2))])
(bind* (current-process) name)))])
(thunk))
(thunk)))
(define-syntax (mztake-top stx)
(syntax-case stx ()
[(_ . name)
#'(mztake-top* 'name (lambda () (#%top . name)))]))
(define (lookup-in-top-level p name)
(let/ec success
(define (try m)
(let/ec fail
(define (fail*) (fail false))
(success (hash-get (hash-get (debug-process-top-level p) m fail*) name fail*))))
(for-each try (map mark-module-name (debug-process-marks p)))
(hash-for-each (debug-process-top-level p) (lambda (m ns) (try m)))
(error 'bind "variable `~a' not found in target at the current location" name)))
(define (bind* p name)
(unless (debug-process-marks p)
(error "Bind called but the target process is not paused."))
(let ([bs (lookup-all-bindings
(lambda (id) (eq? (syntax-e id) name))
(debug-process-marks p))])
(if (empty? bs)
(lookup-in-top-level p name)
(mark-binding-value (first bs)))))
(define-syntax bind
(syntax-rules ()
[(_ (name ...) body0 body ...)
(let* ([p (current-process)]
[name (bind* p 'name)]
...)
body0 body ...)]))
(define-syntax define/bind-e
(syntax-rules ()
[(_ loc name ...)
(begin
(define here loc)
(define name (trace here (bind (name) name)))
...)]))
(define-syntax define/bind
(syntax-rules ()
[(_ loc name ...)
(begin
(define here loc)
(define name (frp:hold (trace here (bind (name) name))))
...)]))
)

View File

@ -1,64 +0,0 @@
(module useful-code frtime
(require (lib "string.ss")
(lib "contract.ss")
(lib "list.ss"))
(provide (all-defined))
; Keeps a list of the last n values of a behavior
(define/contract history-e (case-> (event? . -> . any)
(event? number? . -> . any))
(case-lambda [(stream)
(define ((add-to-complete-hist x) hist) (append hist (list x)))
(accum-e (stream . ==> . add-to-complete-hist) empty)]
[(stream n)
(define ((add-to-short-hist x) hist) (append (if (< (length hist) n) hist (rest hist)) (list x)))
(accum-e (stream . ==> . add-to-short-hist) empty)]))
(define/contract history-b (case-> (event? . -> . any)
(event? number? . -> . any))
(case-lambda [(stream) (hold (history-e stream) empty)]
[(stream n) (hold (history-e stream n) empty)]))
; Counts number of events on an event stream
(define/contract count-b (event? . -> . any)
(lambda (stream)
(hold (accum-e (stream . -=> . add1) 0) 0)))
; Keeps track of the largest value seen on a stream
(define/contract largest-val-b (event? . -> . any)
(lambda (stream)
(hold (accum-e (stream
. ==> .
(lambda (last)
(lambda (x)
(if (> x last) x last))))
-inf.0))))
; Keeps track of the smallest value seen on a stream
(define/contract smallest-val-b (event? . -> . any)
(lambda (stream)
(hold (accum-e (stream
. ==> .
(lambda (last)
(lambda (x)
(if (< x last) x last))))
+inf.0))))
; Matches a sequence of items in a list to event pings
(define/contract sequence-match? ((listof any/c) . -> . any)
(lambda (seq evs)
(equal? seq (history-b evs (length seq)))))
; Cheap printf for behaviors
(define printf-b format)
; Flattens a list
(define (flatten x)
(cond ((empty? x) '())
((and (list? x)
(list? (first x)))
(append (flatten (car x)) (flatten (cdr x))))
(else (list x)))))

View File

@ -1,12 +0,0 @@
(module engine-test mzscheme
(require "../engine.ss")
(define stx (read-all-syntax "../demos/dijkstra/dijkstra.ss"))
(define fn (pattern->pos stx))
(define result (fn '(define (_ ...) _ ...)))
(define expected '((7 2 147) (18 2 463)))
(printf "~a~n" (list (equal? result expected) result expected))
)

View File

@ -1,7 +0,0 @@
(require (as-is mzscheme load)
(as-is "test-harness.ss" test))
(load "../demos/dijkstra/dijkstra-mztake.ss")
(map-e (lambda (e)
(unless e
(test (dv:vector-length (t-data heap)) 5)))
(debug-process-running-e (current-process)))

View File

@ -1,75 +0,0 @@
(module test-harness mzscheme
(provide (all-defined))
(require (lib "list.ss")
(lib "etc.ss")
(lib "pretty.ss"))
(define print-tests (make-parameter #f))
(define test-inspector (make-parameter (current-inspector)))
(define test-inexact-epsilon (make-parameter 0.01))
(define-struct (exn:test exn) ())
(define (install-test-inspector)
(test-inspector (current-inspector))
(current-inspector (make-inspector))
(print-struct #t))
(define (may-print-result result)
(parameterize ([current-inspector (test-inspector)]
[print-struct #t])
(when (or (eq? (print-tests) (first result))
(eq? (print-tests) #t))
(pretty-print result))
(when (and (eq? (print-tests) 'stop)
(eq? (first result) 'bad))
(raise (make-exn:test (format "test failed: ~a" result)
(current-continuation-marks))))))
(define test
(opt-lambda (result expected [compare equal?])
(let* ([test-result
(cond [(or (and (number? result) (not (exact? result)))
(and (number? expected) (not (exact? expected))))
(< (abs (- result expected)) (test-inexact-epsilon))]
[else
(parameterize ([current-inspector (test-inspector)])
(compare result expected))])]
[to-print (if test-result
(list 'good result expected)
(list 'bad result expected))])
(may-print-result to-print)
to-print)))
(define (test/pred result pred)
(let* ([test-result (pred result)]
[to-print (if test-result
(list 'good result test-result)
(list 'bad result test-result))])
(may-print-result to-print)
to-print))
(define (test/exn thunk expected-exception-msg)
(unless (and (procedure? thunk)
(procedure-arity-includes? thunk 0))
(error (format
"the first argument to test/exn should be a function of no arguments (a \"thunk\"), got ~a"
thunk)))
(let* ([result
(with-handlers
([void (lambda (exn) exn)])
(thunk))]
[test-result
(if (and (exn? result)
(regexp-match expected-exception-msg (exn-message result)))
(list 'good result expected-exception-msg)
(list 'bad result expected-exception-msg))])
(may-print-result test-result)
test-result))
(install-test-inspector)
)