;** allow primitives
; Procedure call/return tracing
; Uses: startup

(define trace
  (lambda (var)
    (if (get var 'traced)
        (list var 'already 'traced)
        (if (not (bound-to-lambda? var))
            '(not a defined-procedure name)
            (begin
              (put var 'traced (eval var))
              (set var (make-traced var))
              var)))))

(define untrace
  (lambda (var)
    (if (not (get var 'traced))
        (list var 'not 'traced)
        (begin
          (set var (get var 'traced))
          var))))

(define bound-to-lambda?
  (lambda (var)
    (and (symbol? var)
         (let ((value (eval var)))
           (and (pair? value)
                (eq? (car value) 'lambda))))))

(define make-traced
  (lambda (var)
    (let ((proc (eval var)))
      (list 'lambda (cadr proc)
        (list '=enter= (list 'quote var) (cons 'list (cadr proc)))
        (list '=exit= (list 'quote var)
          (cons (list 'get (list 'quote var) (list 'quote 'traced))
                (cadr proc)))))))

(define =enter=
  (lambda (name args)
    (tab *indentation*)
    (set! *indentation* (+ *indentation* 1))
    (write 'entering)
    (write name)
    (write ':)
    (for-each write args)
    (newline)))

(define =exit=
  (lambda (name result)
    (set! *indentation* (- *indentation* 1))
    (tab *indentation*)
    (write 'exiting)
    (write name)
    (write ':)
    (print result)
    result))

(define *indentation* 0)

(define tab
  (lambda (n)
    (while (< 0 n)
      (write '>)
      (set! n (- n 1)))))

(define set
  (lambda (var value)
    (eval (list 'set! var (list 'quote value)))))