Initial revision
authorChris Hanson <org/chris-hanson/cph>
Wed, 12 Oct 1994 07:54:09 +0000 (07:54 +0000)
committerChris Hanson <org/chris-hanson/cph>
Wed, 12 Oct 1994 07:54:09 +0000 (07:54 +0000)
v7/src/edwin/eystep.scm [new file with mode: 0644]
v7/src/runtime/ystep.scm [new file with mode: 0644]

diff --git a/v7/src/edwin/eystep.scm b/v7/src/edwin/eystep.scm
new file mode 100644 (file)
index 0000000..89bae4c
--- /dev/null
@@ -0,0 +1,298 @@
+;;; -*-Scheme-*-
+;;;
+;;;    $Id: eystep.scm,v 1.1 1994/10/12 07:54:09 cph Exp $
+;;;
+;;;    Copyright (c) 1994 Massachusetts Institute of Technology
+;;;
+;;;    This material was developed by the Scheme project at the
+;;;    Massachusetts Institute of Technology, Department of
+;;;    Electrical Engineering and Computer Science.  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. Users of this software agree to make their best efforts (a)
+;;;    to return to the MIT Scheme project any improvements or
+;;;    extensions that they make, so that these may be included in
+;;;    future releases; and (b) to inform MIT of noteworthy uses of
+;;;    this software.
+;;;
+;;;    3. All materials developed as a consequence of the use of this
+;;;    software shall duly acknowledge such use, in accordance with
+;;;    the usual standards of acknowledging credit in academic
+;;;    research.
+;;;
+;;;    4. MIT has made no warrantee or representation that the
+;;;    operation of this software will be error-free, and MIT is
+;;;    under no obligation to provide any services, by way of
+;;;    maintenance, update, or otherwise.
+;;;
+;;;    5. In conjunction with products arising from the use of this
+;;;    material, there shall be no use of the name of the
+;;;    Massachusetts Institute of Technology nor of any adaptation
+;;;    thereof in any advertising, promotional, or sales literature
+;;;    without prior written consent from MIT in each case.
+;;;
+;;; NOTE: Parts of this program (Edwin) were created by translation
+;;; from corresponding parts of GNU Emacs.  Users should be aware that
+;;; the GNU GENERAL PUBLIC LICENSE may apply to these parts.  A copy
+;;; of that license should have been included along with this file.
+;;;
+
+;;;; Edwin Interface to YStep
+
+(declare (usual-integrations))
+\f
+(define-command step-expression
+  "Single-step an expression."
+  "xExpression to step"
+  (lambda (expression)
+    (with-stepper-evaluation-context
+      (lambda ()
+       (step-form expression (evaluation-environment #f))))))
+
+(define-command step-last-sexp
+  "Single-step the expression preceding point."
+  ()
+  (lambda ()
+    (step-region (let ((point (current-point)))
+                  (make-region (backward-sexp point 1 'ERROR) point))
+                (evaluation-environment #f))))
+
+(define-command step-defun
+  "Single-step the definition that the point is in or before."
+  ()
+  (lambda ()
+    (step-region (let ((start (current-definition-start)))
+                  (make-region start (forward-sexp start 1 'ERROR)))
+                (evaluation-environment #f))))
+
+(define (step-region region environment)
+  (with-stepper-evaluation-context
+    (lambda ()
+      (step-form (with-input-from-region region read) environment))))
+
+(define (with-stepper-evaluation-context thunk)
+  (bind-condition-handler (list condition-type:error)
+      evaluation-error-handler
+    (lambda ()
+      (with-input-from-port dummy-i/o-port
+       (lambda ()
+         (with-output-to-transcript-buffer thunk))))))
+\f
+;;;; Stepper Mode
+
+(define-major-mode stepper read-only-noarg "Stepper"
+  "Major mode for the stepper.
+space  advances the computation by one step
+o      steps over the current expression
+u      step over, but show the intevening events
+U      step over, show and animate intervening events
+r      run current expression to completion without stepping
+e      expand the step under the cursor
+c      contract the step under the cursor")
+
+(define-key 'stepper #\space 'stepper-step)
+(define-key 'stepper #\o 'stepper-step-over)
+(define-key 'stepper #\u 'stepper-step-until)
+(define-key 'stepper #\U 'stepper-step-until-visible)
+(define-key 'stepper #\r 'stepper-run)
+(define-key 'stepper #\e 'stepper-expand)
+(define-key 'stepper #\c 'stepper-contract)
+(define-key 'stepper #\? 'stepper-summary)
+
+(define-command stepper-summary
+  "Summarize the stepper commands in the typein window."
+  ()
+  (lambda ()
+    (message "Space: single step, o: step over, e: expand, c: contract")))
+
+(define-command stepper-step
+  "Single step.  With argument, step multiple times."
+  "p"
+  (lambda (argument) (step-n (current-stepper-state) argument)))
+
+(define-command stepper-run
+  "Run current eval to completion without stepping."
+  ()
+  (lambda () (step-run (current-stepper-state))))
+
+(define-command stepper-step-until
+  "Step until current eval completes."
+  ()
+  (lambda () (step-until (current-stepper-state))))
+
+(define-command stepper-step-until-visibly
+  "Step until current eval completes, showing each step as it happens."
+  ()
+  (lambda () (step-until-visibly (current-stepper-state))))
+
+(define-command stepper-step-over
+  "Step over the current eval."
+  ()
+  (lambda () (step-over (current-stepper-state))))
+
+(define-command stepper-expand
+  "Expand the current step."
+  ()
+  (lambda ()
+    (let ((state (current-stepper-state))
+         (node (current-node)))
+      (ynode-expand! node)
+      (edwin-step-output state #f `(EXPAND ,node)))))
+
+(define-command stepper-contract
+  "Contract the current step."
+  ()
+  (lambda () 
+    (let ((state (current-stepper-state))
+         (node (current-node)))
+      (ynode-contract! node)
+      (edwin-step-output state #f `(CONTRACT ,node)))))
+\f
+;;;; Stepper Output Interface
+
+(define (initialize-package!)
+  ;; Load the stepper and grab its output hooks.
+  (load-option 'STEPPER)
+  (set! step-output-initialize edwin-step-output-initialize)
+  (set! step-output edwin-step-output)
+  (set! step-output-final-result edwin-step-output-final-result)
+  unspecific)
+
+(define (edwin-step-output-initialize state)
+  (select-buffer-other-window (get-stepper-buffer state)))
+
+(define (get-stepper-buffer state)
+  (let ((buffer (new-buffer "*Stepper*")))
+    (add-kill-buffer-hook buffer kill-stepper-buffer)
+    (buffer-put! buffer 'STEPPER-STATE state)
+    (hash-table/put! stepper-buffers state buffer)
+    (set-buffer-read-only! buffer)
+    (set-buffer-major-mode! buffer (ref-mode-object stepper))
+    buffer))
+
+(define (kill-stepper-buffer buffer)
+  (let ((state (buffer-get buffer 'STEPPER-STATE)))
+    (if state
+       (hash-table/remove! stepper-buffers state)))
+  (buffer-remove! buffer 'STEPPER-STATE))
+
+(define (buffer->stepper-state buffer)
+  (or (buffer-get buffer 'STEPPER-STATE)
+      (error:bad-range-argument buffer 'BUFFER->STEPPER-STATE)))
+
+(define (stepper-state->buffer state)
+  (or (hash-table/get stepper-buffers state #f)
+      (get-stepper-buffer state)))
+
+(define stepper-buffers
+  (make-eq-hash-table))
+
+(define (current-stepper-state)
+  (buffer->stepper-state (current-buffer)))
+
+(define (edwin-step-output-final-result state result)
+  state
+  (editor-error
+   (string-append "Stepping terminated with result "
+                 (write-to-string result))))
+
+(define (current-node)
+  (let ((point (current-point)))
+    (or (get-text-property (mark-group point)
+                          (mark-index point)
+                          'STEPPER-NODE
+                          #f)
+       (editor-error "Point not pointing to stepper node."))))
+
+(define (get-buffer-ynode-regions buffer)
+  (or (buffer-get buffer 'YNODE-REGIONS)
+      (let ((table (make-eq-hash-table)))
+       (buffer-put! buffer 'YNODE-REGIONS table)
+       table)))
+
+(define (clear-ynode-regions! regions)
+  (for-each mark-temporary! (hash-table/datum-list regions))
+  (hash-table/clear! regions))
+
+(define (ynode-start-mark regions node)
+  (hash-table/get regions node #f))
+
+(define (save-ynode-region! regions node start end)
+  (hash-table/put! regions node (mark-temporary-copy start))
+  (add-text-property (mark-group start) (mark-index start) (mark-index end)
+                    'STEPPER-NODE node))
+\f
+(define (edwin-step-output state redisplay? #!optional last-event)
+  (let ((buffer (stepper-state->buffer state))
+       (last-event
+        (if (default-object? last-event)
+            (stepper-last-event state)
+            last-event)))
+    (let ((regions (get-buffer-ynode-regions buffer)))
+      (clear-ynode-regions! regions)
+      (with-read-only-defeated (buffer-start buffer)
+       (lambda ()
+         (delete-string (buffer-start buffer) (buffer-end buffer))
+         (let ((node (stepper-root-node state))
+               (start (mark-right-inserting-copy (buffer-start buffer)))
+               (point (mark-left-inserting-copy (buffer-start buffer))))
+           (let loop ((node node) (level 0))
+             (if (not (eq? (ynode-type node) 'STEPPED-OVER))
+                 (begin
+                   (move-mark-to! start point)
+                   (output-and-mung-region point
+                     (lambda ()
+                       (debugger-pp (ynode-exp node)
+                                    (* 2 level)
+                                    (current-output-port)))
+                     (and last-event
+                          (eq? (car last-event) 'CALL)
+                          (eq? (cadr last-event) node)
+                          (lambda (region)
+                            (highlight-region-excluding-indentation region
+                                                                    #t))))
+                   (insert-string (if (ynode-hidden-children? node)
+                                      " ===> "
+                                      " => ")
+                                  point)
+                   (let ((value-node (ynode-value-node node)))
+                     (output-and-mung-region point
+                       (lambda ()
+                         (write
+                          (ynode-result
+                           (if (eq? (ynode-type node) 'STEP-OVER)
+                               value-node
+                               node))))
+                       (and last-event
+                            (eq? (car last-event) 'RETURN)
+                            (eq? (cadr last-event) value-node)
+                            (lambda (region)
+                              (highlight-region region #t)))))
+                   (insert-newline point)
+                   (save-ynode-region! regions node start point)
+                   (if (not (eq? 'STEP-OVER (ynode-type node)))
+                       (for-each (lambda (n) (loop n (+ level 1)))
+                                 (reverse (ynode-children node)))))))
+           (mark-temporary! point)
+           (mark-temporary! start))))
+      (buffer-not-modified! buffer)
+      (if last-event
+         (let ((start (ynode-start-mark regions (cadr last-event))))
+           (if start
+               (set-buffer-point! buffer start))))))
+  (if redisplay? (update-screens! #f)))
+
+(define (output-and-mung-region point thunk region-munger)
+  ;; Display something in the stepper buffer and then run something on
+  ;; it.  REGION-MUNGER takes one argument, a region.
+  (let ((start (mark-right-inserting-copy point)))
+    (with-output-to-mark point thunk)
+    (if region-munger (region-munger (make-region start point)))
+    (mark-temporary! start)))
+
+(initialize-package!)
\ No newline at end of file
diff --git a/v7/src/runtime/ystep.scm b/v7/src/runtime/ystep.scm
new file mode 100644 (file)
index 0000000..3f80067
--- /dev/null
@@ -0,0 +1,469 @@
+#| -*-Scheme-*-
+
+$Id: ystep.scm,v 1.1 1994/10/12 07:54:00 cph Exp $
+
+Copyright (c) 1994 Massachusetts Institute of Technology
+
+This material was developed by the Scheme project at the Massachusetts
+Institute of Technology, Department of Electrical Engineering and
+Computer Science.  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. Users of this software agree to make their best efforts (a) to
+return to the MIT Scheme project any improvements or extensions that
+they make, so that these may be included in future releases; and (b)
+to inform MIT of noteworthy uses of this software.
+
+3. All materials developed as a consequence of the use of this
+software shall duly acknowledge such use, in accordance with the usual
+standards of acknowledging credit in academic research.
+
+4. MIT has made no warrantee or representation that the operation of
+this software will be error-free, and MIT is under no obligation to
+provide any services, by way of maintenance, update, or otherwise.
+
+5. In conjunction with products arising from the use of this material,
+there shall be no use of the name of the Massachusetts Institute of
+Technology nor of any adaptation thereof in any advertising,
+promotional, or sales literature without prior written consent from
+MIT in each case. |#
+
+;;;; YStep - a step away from ZStep
+;;; package: (runtime stepper)
+
+(declare (usual-integrations))
+\f
+(define-structure (stepper (constructor make-stepper (stack)))
+  (stack '())
+  (run? #f)                            ;#t => run; #f => step
+  (step-over #f)                       ;#f or top node of step-over
+  (step-until? #f)                     ;the step-over is really a step-until
+  next                                 ;continuation of stepped program
+  continuation
+  last-event                           ;last thing that happened
+  (finished #f)                                ;when completed, stack is
+                                       ;empty and this points to top node
+  hooks                                        ;low-level stepper hooks
+  (trace '())                          ;low-level trace recording
+  )
+
+(define (stack-push! state node)
+  (set-stepper-stack! state (cons node (stepper-stack state))))
+
+(define (stack-pop! state)
+  (set-stepper-stack! state (cdr (stepper-stack state))))
+
+(define (stack-top state)
+  (car (stepper-stack state)))
+
+(define (stack-bottom state)
+  (car (last-pair (stepper-stack state))))
+
+(define (stack-empty? state)
+  (null? (stepper-stack state)))
+
+(define (stepper-root-node state)
+  (if (stack-empty? state)
+      (stepper-finished state)
+      (stack-bottom state)))
+
+;;; The magic numbers here represent the number of eval and return
+;;; events that occur during the startup process.  They will very
+;;; likely have to change when the system changes.
+
+(define (step-form expression environment)
+  ;; start a new evaluation
+  (step-start (make-ynode #f 'TOP ynode-exp:top-level)
+             (lambda () (eval expression environment))
+             (if (stepper-compiled?) 0 6)
+             (if (stepper-compiled?) 1 5)))
+
+(define (step-proceed)
+  ;; proceed from breakpoint
+  (step-start (make-ynode #f 'PROCEED ynode-exp:proceed)
+             (lambda () (continue))
+             (if (stepper-compiled?) 0 4)
+             (if (stepper-compiled?) 5 7)))
+
+(define (stepper-compiled?)
+  (compiled-procedure? (lambda () unspecific)))
+
+(define (step-start top-node thunk skip-evals skip-returns)
+  (if (not (step-hooks-present?))
+      (error "Sorry, this copy of Scheme does not support stepping."))
+  (let ((state (make-stepper (list top-node))))
+    (set-stepper-hooks! state (make-stepper-hooks state))
+    (set-stepper-next! state
+                      (lambda ()
+                        (dummy-eval-step
+                         (make-starting-hooks state skip-evals skip-returns))
+                        (thunk)))
+    (step-output-initialize state)
+    (step state)))
+\f
+(define (step state)
+  (set-stepper-run?! state #f)
+  (raw-step state))
+
+(define (step-run state)
+  (set-stepper-run?! state #t)
+  (raw-step state))
+
+(define (step-quit state)
+  ;; [entry] not working yet
+  (dummy-eval-step no-step-hooks)
+  ((stepper-next state)))
+
+(define (step-n state n)
+  (do ((n n (- n 1))
+       (value unspecific (step state)))
+      ((<= n 0) value)))
+
+(define (step-over state)
+  (set-stepper-step-until?! state #f)
+  (step-over-1 state))
+
+(define (step-until state)
+  (set-stepper-step-until?! state #t)
+  (step-over-1 state))
+
+(define (step-until-visibly state)
+  (set-stepper-step-until?! state 'ANIMATE)
+  (step-over-1 state))
+
+(define (step-over-1 state)
+  (if (not (eq? (car (stepper-last-event state)) 'CALL))
+      (error "Last event was not a call:" (stepper-last-event state)))
+  (set-stepper-step-over! state (stack-top state))
+  (new-ynode-type! (stack-top state)
+                  (if (stepper-step-until? state) 'EVAL 'STEP-OVER))
+  (raw-step state))
+
+(define (raw-step state)
+  ;; the workhorse
+  (if (stepper-finished state)
+      (step-output-final-result state (ynode-result (stepper-finished state)))
+      (begin
+       (set-stepper-next! state
+                          (call-with-current-continuation
+                           (lambda (kk)
+                             (set-stepper-continuation! state kk)
+                             ((stepper-next state)))))
+       (if (stepper-run? state)
+           (raw-step state)
+           (step-output state #f)))))
+
+;;; Output Stubs:
+
+(define (step-output-initialize state)
+  state
+  unspecific)
+
+(define (step-output state redisplay?)
+  state redisplay?
+  unspecific)
+
+(define (step-output-final-result state value)
+  state
+  value)
+\f
+;;;; Low-level Hooks
+
+(define (make-stepper-hooks state)
+  (letrec
+      ((hooks
+       (hunk3-cons
+        (lambda (expr env)
+          (hook-record state
+                       (list 'EVAL (map-reference-trap (lambda () expr)) env))
+          (process-eval state (map-reference-trap (lambda () expr)))
+          (primitive-eval-step expr env hooks))
+        (lambda (proc . args)
+          (hook-record state
+                       (list 'APPLY
+                             proc
+                             (map (lambda (arg)
+                                    (map-reference-trap (lambda () arg)))
+                                  args)))
+          (process-apply state proc)
+          (primitive-apply-step proc args hooks))
+        (lambda (value)
+          (hook-record state
+                       (list 'RETURN (map-reference-trap (lambda () value))))
+          (process-return state (map-reference-trap (lambda () value)))
+          (primitive-return-step value hooks)))))
+    hooks))
+
+(define (make-starting-hooks state skip-evals skip-returns)
+  (letrec
+      ((hooks
+       (hunk3-cons
+        (lambda (expr env)
+          (if (and (<= skip-evals 0) (<= skip-returns 0))
+              ((system-hunk3-cxr0 (stepper-hooks state)) expr env)
+              (begin
+                (set! skip-evals (- skip-evals 1))
+                (hook-record state (list 'EVAL expr env))
+                (primitive-eval-step expr env hooks))))
+        #f
+        (lambda (result)
+          (if (and (<= skip-evals 0) (<= skip-returns 0))
+              ((system-hunk3-cxr2 (stepper-hooks state)) result)
+              (begin
+                (set! skip-returns (- skip-returns 1))
+                (hook-record state (list 'RESULT result))
+                (primitive-return-step result hooks)))))))
+    hooks))
+
+(define no-step-hooks
+  (hunk3-cons #f #f #f))
+
+(define-integrable primitive-eval-step
+  (ucode-primitive primitive-eval-step))
+
+(define-integrable primitive-apply-step
+  (ucode-primitive primitive-apply-step))
+
+(define-integrable primitive-return-step
+  (ucode-primitive primitive-return-step))
+\f
+;;;; Worker Bees
+
+(define (process-eval state exp)
+  (if (reduction? exp (ynode-exp (stack-top state)))
+      (process-reduction state))
+  (let ((node
+        (make-ynode (and (not (stack-empty? state))
+                         (stack-top state))
+                    (if (and (stepper-step-over state)
+                             (not (stepper-step-until? state)))
+                        'STEPPED-OVER
+                        'EVAL)
+                    exp)))
+    (stack-push! state node)
+    (set-stepper-last-event! state `(CALL ,node))
+    (maybe-redisplay state)))
+
+(define (process-apply state proc)
+  (if (compound-procedure? proc)
+      (process-reduction state)))
+
+(define (process-return state result)
+  (if (stepper-step-over state)
+      (maybe-end-step-over state))
+  (let ((node
+        (let ((node (stack-top state)))
+          (if (eq? (ynode-type node) 'PROCEED)
+              (ynode-splice-under node)
+              (begin
+                (stack-pop! state)
+                node)))))
+    (new-ynode-result! node result)
+    (if (stack-empty? state)
+       (set-stepper-finished! state node))
+    (set-stepper-last-event! state `(RETURN ,node))
+    (maybe-redisplay state)))
+
+(define (maybe-redisplay state)
+  (if (stepper-step-over state)
+      (if (eq? (stepper-step-until? state) 'ANIMATE)
+         (step-output state #t))
+      (call-with-current-continuation
+       (lambda (k)
+        ((stepper-continuation state) (lambda () (k unspecific)))))))
+
+(define (maybe-end-step-over state)
+  (if (ynode-reduces-to? (stack-top state) (stepper-step-over state))
+      (begin
+       (set-stepper-step-over! state #f)
+       (set-stepper-step-until?! state #f))))
+
+(define (process-reduction state)
+  (new-ynode-result! (stack-top state) ynode-result:reduced)
+  (stack-pop! state))
+
+(define (reduction? f1 f2)
+  ;; Args are SCode expressions.  True if F2 is a reduction of F1.
+  (cond ((conditional? f2)
+        (or (eq? f1 (conditional-consequent f2))
+            (eq? f1 (conditional-alternative f2))))
+       ((sequence? f2)
+        (eq? f1 (car (last-pair (sequence-actions f2)))))
+       (else #f)))
+\f
+;;;; Stepper nodes
+
+(define-structure (ynode (constructor make-ynode-1 (parent type exp)))
+  ;; Could easily store environment as well.
+  parent
+  type
+  (exp #f read-only #t)
+  (children '())
+  (result #f)
+  (redisplay-flags (cons #t (if parent (ynode-redisplay-flags parent) '()))
+                  read-only #t))
+
+(define ynode-exp:top-level (list 'STEPPER-TOP-LEVEL))
+(define ynode-exp:proceed   (list 'STEPPER-PROCEED))
+
+(define ynode-result:waiting (list '<WAITING>))
+(define ynode-result:reduced (list '<REDUCED>))
+(define ynode-result:unknown (list '<UNKNOWN>))
+
+(define (ynode-reduced? node)
+  (eq? (ynode-result node) ynode-result:reduced))
+
+(define (make-ynode parent type exp)
+  (let ((node (make-ynode-1 parent type exp)))
+    (set-ynode-result! node ynode-result:waiting)
+    (if parent
+       (set-ynode-children! parent (cons node (ynode-children parent))))
+    (ynode-needs-redisplay! node)
+    node))
+
+(define (ynode-previous node)
+  (let loop ((sibs (ynode-children (ynode-parent node))))
+    (cond ((null? sibs)
+          #f)
+         ((eq? (car sibs) node)
+          (and (not (null? (cdr sibs)))
+               (cadr sibs)))
+         (else
+          (loop (cdr sibs))))))
+
+(define (ynode-next node)
+  (let loop ((sibs (ynode-children (ynode-parent node))))
+    (cond ((or (null? sibs) (null? (cdr sibs)))
+          #f)
+         ((eq? (cadr sibs) node)
+          (car sibs))
+         (else
+          (loop (cdr sibs))))))
+
+(define (ynode-value-node node)
+  (if (ynode-reduced? node)
+      (let ((next (ynode-next node)))
+       (and next
+            (ynode-value-node next)))
+      node))
+
+(define (ynode-reduces-to? node reduces-to)
+  (and node
+       (or (eq? node reduces-to)
+          (let ((previous (ynode-previous node)))
+            (and previous
+                 (ynode-reduced? previous)
+                 (ynode-reduces-to? previous reduces-to))))))
+
+(define (ynode-splice-under node)
+  (let ((children (ynode-children node)))
+    (set-ynode-children! node '())
+    (let ((new-node (make-ynode node 'EVAL ynode-result:unknown)))
+      (set-ynode-children! new-node children)
+      (for-each (lambda (c) (set-ynode-parent! c new-node)) children)
+      (let loop ((node new-node))
+       (ynode-needs-redisplay! ynode)
+       (for-each loop (ynode-children node)))
+      new-node)))
+\f
+(define (ynode-reductions node)
+  (if (ynode-reduced? node)
+      (let ((next (ynode-next node)))
+       (cons next (ynode-reductions next)))
+      '()))
+
+(define (ynode-dependents node)
+  ;; A dependent (misnomer) roughly means nodes that are directly
+  ;; called by another node (which is not the same as children,
+  ;; because reductions muck things up).
+  (if (ynode-reduced? node)
+      (cons (ynode-next node)
+           (ynode-direct-children node))
+      (ynode-direct-children node)))
+
+(define (ynode-direct-children node)
+  ;; A "direct" child is one that is not a reduction of another child...
+  (let loop ((children (ynode-children node)) (dependents '()))
+    (if (null? children)
+       dependents
+       (loop (cdr children)
+             (if (and (not (null? (cdr children)))
+                      (ynode-reduced? (cadr children)))
+                 dependents
+                 (cons (car children) dependents))))))
+
+(define (ynode-hidden-children? node)
+  ;; used to control drawing of arrow
+  (and (eq? (ynode-type node) 'STEP-OVER)
+       (not (null? (ynode-children node)))))
+
+(define (ynode-needs-redisplay! ynode)
+  (if (not (car (ynode-redisplay-flags ynode)))
+      (begin
+       (set-car! (ynode-redisplay-flags ynode) #t)
+       (if (ynode-parent ynode)
+           (ynode-needs-redisplay! (ynode-parent ynode))))))
+
+(define (ynode-needs-redisplay? ynode)
+  (car (ynode-redisplay-flags ynode)))
+
+(define (ynode-doesnt-need-redisplay! ynode)
+  (set-car! (ynode-redisplay-flags ynode) #f))
+
+(define (new-ynode-type! ynode type)
+  (set-ynode-type! ynode type)
+  (ynode-needs-redisplay! ynode))
+
+(define (new-ynode-result! ynode result)
+  (set-ynode-result! ynode result)
+  (ynode-needs-redisplay! ynode))
+
+(define (ynode-expand! node)
+  (new-ynode-type! node 'EVAL)
+  (for-each (lambda (dependent)
+             (if (eq? (ynode-type dependent) 'STEPPED-OVER)
+                 (new-ynode-type! dependent 'STEP-OVER)))
+           (ynode-dependents node)))
+
+(define (ynode-contract! node)
+  (new-ynode-type! node 'STEP-OVER)
+  (for-each (lambda (dependent)
+             (new-ynode-type! dependent 'STEPPED-OVER))
+           (ynode-reductions node)))
+\f
+;;;; Miscellaneous
+
+(define (dummy-eval-step hooks)
+  (primitive-eval-step #f system-global-environment hooks))
+
+(define (step-hooks-present?)
+  (let ((flag #f))
+    (dummy-eval-step
+     (hunk3-cons #f
+                #f
+                (lambda (value)
+                  (set! flag #t)
+                  (primitive-return-step value no-step-hooks))))
+    flag))
+
+;;; Debugging trace:
+
+;;; disabled, see next definition
+;;; (define (hook-record state item)
+;;;   (set-stepper-trace! state (cons item (stepper-trace state))))
+
+(define-integrable (hook-record state item)
+  ;; DEFINE-INTEGRABLE guarantees that argument in ITEM position is
+  ;; not evaluated.
+  state item
+  unspecific)
+
+(define (print-hook-trace state)
+  (pp (let loop ((thing (stepper-trace state)))
+       (cond ((list? thing) (map loop thing))
+             ((symbol? thing) thing)
+             (else (unsyntax thing))))))
\ No newline at end of file