;		       Copyright (C) 1992 by Marc Feeley -- all rights reserved

;; "_eval.scm"

(##include "header.scm")

;------------------------------------------------------------------------------

(##define-macro (global-env-loc x) `(##global-var ,x))
(##define-macro (global-env-ref x) `(##global-var-ref ,x))
(##define-macro (global-env-set! x y) `(##global-var-set! ,x ,y))
(##define-macro (global-env-loc->var x) `(##index->global-var-name ,x))

(##define-macro (quasi-list->vector x) `(##quasi-list->vector ,x))
(##define-macro (quasi-append x y) `(##quasi-append ,x ,y))
(##define-macro (quasi-cons x y) `(##quasi-cons ,x ,y))

(##define-macro (true? x) x)
(##define-macro (unbound? x) `(##unbound? ,x))
(##define-macro (unspecified-obj) '##undef-object)
(##define-macro (set!-ret-obj) '##unprint-object)

(define ##self-var     (##string->uninterned-symbol "<self>"))
(define ##selector-var (##string->uninterned-symbol "<selector>"))
(define ##do-loop-var  (##string->uninterned-symbol "<do-loop>"))

(##define-macro (self-var)     '##self-var)
(##define-macro (selector-var) '##selector-var)
(##define-macro (do-loop-var)  '##do-loop-var)

(##define-macro (rt-error-unbound-global-var code rte)
  `(##signal '##SIGNAL.GLOBAL-UNBOUND ,code ,rte))

(##define-macro (rt-error-non-procedure-send code rte)
  `(##signal '##SIGNAL.NON-PROCEDURE-SEND ,code ,rte))

(##define-macro (rt-error-non-procedure-oper code rte)
  `(##signal '##SIGNAL.NON-PROCEDURE-OPERATOR ,code ,rte))

(##define-macro (rt-error-too-few-args proc args)
  `(##signal '##SIGNAL.WRONG-NB-ARG ,proc ,args))

(##define-macro (rt-error-too-many-args proc args)
  `(##signal '##SIGNAL.WRONG-NB-ARG ,proc ,args))

(##define-macro (ct-error-global-env-overflow var)
  `(##signal '##SIGNAL.GLOBAL-ENV-OVERFLOW ,var))

(##define-macro (ct-error-syntax msg . args)
  `(##signal '##SIGNAL.SYNTAX-ERROR src ,msg ,@args))

;------------------------------------------------------------------------------

; Macro to create a node of executable code

(##define-macro (mk-code code-prc subcodes . lst)
  (let ((n (+ (length subcodes) (length lst))))
    `(let (($code (##make-vector ,(+ n 2) #f)))
       (##vector-set! $code 0 #f)
       (##vector-set! $code 1 ,code-prc)
       ,@(let loop1 ((l subcodes) (i 2) (r '()))
           (if (pair? l)
             (loop1 (cdr l)
                    (+ i 1)
                    (cons `(##vector-set! $code ,i (link-to ,(car l) $code)) r))
             (let loop2 ((l lst) (i i) (r r))
               (if (pair? l)
                 (loop2 (cdr l)
                        (+ i 1)
                        (cons `(##vector-set! $code ,i ,(car l)) r))
                 (reverse r)))))
       $code)))

(##define-macro (link-to child parent)
  `(let (($child ,child)) (##vector-set! $child 0 ,parent) $child))

(##define-macro (code-link c)     `(##vector-ref ,c 0))
(##define-macro (code-cprc c)     `(##vector-ref ,c 1))
(##define-macro (code-length c)   `(##fixnum.- (##vector-length ,c) 2))
(##define-macro (code-ref c n)    `(##vector-ref ,c (##fixnum.+ ,n 2)))
(##define-macro (code-set! c n x) `(##vector-set! ,c (##fixnum.+ ,n 2) ,x))
(##define-macro (^ n)             `(##vector-ref $code ,(+ n 2)))

(define (##mk-code* code-prc lst n)
  (let (($code (##make-vector (##fixnum.+ (##length lst) (##fixnum.+ n 2)) #f)))
    (##vector-set! $code 0 #f)
    (##vector-set! $code 1 code-prc)
    (let loop ((i 0) (l lst))
      (if (##pair? l)
        (begin
          (code-set! $code i (link-to (##car l) $code))
          (loop (##fixnum.+ i 1) (##cdr l)))
        $code))))

(##define-macro (code-run c)
  `(let (($$code ,c))
     ((##vector-ref $$code 1) $$code rte)))

; Macro to create the "code procedure" associated with a code node

(##define-macro (mk-cprc . def)
  `(lambda ($code rte) ,@def))

(##define-macro (mk-gen params . def)
  `(lambda (cte src tail? ,@params) ,@def))

(##define-macro (gen proc . args)
  `(,proc cte src tail? ,@args))

;==============================================================================

; Compiler

;------------------------------------------------------------------------------

; Compile time environment manipulation

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

; Macros to manipulate the compile time environment

(##define-macro (mk-loc-access up over) `(##cons ,up ,over))
(##define-macro (loc-access? x) `(##pair? ,x))
(##define-macro (loc-access-up x) `(##car ,x))
(##define-macro (loc-access-over x) `(##cdr ,x))

(##define-macro (mk-glo-access var)
  `(or (global-env-loc ,var)
       (ct-error-global-env-overflow ,var)))

(##define-macro (glo-access? x)
  `(##not (##pair? ,x)))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

; Initial global environment

(define ##global-env-macros (##cons (##cons #f #f) '()))
(define ##global-env-decls (##cons '() '()))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##make-cte frames)
  (let ((v (##make-vector 3 #f)))
    (##vector-set! v 0 frames)
    (##vector-set! v 1 ##global-env-macros)
    (##vector-set! v 2 ##global-env-decls)
    v))

(define (##cte-frames cte) (##vector-ref cte 0))
(define (##cte-macros cte) (##vector-ref cte 1))
(define (##cte-decls cte)  (##vector-ref cte 2))

(define (##cte-push-frame cte frame)
  (let ((v (##make-vector 3 #f)))
    (##vector-set! v 0 (##cons frame (##cte-frames cte)))
    (##vector-set! v 1 (##cte-macros cte))
    (##vector-set! v 2 (##cte-decls cte))
    v))

(define (##cte-push-macro cte name proc)
  (let ((v (##make-vector 3 #f)))
    (##vector-set! v 0 (##cte-frames cte))
    (##vector-set! v 1 (##cons (##cons name proc) (##cte-macros cte)))
    (##vector-set! v 2 (##cte-decls cte))
    v))

(define (##cte-push-decl cte decl)
  (let ((v (##make-vector 3 #f)))
    (##vector-set! v 0 (##cte-frames cte))
    (##vector-set! v 1 (##cte-macros cte))
    (##vector-set! v 2 (##append decl (##cte-decls cte)))
    v))          

(define (##cte-add-global-macro name proc)
  (let ((x (##cdr ##global-env-macros)))
    (let ((y (##assq name x)))
      (if y
        (##set-cdr! y proc)
        (##set-cdr! ##global-env-macros
          (##cons (##cons name proc) (##cdr ##global-env-macros)))))))

(define (##cte-add-global-decl decl)
  (##set-cdr! ##global-env-decls
    (##append decl (##cdr ##global-env-decls))))

(define (##cte-lookup-var cte var)

  (define (lookup e up)
    (if e
      (let ((x (##memq var (##car e))))
        (if x
          (mk-loc-access
            up
            (##fixnum.+ (##fixnum.- (##length (##car e)) (##length x)) 1))
          (lookup (##cdr e) (##fixnum.+ up 1))))
      (mk-glo-access var)))

  (lookup (##cte-frames cte) 0))

(define (##macro? cte name)
  (and (##symbol? name)
       (##assq name (##cte-macros cte))))

(define (##cte-lookup-macro cte name)
  (##cdr (##assq name (##cte-macros cte))))

(define (##macro-expand cte src)
  (let ((x (##car src)))
    (touch-vars (x)
      (##apply (##cte-lookup-macro cte x) (##cdr src)))))

;------------------------------------------------------------------------------

; Utilities

(define (##self-eval? val)
  (touch-vars (val)
    (or (##complex? val)
        (##string? val)
        (##char? val)
        (##eq? val #f)
        (##eq? val #t))))

(define (##variable src x)
  (if (##not (##symbol? x))
    (ct-error-syntax "Identifier expected:" x))
  (if (##memq x
              '(QUOTE QUASIQUOTE UNQUOTE UNQUOTE-SPLICING LAMBDA IF SET!
                COND => ELSE AND OR CASE LET LET* LETREC BEGIN DO DEFINE
                DELAY FUTURE ##DECLARE ##DEFINE-MACRO ##INCLUDE))
    (ct-error-syntax "Variable name can not be a syntactic keyword:" x)))

(define (##shape src x size)
  (let ((n (##proper-length x)))
    (if (or (##not n)
            (if (##fixnum.< 0 size)
              (##not (##fixnum.= n size))
              (##fixnum.< n (##fixnum.- 0 size))))
      (ct-error-syntax "Ill-formed special form:" (##car src)))))

(define (##proper-length l)

  (define (len l n)
    (cond ((##pair? l) (len (##cdr l) (##fixnum.+ n 1)))
          ((##null? l) n)
          (else        #f)))

  (len l 0))

(define (##touch-list l)
  (if-touches
    (let loop ((l l))
      (touch-vars (l)
        (if (##pair? l)
          (##cons (##car l) (loop (##cdr l)))
          l)))
    l))

(define (##read-expressions cte src filename)
  (if (##string? filename)

    (let ((port (##open-input-file filename)))

      (define (read-exprs)
        (let ((expr (##read port)))
          (if (##not (##eof-object? expr))
            (##cons expr (read-exprs))
            '())))

      (if port
        (let ((exprs (read-exprs)))
          (##close-port port)
          exprs)
        (ct-error-syntax "File not found")))

    (ct-error-syntax "Filename expected")))

;------------------------------------------------------------------------------

; Compiler's main entry

(define (##compile src frames)
  (let ((cte (##make-cte frames)) (tail? #t))
    (gen ##gen-top
      frames
      (##comp-top (##cte-push-frame cte (##list (self-var))) src tail?))))

(define (##comp-top cte src tail?)
  (let ( (src (syntax-expand (##touch-list src))) )  ;; @@ kad
    (cond ((##symbol? src)         (##comp-ref cte src tail?))
          ((##self-eval? src)      (##comp-cst cte src tail?))
          ((##not (##pair? src))   (ct-error-syntax "Ill-formed expression"))
          (else
           (let ((first (##car src)))
             (if (##macro? cte first)
               (##comp-top cte (##macro-expand cte src) tail?)
               (case first
                 ((BEGIN)          (##comp-top-BEGIN cte src tail?))
                 ((DEFINE)         (##comp-top-DEFINE cte src tail?))
                 ((##DECLARE)      (##comp-top-DECLARE cte src tail?))
                 ((##DEFINE-MACRO) (##comp-top-DEFINE-MACRO cte src tail?))
                 ((##INCLUDE)      (##comp-top-INCLUDE cte src tail?))
                 (else             (##comp-aux cte src tail? first)))))))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-top-BEGIN cte src tail?)
  (##shape src src -1)
  (##comp-top-seq cte src tail? (##cdr src)))

(define (##comp-top-seq cte src tail? seq)
  (if (##pair? seq)
    (##comp-top-seq-aux cte src tail? seq)
    (gen ##gen-cst (unspecified-obj))))

(define (##comp-top-seq-aux cte src tail? seq)
  (let ((rest (##cdr seq)))
    (if (##pair? rest)
      (gen ##gen-seq
        (##comp-top cte (##car seq) #f)
        (##comp-top-seq-aux cte src tail? rest))
      (##comp-top cte (##car seq) tail?))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-top-DEFINE cte src tail?)
  (let ((cte (##make-cte #f)))
    (let ((name (##definition-name src)))
      (let ((ind (##cte-lookup-var cte name)))
        (gen ##gen-glo-def
          name
          ind
          (##comp cte (##definition-value src) #f))))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-top-DECLARE cte src tail?)
  (##shape src src -1)
  (##cte-add-global-decl (##cdr src))
  (gen ##gen-cst (unspecified-obj)))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-top-DEFINE-MACRO cte src tail?)
  (let ((name (##definition-name src)))
    (##cte-add-global-macro name (##eval-global (##definition-value src)))
    (gen ##gen-cst name)))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-top-INCLUDE cte src tail?)
  (##shape src src 2)
  (##comp-top-seq cte src tail? (##read-expressions cte src (##cadr src))))

;------------------------------------------------------------------------------

(define (##comp cte src tail?)
  (let ((src (##touch-list src)))
    (cond ((##symbol? src)         (##comp-ref cte src tail?))
          ((##self-eval? src)      (##comp-cst cte src tail?))
          ((##not (##pair? src))   (ct-error-syntax "Ill-formed expression"))
          (else
           (let ((first (##car src)))
             (if (##macro? cte first)
               (##comp cte (##macro-expand cte src) tail?)
               (case first
                 ((BEGIN)          (##comp-BEGIN cte src tail?))
                 ((DEFINE)         (ct-error-syntax "Ill-placed 'define'"))
                 ((##DECLARE)      (ct-error-syntax "Ill-placed '##declare'"))
                 ((##DEFINE-MACRO) (ct-error-syntax "Ill-placed '##define-macro'"))
                 ((##INCLUDE)      (ct-error-syntax "Ill-placed '##include'"))
                 (else             (##comp-aux cte src tail? first)))))))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-BEGIN cte src tail?)
  (##shape src src -2)
  (##comp-seq cte src tail? (##cdr src)))

(define (##comp-seq cte src tail? seq)
  (if (##pair? seq)
    (##comp-seq-aux cte src tail? seq)
    (gen ##gen-cst (unspecified-obj))))

(define (##comp-seq-aux cte src tail? seq)
  (let ((rest (##cdr seq)))
    (if (##pair? rest)
      (gen ##gen-seq
        (##comp cte (##car seq) #f)
        (##comp-seq-aux cte src tail? rest))
      (##comp cte (##car seq) tail?))))

;------------------------------------------------------------------------------

(define (##comp-aux cte src tail? first)
  (case first
    ((QUOTE)            (##comp-QUOTE cte src tail?))
    ((QUASIQUOTE)       (##comp-QUASIQUOTE cte src tail?))
    ((UNQUOTE)          (ct-error-syntax "Ill-placed 'unquote'"))
    ((UNQUOTE-SPLICING) (ct-error-syntax "Ill-placed 'unquote-splicing'"))
    ((SET!)             (##comp-SET! cte src tail?))
    ((LAMBDA)           (##comp-LAMBDA cte src tail?))
    ((IF)               (##comp-IF cte src tail?))
    ((COND)             (##comp-COND cte src tail?))
    ((AND)              (##comp-AND cte src tail?))
    ((OR)               (##comp-OR cte src tail?))
    ((CASE)             (##comp-CASE cte src tail?))
    ((LET)              (##comp-LET cte src tail?))
    ((LET*)             (##comp-LET* cte src tail?))
    ((LETREC)           (##comp-LETREC cte src tail?))
    ((DO)               (##comp-DO cte src tail?))
    ((DELAY)            (##comp-DELAY cte src tail?))
    ((FUTURE)           (##comp-FUTURE cte src tail?))
    (else               (##comp-app cte src tail?))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-ref cte src tail?)
  (##variable src src)
  (let ((x (##cte-lookup-var cte src)))
    (if (loc-access? x)
      (let ((up (loc-access-up x))
            (over (loc-access-over x)))
        (gen ##gen-loc-ref up over))
      (gen ##gen-glo-ref x))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-cst cte src tail?)
  (gen ##gen-cst src))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-QUOTE cte src tail?)
  (##shape src src 2)
  (gen ##gen-cst (##cadr src)))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-QUASIQUOTE cte src tail?)
  (##comp-quasi cte src tail? (##touch-list (##cadr src)) 1))

(define (##comp-quasi cte src tail? form level)
  (cond ((##eq? level 0)
         (##comp cte form tail?))
        ((##pair? form)
         (let ((x (##car form)))
           (touch-vars (x)
             (case x
               ((QUASIQUOTE)
                (##comp-quasi-list cte src tail? form (##fixnum.+ level 1)))
               ((UNQUOTE)
                (if (##eq? level 1)
                  (##comp cte (##cadr form) tail?)
                  (##comp-quasi-list cte src tail? form (##fixnum.- level 1))))
               ((UNQUOTE-SPLICING)
                (if (##eq? level 1)
                  (ct-error-syntax "Ill-placed 'unquote-splicing'"))
                (##comp-quasi-list cte src tail? form (##fixnum.- level 1)))
               (else
                (##comp-quasi-list cte src tail? form level))))))
        ((##vector? form)
         (gen ##gen-quasi-list->vector
           (##comp-quasi-list cte src #f (##vector->list form) level)))
        (else
         (gen ##gen-cst form))))

(define (##comp-quasi-list cte src tail? l level)
  (if (##pair? l)
    (let ((first (##touch-list (##car l))))
      (if (and (##eq? level 1) (##unquote-splicing? first))
        (begin
          (##shape src first 2)
          (if (##null? (##cdr l))
            (##comp cte (##cadr first) tail?)
            (gen ##gen-quasi-append
              (##comp cte (##cadr first) #f)
              (##comp-quasi cte src #f (##cdr l) 1))))
        (gen ##gen-quasi-cons
          (##comp-quasi cte src #f first level)
          (##comp-quasi cte src #f (##cdr l) level))))
    (##comp-quasi cte src tail? l level)))

(define (##unquote-splicing? x)
  (and (##pair? x)
       (let ((y (##car x)))
         (touch-vars (y)
           (##eq? y 'UNQUOTE-SPLICING)))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-SET! cte src tail?)
  (##shape src src 3)
  (let ((var (##cadr src)))
    (touch-vars (var)
      (begin
        (##variable src var)
        (let ((x (##cte-lookup-var cte var)))
          (if (loc-access? x)
            (let ((up (loc-access-up x))
                  (over (loc-access-over x)))
              (gen ##gen-loc-set up over (##comp cte (##caddr src) #f)))
            (gen ##gen-glo-set x (##comp cte (##caddr src) #f))))))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-LAMBDA cte src tail?)
  (##shape src src -3)
  (##comp-lambda-aux cte src tail? (##touch-list (##cadr src)) (##cddr src)))

(define (##comp-lambda-aux cte src tail? parms body)
  (let ((frame (##parms->frame src parms)))
    (let ((c (##comp-body (##cte-push-frame cte (##cons (self-var) frame)) src #t body)))
      (if (##rest-param? parms)
        (gen ##gen-prc-rest frame c)
        (gen ##gen-prc frame c)))))

(define (##parms->frame src parms)
  (cond ((##null? parms)
         '())
        ((##pair? parms)
         (let ((x (##car parms)))
           (touch-vars (x)
             (let ((rest (##parms->frame src (##cdr parms))))
               (##variable src x)
               (if (##memq x rest)
                 (ct-error-syntax "Duplicate parameter in parameter list"))
               (##cons x rest)))))
        (else
         (##variable src parms)
         (##list parms))))

(define (##rest-param? parms)
  (cond ((##pair? parms)
         (##rest-param? (##cdr parms)))
        ((##null? parms)
         #f)
        (else
         #t)))

(define (##comp-body cte src tail? body)

  (define (letrec-defines cte vars vals body)
    (if (##pair? body)

      (let ((src (##touch-list (##car body))))
        (if (##not (##pair? src))
          (letrec-defines* cte vars vals body)
          (let ((first (##car src)))
            (touch-vars (first)
              (if (##macro? cte first)
                (letrec-defines cte
                                vars
                                vals
                                (##cons (##macro-expand cte src) (##cdr body)))
                (case first
                  ((BEGIN)
                   (letrec-defines cte
                                   vars
                                   vals
                                   (##append (##cdr src) (##cdr body))))
                  ((DEFINE)
                   (let ((x (##definition-name src)))
                     (##variable src x)
                     (if (##memq x vars)
                       (ct-error-syntax "Duplicate definition of a variable"))
                     (letrec-defines cte
                                     (##cons x vars)
                                     (##cons (##definition-value src) vals)
                                     (##cdr body))))
                  ((##DECLARE)
                   (##shape src src -1)
                   (letrec-defines (##cte-push-decl cte (##cdr src))
                                   vars
                                   vals
                                   (##cdr body)))
                  ((##DEFINE-MACRO)
                   (let ((x (##definition-name src)))
                     (letrec-defines (##cte-push-macro
                                       cte
                                       x
                                       (##eval-global (##definition-value src)))
                                     vars
                                     vals
                                     (##cdr body))))
                  ((##INCLUDE)
                   (##shape src src 2)
                   (letrec-defines cte
                                   vars
                                   vals
                                   (##append (##read-expressions cte src (##cadr src))
                                             (##cdr body))))
                  (else
                   (letrec-defines* cte vars vals body))))))))

      (ct-error-syntax "Body must contain at least one evaluable expression")))

  (define (letrec-defines* cte vars vals body)
    (if (##null? vars)
      (##comp-seq cte src tail? body)
      (##comp-letrec-aux cte src tail? vars vals body)))

  (letrec-defines cte '() '() body))

(define (##definition-name src)
  (##shape src src -3)
  (let ((pattern (##cadr src)))
    (touch-vars (pattern)
      (let ((name (if (##pair? pattern)
                    (let ((name (##car pattern)))
                      (touch-vars (name)
                        name))
                    (begin
                      (##shape src src 3)
                      pattern))))
        (if (##not (##symbol? name))
          (ct-error-syntax "Defined variable must be an identifier"))
        name))))

(define (##definition-value src)
  (let ((pattern (##cadr src)))
    (touch-vars (pattern)
      (if (##pair? pattern)
        (##cons 'LAMBDA (##cons (##cdr pattern) (##cddr src)))
        (##caddr src)))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-IF cte src tail?)
  (##shape src src -3)
  (if (##pair? (##cdddr src))
    (begin
      (##shape src src 4)
      (gen ##gen-if3
        (##comp cte (##cadr src) #f)
        (##comp cte (##caddr src) tail?)
        (##comp cte (##cadddr src) tail?)))
    (begin
      (##shape src src 3)
      (gen ##gen-if2
        (##comp cte (##cadr src) #f)
        (##comp cte (##caddr src) tail?)))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-COND cte src tail?)
  (##shape src src -2)
  (##comp-cond-aux cte src tail? (##cdr src)))

(define (##comp-cond-aux cte src tail? clauses)
  (if (##pair? clauses)
    (let ((clause (##touch-list (##car clauses))))
      (##shape src clause -1)
      (let ((x (##car clause)))
        (touch-vars (x)
          (cond ((##eq? x 'ELSE)
                 (##shape src clause -2)
                 (if (##not (##null? (##cdr clauses)))
                   (ct-error-syntax "ELSE clause must be last"))
                 (##comp-seq cte src tail? (##cdr clause)))
                ((##not (##pair? (##cdr clause)))
                 (gen ##gen-cond-or
                    (##comp cte (##car clause) #f)
                    (##comp-cond-aux cte src tail? (##cdr clauses))))
                (else
                 (let ((y (##cadr clause)))
                   (touch-vars (y)
                     (if (##eq? y '=>)
                       (begin
                         (##shape src clause -3)
                         (gen ##gen-cond-send
                           (##comp cte (##car clause) #f)
                           (##comp cte (##caddr clause) #f)
                           (##comp-cond-aux cte src tail? (##cdr clauses))))
                       (gen ##gen-cond-if
                         (##comp cte (##car clause) #f)
                         (##comp-seq cte src tail? (##cdr clause))
                         (##comp-cond-aux cte src tail? (##cdr clauses)))))))))))
    (gen ##gen-cst (unspecified-obj))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-AND cte src tail?)
  (let ((rest (##cdr src)))
    (if (##pair? rest)
      (##comp-and-aux cte src tail? rest)
      (gen ##gen-cst #t))))

(define (##comp-and-aux cte src tail? l)
  (let ((rest (##cdr l)))
    (if (##pair? rest)
      (gen ##gen-and
        (##comp cte (##car l) #f)
        (##comp-and-aux cte src tail? rest))
      (##comp cte (##car l) tail?))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-OR cte src tail?)
  (let ((rest (##cdr src)))
    (if (##pair? rest)
      (##comp-or-aux cte src tail? rest)
      (gen ##gen-cst #f))))

(define (##comp-or-aux cte src tail? l)
  (let ((rest (##cdr l)))
    (if (##pair? rest)
      (gen ##gen-or
        (##comp cte (##car l) #f)
        (##comp-or-aux cte src tail? rest))
      (##comp cte (##car l) tail?))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-CASE cte src tail?)
  (##shape src src -3)
  (gen ##gen-case
    (##comp cte (##cadr src) #f)
    (let ((cte (##cte-push-frame cte (##list (selector-var)))))
      (##comp-case-aux cte src tail? (##cddr src)))))

(define (##comp-case-aux cte src tail? clauses)
  (if (##pair? clauses)
    (let ((clause (##touch-list (##car clauses))))
      (##shape src clause -2)
      (let ((first (##touch-list (##car clause))))
        (if (##eq? first 'ELSE)
          (begin
            (if (##not (##null? (##cdr clauses)))
              (ct-error-syntax "ELSE clause must be last"))
            (gen ##gen-case-else
              (##comp-seq cte src tail? (##cdr clause))))
          (gen ##gen-case-clause
            first
            (##comp-seq cte src tail? (##cdr clause))
            (##comp-case-aux cte src tail? (##cdr clauses))))))
    (gen ##gen-case-else
      (gen ##gen-cst (unspecified-obj)))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-LET cte src tail?)
  (##shape src src -3)
  (let ((x (##touch-list (##cadr src))))
    (cond ((##symbol? x)
           (##shape src src -4)
           (let ((bindings (##touch-list (##caddr src))))
             (let* ((vars (##bindings->vars src bindings))
                    (vals (##bindings->vals bindings)))
               (gen ##gen-app
                 (let ((inner-cte (##cte-push-frame cte (##list x))))
                   (gen ##gen-letrec
                     (##list x)
                     (let ((cte inner-cte)
                           (tail? #f))
                       (##list (gen ##gen-prc
                               vars
                               (##comp-body (##cte-push-frame cte (##cons (self-var) vars))
                                            src
                                            #t
                                            (##cdddr src)))))
                     (let ((cte inner-cte)
                           (tail? #f))
                       (gen ##gen-loc-ref 0 1)))) ; fetch loop variable
                 (##comp-vals cte vals)))))
          ((##null? x)
           (##comp-body cte src tail? (##cddr src)))
          (else
           (let* ((bindings x)
                  (vars (##bindings->vars src bindings))
                  (vals (##bindings->vals bindings)))
             (let ((c (##comp-body (##cte-push-frame cte vars) src tail? (##cddr src))))
               (gen ##gen-let
                 vars
                 (##comp-vals cte vals)
                 c)))))))

(define (##comp-vals cte l)
  (if (##pair? l)
    (##cons (##comp cte (##car l) #f) (##comp-vals cte (##cdr l)))
    '()))

(define (##bindings->vars src bindings)
  (if (##pair? bindings)
    (let ((binding (##touch-list (##car bindings))))
      (##shape src binding 2)
      (let ((x (##car binding)))
        (touch-vars (x)
          (let ((rest (##bindings->vars src (##cdr bindings))))
            (##variable src x)
            (if (##memq x rest)
              (ct-error-syntax "Duplicate variable in bindings"))
            (##cons x rest)))))
    (if (##null? bindings)
      '()
      (ct-error-syntax "Ill-terminated bindings"))))

(define (##bindings->vals bindings)
  (if (##pair? bindings)
    (let ((binding (##touch-list (##car bindings))))
      (##cons (##cadr binding) (##bindings->vals (##cdr bindings))))
    '()))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-LET* cte src tail?)
  (##shape src src -3)
  (let ((bindings (##cadr src)))
    (touch-vars (bindings)
      (let* ((vars (##bindings->vars src bindings))
             (vals (##bindings->vals bindings)))
        (##comp-let*-aux cte src tail? vars vals (##cddr src))))))

(define (##comp-let*-aux cte src tail? vars vals body)
  (if (##pair? vars)
    (let ((frame (##list (##car vars))))
      (let ((inner-cte (##cte-push-frame cte frame)))
        (gen ##gen-let
          frame
          (##list (##comp cte (##car vals) #f))
          (##comp-let*-aux inner-cte src tail? (##cdr vars) (##cdr vals) body))))
    (##comp-body cte src tail? body)))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-LETREC cte src tail?)
  (##shape src src -3)
  (let ((bindings (##touch-list (##cadr src))))
    (if (##null? bindings)
      (##comp-body cte src tail? (##cddr src))
      (let* ((vars (##bindings->vars src bindings))
             (vals (##bindings->vals bindings)))
        (##comp-letrec-aux cte src tail? vars vals (##cddr src))))))

(define (##comp-letrec-aux cte src tail? vars vals body)
  (if (##pair? vars)
    (let ((inner-cte (##cte-push-frame cte vars)))
      (gen ##gen-letrec
        vars
        (##comp-vals inner-cte vals)
        (##comp-body inner-cte src tail? body)))
    (##comp-body cte src tail? body)))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-do cte src tail?)
  (##shape src src -3)
  (let ((bindings (##touch-list (##cadr src)))
        (exit (##touch-list (##caddr src))))
    (##shape src exit -1)
    (let* ((vars (##bindings->vars* src bindings))
           (do-loop-vars (##list (do-loop-var)))
           (inner-cte (##cte-push-frame cte do-loop-vars)))
      (gen ##gen-letrec
        do-loop-vars
        (##list
          (let ((cte inner-cte)
                (tail? #f))
            (gen ##gen-prc
              vars
              (let ((cte (##cte-push-frame cte (##cons (self-var) vars)))
                    (tail? #t))
                (gen ##gen-if3
                  (##comp cte (##car exit) #f)
                  (##comp-seq cte src tail? (##cdr exit))
                  (let ((call
                          (gen ##gen-app
                            (let ((tail? #f))
                              (gen ##gen-loc-ref 1 1)) ; fetch do-loop-var
                            (##comp-vals cte (##bindings->steps bindings)))))
                    (if (##null? (##cdddr src))
                      call
                      (gen ##gen-seq
                        (##comp-seq cte src #f (##cdddr src))
                        call))))))))
        (let ((cte inner-cte))
          (gen ##gen-app
            (let ((tail? #f))
              (gen ##gen-loc-ref 0 1)) ; fetch do-loop-var
            (##comp-vals cte (##bindings->vals bindings))))))))

(define (##bindings->vars* src bindings)
  (if (##pair? bindings)
    (let ((binding (##touch-list (##car bindings))))
      (##shape src binding -2)
      (if (##pair? (##cddr binding)) (##shape src binding 3))
      (let ((x (##car binding)))
        (touch-vars (x)
          (let ((rest (##bindings->vars* src (##cdr bindings))))
            (##variable src x)
            (if (##memq x rest)
              (ct-error-syntax "Duplicate variable in bindings"))
            (##cons x rest)))))
    (if (##null? bindings)
      '()
      (ct-error-syntax "Ill-terminated bindings"))))

(define (##bindings->steps bindings)
  (if (##pair? bindings)
    (let ((binding (##touch-list (##car bindings))))
      (##cons (if (##pair? (##cddr binding)) (##caddr binding) (##car binding))
              (##bindings->steps (##cdr bindings))))
    '()))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-app cte src tail?)
  (let ((n (##proper-length src)))
    (if n
      (gen ##gen-app
        (##comp cte (##car src) #f)
        (##comp-vals cte (##cdr src)))
      (ct-error-syntax "Ill-formed procedure application"))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-DELAY cte src tail?)
  (##shape src src 2)
  (gen ##gen-delay (##comp cte (##cadr src) #t)))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##comp-FUTURE cte src tail?)
  (##shape src src 2)
  (gen ##gen-future (##comp cte (##cadr src) #t)))

;==============================================================================

; Code generation procedures

;------------------------------------------------------------------------------

; Macros to manipulate the runtime environment

(##define-macro (mk-rte rte . lst)
  (let ((n (length lst)))
    `(let (($rte (##make-vector ,(+ n 1) (unspecified-obj))))
       (##vector-set! $rte 0 ,rte)
       ,@(let loop2 ((l lst) (i 1) (r '()))
           (if (pair? l)
             (loop2 (cdr l) (+ i 1) (cons `(##vector-set! $rte ,i ,(car l)) r))
             (reverse r)))
       $rte)))

(##define-macro (mk-rte* rte n)
  `(let (($rte (##make-vector (##fixnum.+ ,n 1) (unspecified-obj))))
     (##vector-set! $rte 0 ,rte)
     $rte))

(##define-macro (rte-up rte)         `(##vector-ref ,rte 0))
(##define-macro (rte-ref rte i)      `(##vector-ref ,rte ,i))
(##define-macro (rte-set! rte i val) `(##vector-set! ,rte ,i ,val))

;------------------------------------------------------------------------------

(define ##cprc-top
  (mk-cprc
    (##subproblem-apply0 $code rte
      (lambda ()
        (let ((rte (mk-rte rte #f)))
          (code-run (^ 0)))))))

(define ##gen-top
  (mk-gen (frames val)
    (mk-code ##cprc-top (val) frames)))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-cst-null  (mk-cprc '()))
(define ##cprc-cst-true  (mk-cprc #t))
(define ##cprc-cst-false (mk-cprc #f))
(define ##cprc-cst--2    (mk-cprc -2))
(define ##cprc-cst--1    (mk-cprc -1))
(define ##cprc-cst-0     (mk-cprc 0))
(define ##cprc-cst-1     (mk-cprc 1))
(define ##cprc-cst-2     (mk-cprc 2))
(define ##cprc-cst       (mk-cprc (^ 0)))

(define ##gen-cst
  (mk-gen (val)
    (case val
      ((()) (mk-code ##cprc-cst-null  ()))
      ((#t) (mk-code ##cprc-cst-true  ()))
      ((#f) (mk-code ##cprc-cst-false ()))
      ((-2) (mk-code ##cprc-cst--2    ()))
      ((-1) (mk-code ##cprc-cst--1    ()))
      ((0)  (mk-code ##cprc-cst-0     ()))
      ((1)  (mk-code ##cprc-cst-1     ()))
      ((2)  (mk-code ##cprc-cst-2     ()))
      (else (mk-code ##cprc-cst       () val)))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-loc-ref-0-1 (mk-cprc (rte-ref rte 1)))
(define ##cprc-loc-ref-0-2 (mk-cprc (rte-ref rte 2)))
(define ##cprc-loc-ref-0-3 (mk-cprc (rte-ref rte 3)))

(define ##cprc-loc-ref-1-1 (mk-cprc (rte-ref (rte-up rte) 1)))
(define ##cprc-loc-ref-1-2 (mk-cprc (rte-ref (rte-up rte) 2)))
(define ##cprc-loc-ref-1-3 (mk-cprc (rte-ref (rte-up rte) 3)))

(define ##cprc-loc-ref-2-1 (mk-cprc (rte-ref (rte-up (rte-up rte)) 1)))
(define ##cprc-loc-ref-2-2 (mk-cprc (rte-ref (rte-up (rte-up rte)) 2)))
(define ##cprc-loc-ref-2-3 (mk-cprc (rte-ref (rte-up (rte-up rte)) 3)))

(define ##cprc-loc-ref
  (mk-cprc
    (let loop ((e rte) (i (^ 0)))
      (if (##fixnum.< 0 i)
        (loop (rte-up e) (##fixnum.- i 1))
        (rte-ref e (^ 1))))))

(define ##gen-loc-ref
  (mk-gen (up over)
    (case up
      ((0)
       (case over
         ((1)  (mk-code ##cprc-loc-ref-0-1 ()))
         ((2)  (mk-code ##cprc-loc-ref-0-2 ()))
         ((3)  (mk-code ##cprc-loc-ref-0-3 ()))
         (else (mk-code ##cprc-loc-ref     () up over))))
      ((1)
       (case over
         ((1)  (mk-code ##cprc-loc-ref-1-1 ()))
         ((2)  (mk-code ##cprc-loc-ref-1-2 ()))
         ((3)  (mk-code ##cprc-loc-ref-1-3 ()))
         (else (mk-code ##cprc-loc-ref     () up over))))
      ((2)
       (case over
         ((1)  (mk-code ##cprc-loc-ref-2-1 ()))
         ((2)  (mk-code ##cprc-loc-ref-2-2 ()))
         ((3)  (mk-code ##cprc-loc-ref-2-3 ()))
         (else (mk-code ##cprc-loc-ref     () up over))))
     (else
       (mk-code ##cprc-loc-ref () up over)))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-glo-ref
  (mk-cprc
    (let loop ((val (global-env-ref (^ 0))))
      (if (unbound? val)
        (loop (rt-error-unbound-global-var $code rte))
        val))))

(define ##gen-glo-ref
  (mk-gen (ind)
    (mk-code ##cprc-glo-ref () ind)))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-loc-set
  (mk-cprc
    (let ((val (code-run (^ 0))))
      (let loop ((e rte) (i (^ 1)))
        (if (##fixnum.< 0 i)
          (loop (rte-up e) (##fixnum.- i 1))
          (begin
            (rte-set! e (^ 2) val)
            (set!-ret-obj)))))))

(define ##gen-loc-set
  (mk-gen (up over val)
    (mk-code ##cprc-loc-set (val) up over)))

(define ##cprc-glo-set
  (mk-cprc
    (let ((val (code-run (^ 0))))
      (global-env-set! (^ 1) val)
      (set!-ret-obj))))

(define ##gen-glo-set
  (mk-gen (ind val)
    (mk-code ##cprc-glo-set (val) ind)))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-glo-def
  (mk-cprc
    (let ((rte #f))
      (global-env-set! (^ 1) (code-run (^ 0)))
      (^ 2))))

(define ##gen-glo-def
  (mk-gen (name ind val)
    (mk-code ##cprc-glo-def (val) ind name)))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-if2
  (mk-cprc
    (let ((pred (code-run (^ 0))))
      (touch-vars (pred)
        (if (true? pred)
          (code-run (^ 1))
          (unspecified-obj))))))

(define ##gen-if2
  (mk-gen (pre con)
    (mk-code ##cprc-if2 (pre con))))

(define ##cprc-if3
  (mk-cprc
    (let ((pred (code-run (^ 0))))
      (touch-vars (pred)
        (if (true? pred)
          (code-run (^ 1))
          (code-run (^ 2)))))))

(define ##gen-if3
  (mk-gen (pre con alt)
    (mk-code ##cprc-if3 (pre con alt))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-seq
  (mk-cprc
    (code-run (^ 0))
    (code-run (^ 1))))

(define ##gen-seq
  (mk-gen (val1 val2)
    (mk-code ##cprc-seq (val1 val2))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-quasi-list->vector
  (mk-cprc
    (quasi-list->vector (code-run (^ 0)))))

(define ##gen-quasi-list->vector
  (mk-gen (val)
    (mk-code ##cprc-quasi-list->vector (val))))

(define ##cprc-quasi-append
  (mk-cprc
    (quasi-append (code-run (^ 0)) (code-run (^ 1)))))

(define ##gen-quasi-append
  (mk-gen (val1 val2)
    (mk-code ##cprc-quasi-append (val1 val2))))

(define ##cprc-quasi-cons
  (mk-cprc
    (quasi-cons (code-run (^ 0)) (code-run (^ 1)))))

(define ##gen-quasi-cons
  (mk-gen (val1 val2)
    (mk-code ##cprc-quasi-cons (val1 val2))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-cond-if
  (mk-cprc
    (let ((pred (code-run (^ 0))))
      (touch-vars (pred)
        (if (true? pred)
          (code-run (^ 1))
          (code-run (^ 2)))))))

(define ##gen-cond-if
  (mk-gen (val1 val2 val3)
    (mk-code ##cprc-cond-if (val1 val2 val3))))

(define ##cprc-cond-or
  (mk-cprc
    (let ((pred (code-run (^ 0))))
      (touch-vars (pred)
        (if (true? pred)
          pred
          (code-run (^ 1)))))))

(define ##gen-cond-or
  (mk-gen (val1 val2)
    (mk-code ##cprc-cond-or (val1 val2))))

(define ##cprc-cond-send-red
  (mk-cprc
    (let ((pred (code-run (^ 0))))
      (touch-vars (pred)
        (if (true? pred)
          (let loop ((proc (code-run (^ 1))))
            (touch-vars (proc)
              (if (##not (##procedure? proc))
                (loop (rt-error-non-procedure-send $code rte))
                (##reduction-apply1 $code rte proc pred))))
          (code-run (^ 2)))))))

(define ##cprc-cond-send-sub
  (mk-cprc
    (let ((pred (code-run (^ 0))))
      (touch-vars (pred)
        (if (true? pred)
          (let loop ((proc (code-run (^ 1))))
            (touch-vars (proc)
              (if (##not (##procedure? proc))
                (loop (rt-error-non-procedure-send $code rte))
                (##subproblem-apply1 $code rte proc pred))))
          (code-run (^ 2)))))))

(define ##gen-cond-send
  (mk-gen (val1 val2 val3)
    (mk-code (if tail? ##cprc-cond-send-red ##cprc-cond-send-sub)
             (val1 val2 val3))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-or
  (mk-cprc
    (let ((pred (code-run (^ 0))))
      (touch-vars (pred)
        (if (true? pred)
          pred
          (code-run (^ 1)))))))

(define ##gen-or
  (mk-gen (val1 val2)
    (mk-code ##cprc-or (val1 val2))))

(define ##cprc-and
  (mk-cprc
    (let ((pred (code-run (^ 0))))
      (touch-vars (pred)
        (if (##not (true? pred))
          pred
          (code-run (^ 1)))))))

(define ##gen-and
  (mk-gen (val1 val2)
    (mk-code ##cprc-and (val1 val2))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-case
  (mk-cprc
    (let ((selector (code-run (^ 0))))
      (touch-vars (selector)
        (let ((rte (mk-rte rte selector)))
          (code-run (^ 1)))))))

(define ##gen-case
  (mk-gen (val1 val2)
    (mk-code ##cprc-case (val1 val2))))

(define ##cprc-case-clause
  (mk-cprc
    (if (##case-memv (rte-ref rte 1) (^ 2))
      (code-run (^ 0))
      (code-run (^ 1)))))

(define ##gen-case-clause
  (mk-gen (cases val1 val2)
    (mk-code ##cprc-case-clause (val1 val2) cases)))

(define ##cprc-case-else
  (mk-cprc
    (code-run (^ 0))))

(define ##gen-case-else
  (mk-gen (val)
    (mk-code ##cprc-case-else (val))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-let
  (mk-cprc
    (let ((n (##fixnum.- (code-length $code) 2)))
      (let ((inner-rte (mk-rte* rte n)))
        (let loop ((i n))
          (if (##fixnum.< 0 i)
            (begin
              (rte-set! inner-rte i (code-run (code-ref $code i)))
              (loop (##fixnum.- i 1)))
            (let ((rte inner-rte))
              (code-run (^ 0)))))))))

(define ##gen-let
  (mk-gen (vars vals body)
    (let ((c (##mk-code* ##cprc-let (##cons body vals) 1)))
      (code-set! c (##fixnum.+ (##length vals) 1) vars)
      c)))

(define ##cprc-letrec
  (mk-cprc
    (let ((n (##fixnum.- (code-length $code) 2)))
      (let ((rte (mk-rte* rte n)))
        (let loop ((i n))
          (if (##fixnum.< 0 i)
            (begin
              (rte-set! rte i (code-run (code-ref $code i)))
              (loop (##fixnum.- i 1)))
            (code-run (^ 0))))))))

(define ##gen-letrec
  (mk-gen (vars vals body)
    (let ((c (##mk-code* ##cprc-letrec (##cons body vals) 1)))
      (code-set! c (##fixnum.+ (##length vals) 1) vars)
      c)))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-prc0
  (mk-cprc
    (letrec ((proc
               (lambda ()
                 (let ((rte (mk-rte rte proc)))
                   (code-run (^ 0))))))
      proc)))

(define ##cprc-prc1
  (mk-cprc
    (letrec ((proc
               (lambda (arg1)
                 (let ((rte (mk-rte rte proc arg1)))
                   (code-run (^ 0))))))
      proc)))

(define ##cprc-prc2
  (mk-cprc
    (letrec ((proc
               (lambda (arg1 arg2)
                 (let ((rte (mk-rte rte proc arg1 arg2)))
                   (code-run (^ 0))))))
      proc)))

(define ##cprc-prc3
  (mk-cprc
    (letrec ((proc
               (lambda (arg1 arg2 arg3)
                 (let ((rte (mk-rte rte proc arg1 arg2 arg3)))
                   (code-run (^ 0))))))
      proc)))

(define ##cprc-prc
  (mk-cprc
    (letrec ((proc
               (lambda args
                 (let ((n (^ 1)))
                   (let ((inner-rte (mk-rte* rte n)))
                     (rte-set! inner-rte 1 proc)
                     (let loop ((i 2) (l args))
                       (if (##fixnum.< n i)
                         (if (##pair? l)
                           (rt-error-too-many-args proc args)
                           (let ((rte inner-rte))
                             (code-run (^ 0))))
                         (if (##pair? l)
                           (begin
                             (rte-set! inner-rte i (##car l))
                             (loop (##fixnum.+ i 1) (##cdr l)))
                           (rt-error-too-few-args proc args)))))))))
      proc)))

(define ##gen-prc
  (mk-gen (frame body)
    (case (##length frame)
      ((0)  (mk-code ##cprc-prc0 (body) frame))
      ((1)  (mk-code ##cprc-prc1 (body) frame))
      ((2)  (mk-code ##cprc-prc2 (body) frame))
      ((3)  (mk-code ##cprc-prc3 (body) frame))
      (else (mk-code ##cprc-prc  (body) (##fixnum.+ (##length frame) 1) frame)))))

(define ##cprc-prc-rest
  (mk-cprc
    (letrec ((proc
               (lambda args
                 (let ((n (^ 1)))
                   (let ((inner-rte (mk-rte* rte n)))
                     (rte-set! inner-rte 1 proc)
                     (let loop ((i 2) (l args))
                       (if (##fixnum.< i n)
                         (if (##pair? l)
                           (begin
                             (rte-set! inner-rte i (##car l))
                             (loop (##fixnum.+ i 1) (##cdr l)))
                           (rt-error-too-few-args proc args))
                         (begin
                           (rte-set! inner-rte i l)
                           (let ((rte inner-rte))
                             (code-run (^ 0)))))))))))
      proc)))

(define ##gen-prc-rest
  (mk-gen (frame body)
    (mk-code ##cprc-prc-rest (body) (##fixnum.+ (##length frame) 1) frame)))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-app0-red
  (mk-cprc
    (let ((proc (code-run (^ 0))))
      (touch-vars (proc)
        (if (##not (##procedure? proc))
          (rt-error-non-procedure-oper $code rte)
          (##reduction-apply0 $code rte proc))))))

(define ##cprc-app1-red
  (mk-cprc
    (let ((proc (code-run (^ 0))))
      (touch-vars (proc)
        (if (##not (##procedure? proc))
          (rt-error-non-procedure-oper $code rte)
          (let ((arg1 (code-run (^ 1))))
            (##reduction-apply1 $code rte proc arg1)))))))

(define ##cprc-app2-red
  (mk-cprc
    (let ((proc (code-run (^ 0))))
      (touch-vars (proc)
        (if (##not (##procedure? proc))
          (rt-error-non-procedure-oper $code rte)
          (let ((arg1 (code-run (^ 1)))
                (arg2 (code-run (^ 2))))
            (##reduction-apply2 $code rte proc arg1 arg2)))))))

(define ##cprc-app3-red
  (mk-cprc
    (let ((proc (code-run (^ 0))))
      (touch-vars (proc)
        (if (##not (##procedure? proc))
          (rt-error-non-procedure-oper $code rte)
          (let ((arg1 (code-run (^ 1)))
                (arg2 (code-run (^ 2)))
                (arg3 (code-run (^ 3))))
            (##reduction-apply3 $code rte proc arg1 arg2 arg3)))))))

(define ##cprc-app-red
  (mk-cprc
    (let ((proc (code-run (^ 0))))
      (touch-vars (proc)
        (if (##not (##procedure? proc))
          (rt-error-non-procedure-oper $code rte)
          (let loop ((i (##fixnum.- (code-length $code) 1)) (args '()))
            (if (##fixnum.< 0 i)
              (loop (##fixnum.- i 1) (##cons (code-run (code-ref $code i)) args))
              (##reduction-apply $code rte proc args))))))))

(define ##cprc-app0-sub
  (mk-cprc
    (let ((proc (code-run (^ 0))))
      (touch-vars (proc)
        (if (##not (##procedure? proc))
          (rt-error-non-procedure-oper $code rte)
          (##subproblem-apply0 $code rte proc))))))

(define ##cprc-app1-sub
  (mk-cprc
    (let ((proc (code-run (^ 0))))
      (touch-vars (proc)
        (if (##not (##procedure? proc))
          (rt-error-non-procedure-oper $code rte)
          (let ((arg1 (code-run (^ 1))))
            (##subproblem-apply1 $code rte proc arg1)))))))

(define ##cprc-app2-sub
  (mk-cprc
    (let ((proc (code-run (^ 0))))
      (touch-vars (proc)
        (if (##not (##procedure? proc))
          (rt-error-non-procedure-oper $code rte)
          (let ((arg1 (code-run (^ 1)))
                (arg2 (code-run (^ 2))))
            (##subproblem-apply2 $code rte proc arg1 arg2)))))))

(define ##cprc-app3-sub
  (mk-cprc
    (let ((proc (code-run (^ 0))))
      (touch-vars (proc)
        (if (##not (##procedure? proc))
          (rt-error-non-procedure-oper $code rte)
          (let ((arg1 (code-run (^ 1)))
                (arg2 (code-run (^ 2)))
                (arg3 (code-run (^ 3))))
            (##subproblem-apply3 $code rte proc arg1 arg2 arg3)))))))

(define ##cprc-app-sub
  (mk-cprc
    (let ((proc (code-run (^ 0))))
      (touch-vars (proc)
        (if (##not (##procedure? proc))
          (rt-error-non-procedure-oper $code rte)
          (let loop ((i (##fixnum.- (code-length $code) 1)) (args '()))
            (if (##fixnum.< 0 i)
              (loop (##fixnum.- i 1) (##cons (code-run (code-ref $code i)) args))
              (##subproblem-apply $code rte proc args))))))))

(define ##gen-app
  (mk-gen (oper args)
    (case (##length args)
      ((0)  (mk-code    (if tail? ##cprc-app0-red ##cprc-app0-sub) (oper)))
      ((1)  (mk-code    (if tail? ##cprc-app1-red ##cprc-app1-sub) (oper (##car args))))
      ((2)  (mk-code    (if tail? ##cprc-app2-red ##cprc-app2-sub) (oper (##car args) (##cadr args))))
      ((3)  (mk-code    (if tail? ##cprc-app3-red ##cprc-app3-sub) (oper (##car args) (##cadr args) (##caddr args))))
      (else (##mk-code* (if tail? ##cprc-app-red  ##cprc-app-sub)  (##cons oper args) 0)))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##reduction-apply0 $code rte proc)
  (##declare (intr-checks))
  (proc))

(define (##reduction-apply1 $code rte proc arg1)
  (##declare (intr-checks))
  (proc arg1))

(define (##reduction-apply2 $code rte proc arg1 arg2)
  (##declare (intr-checks))
  (proc arg1 arg2))

(define (##reduction-apply3 $code rte proc arg1 arg2 arg3)
  (##declare (intr-checks))
  (proc arg1 arg2 arg3))

(define (##reduction-apply $code rte proc args)
  (##declare (intr-checks))
  (##apply proc args))

(define (##subproblem-apply0 $code rte proc)
  (##declare (intr-checks))
  (let ((result (proc)))
    (let ((a $code) (b rte))
      result)))

(define (##subproblem-apply1 $code rte proc arg1)
  (##declare (intr-checks))
  (let ((result (proc arg1)))
    (let ((a $code) (b rte))
      result)))

(define (##subproblem-apply2 $code rte proc arg1 arg2)
  (##declare (intr-checks))
  (let ((result (proc arg1 arg2)))
    (let ((a $code) (b rte))
      result)))

(define (##subproblem-apply3 $code rte proc arg1 arg2 arg3)
  (##declare (intr-checks))
  (let ((result (proc arg1 arg2 arg3)))
    (let ((a $code) (b rte))
      result)))

(define (##subproblem-apply $code rte proc args)
  (##declare (intr-checks))
  (let ((result (##apply proc args)))
    (let ((a $code) (b rte))
      result)))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-delay
  (mk-cprc
    (delay (code-run (^ 0)))))

(define ##gen-delay
  (mk-gen (val)
    (mk-code ##cprc-delay (val))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##cprc-future
  (mk-cprc
    (future (code-run (^ 0)))))

(define ##gen-future
  (mk-gen (val)
    (mk-code ##cprc-future (val))))

;------------------------------------------------------------------------------

; Access to compiler created structures for interpreter procedures and frames

(define ##int-proc-body-format-1
  (##list (##proc-closure-body (##cprc-prc0 #f #f))
          (##proc-closure-body (##cprc-prc1 #f #f))
          (##proc-closure-body (##cprc-prc2 #f #f))
          (##proc-closure-body (##cprc-prc3 #f #f))))

(define ##int-proc-body-format-2
  (##list (##proc-closure-body (##cprc-prc       #f #f))
          (##proc-closure-body (##cprc-prc-rest  #f #f))))

(define (##int-proc? x)
  (and (##procedure? x)
       (##proc-closure? x)
       (or (##memq (##proc-closure-body x) ##int-proc-body-format-1)
           (##memq (##proc-closure-body x) ##int-proc-body-format-2))))

(define (##int-proc-code x)
  (if (##memq (##proc-closure-body x) ##int-proc-body-format-1)
    (##proc-closure-ref x 0)
    (##proc-closure-ref x 2)))

(define (##int-proc-rte x)
  (if (##memq (##proc-closure-body x) ##int-proc-body-format-1)
    (##proc-closure-ref x 2)
    (##proc-closure-ref x 1)))

;==============================================================================

; Eval

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

; Evaluation in the global environment (with current dynamic env)

(define (##eval-global expr)
  (##eval expr #f #f (##dynamic-env-ref)))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

; Evaluation in a particular environment ('frames' describes the runtime
; environment 'rte').

(define (##eval expr frames rte dyn-env)
  (let ((c (##compile expr frames)))
    (##dynamic-env-bind
      dyn-env
      (lambda () (let ((rte rte)) (code-run c))))))

;==============================================================================

; Decompilation of a piece of code

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(##define-macro (mk-degen params . def)
  `(lambda ($code ,@params) ,@def))

(##define-macro (degen proc . args)
  `(,proc $code ,@args))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##extract-frame subcode up)
  (let (($code (code-link subcode)))
    (if $code
      (let ((cprc (code-cprc $code)))
        (cond ((##eq? cprc ##cprc-top)
               (##extract-frame-top $code subcode up))
              ((##eq? cprc ##cprc-glo-def)
               (##extract-frame-glo-def $code subcode up))
              ((##eq? cprc ##cprc-case)
               (##extract-frame-case $code subcode up))
              ((##eq? cprc ##cprc-let)
               (##extract-frame-let $code subcode up))
              ((##eq? cprc ##cprc-letrec)
               (##extract-frame-letrec $code subcode up))
              ((or (##eq? cprc ##cprc-prc0)
                   (##eq? cprc ##cprc-prc1)
                   (##eq? cprc ##cprc-prc2)
                   (##eq? cprc ##cprc-prc3)
                   (##eq? cprc ##cprc-prc)
                   (##eq? cprc ##cprc-prc-rest))
               (##extract-frame-prc $code subcode up))
              (else
               (##extract-frame-default $code subcode up))))
      #f)))

(define ##extract-frame-default
  (lambda ($code subcode up)
    (##extract-frame $code up)))

(define ##extract-frame-top
  (lambda ($code subcode up)
    (if (##fixnum.= up 0)
      (##list (self-var))
      (let loop ((frames (^ 1)) (up (##fixnum.- up 1)))
        (if frames
          (if (##fixnum.= up 0)
            (##car frames)
            (loop (##cdr frames) (##fixnum.- up 1)))
          #f)))))

(define ##extract-frame-glo-def
  (lambda ($code subcode up)
    #f))

(define ##extract-frame-case
  (lambda ($code subcode up)
    (if (##eq? subcode (^ 1))
      (if (##fixnum.= up 0)
        (##list (selector-var))
        (##extract-frame $code (##fixnum.- up 1)))
      (##extract-frame $code up))))

(define ##extract-frame-let
  (lambda ($code subcode up)
    (if (##eq? subcode (^ 0))
      (if (##fixnum.= up 0)
        (code-ref $code (##fixnum.- (code-length $code) 1))
        (##extract-frame $code (##fixnum.- up 1)))
      (##extract-frame $code up))))

(define ##extract-frame-letrec
  (lambda ($code subcode up)
    (if (##fixnum.= up 0)
      (code-ref $code (##fixnum.- (code-length $code) 1))
      (##extract-frame $code (##fixnum.- up 1)))))

(define ##extract-frame-prc
  (lambda ($code subcode up)
    (if (##fixnum.= up 0)
      (##cons (self-var) (code-ref $code (##fixnum.- (code-length $code) 1)))
      (##extract-frame $code (##fixnum.- up 1)))))

(define (##extract-frames $code)

  (define (rev l tail)
    (if (##pair? l) (rev (##cdr l) (##cons (##car l) tail)) tail))

  (let loop ((i 0) (frames '()))
    (let ((frame (##extract-frame $code i)))
      (if frame
        (loop (##fixnum.+ i 1) (##cons frame frames))
        (rev frames #f)))))

(define (##extract-proc $code rte)
  (let loop ((i 0) (rte rte))
    (let ((frame (##extract-frame $code i)))
      (if frame
        (if (and (##pair? frame) (##eq? (##car frame) (self-var)))
          (rte-ref rte 1)
          (loop (##fixnum.+ i 1) (rte-up rte)))
        #f))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##BEGIN? x) (and (##pair? x) (##eq? (##car x) 'BEGIN)))
(define (##COND? x)  (and (##pair? x) (##eq? (##car x) 'COND)))
(define (##AND? x)   (and (##pair? x) (##eq? (##car x) 'AND)))
(define (##OR? x)    (and (##pair? x) (##eq? (##car x) 'OR)))
(define (##unspecified-obj? x)
  (and (##pair? x) (##eq? (##car x) 'QUOTE) (##eq? (##cadr x) (unspecified-obj))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define ##degen-top
  (mk-degen ()
    (##decomp (^ 0))))

(define ##degen-cst-x
  (mk-degen (val)
    (if (##self-eval? val) val (##list 'QUOTE val))))

(define ##degen-cst
  (mk-degen ()
    (degen ##degen-cst-x (^ 0))))

(define ##degen-loc-ref-x-y
  (mk-degen (up over)
    (degen ##degen-up-over up over)))

(define ##degen-up-over
  (mk-degen (up over)
    (let loop ((l (##extract-frame $code up)) (i over))
      (if (##fixnum.< i 2)
        (##car l)
        (loop (##cdr l) (##fixnum.- i 1))))))

(define ##degen-loc-ref
  (mk-degen ()
    (degen ##degen-loc-ref-x-y (^ 0) (^ 1))))

(define ##degen-glo-ref
  (mk-degen ()
    (global-env-loc->var (^ 0))))

(define ##degen-loc-set
  (mk-degen ()
    (##list 'SET! (degen ##degen-up-over (^ 1) (^ 2))
                  (##decomp (^ 0)))))

(define ##degen-glo-set
  (mk-degen ()
    (##list 'SET! (global-env-loc->var (^ 1))
                  (##decomp (^ 0)))))

(define ##degen-glo-def
  (mk-degen ()
    (##list 'DEFINE (global-env-loc->var (^ 1))
                    (##decomp (^ 0)))))

(define ##degen-if2
  (mk-degen ()
    (##list 'IF (##decomp (^ 0))
                (##decomp (^ 1)))))

(define ##degen-if3
  (mk-degen ()
    (##list 'IF (##decomp (^ 0))
                (##decomp (^ 1))
                (##decomp (^ 2)))))

(define ##degen-seq
  (mk-degen ()
    (let ((val1 (##decomp (^ 0)))
          (val2 (##decomp (^ 1))))
      (if (##BEGIN? val2)
        (##cons 'BEGIN (##cons val1 (##cdr val2)))
        (##list 'BEGIN val1 val2)))))

(define ##degen-quasi-list->vector
  (mk-degen ()
    (##list 'QUASIQUOTE (##make-vector 1 (##list 'UNQUOTE-SPLICING (##decomp (^ 0)))))))

(define ##degen-quasi-append
  (mk-degen ()
    (##list 'QUASIQUOTE (##list (##list 'UNQUOTE-SPLICING (##decomp (^ 0)))
                                (##list 'UNQUOTE-SPLICING (##decomp (^ 1)))))))

(define ##degen-quasi-cons
  (mk-degen ()
    (##list 'QUASIQUOTE (##list (##list 'UNQUOTE (##decomp (^ 0)))
                                (##list 'UNQUOTE-SPLICING (##decomp (^ 1)))))))

(define ##degen-cond-if
  (mk-degen ()
    (let ((val1 (##decomp (^ 0)))
          (val2 (##decomp (^ 1)))
          (val3 (##decomp (^ 2))))
      (##build-cond
        (if (##BEGIN? val2) (##cons val1 (##cdr val2)) (##list val1 val2))
        val3))))

(define ##degen-cond-or
  (mk-degen ()
    (let ((val1 (##decomp (^ 0)))
          (val2 (##decomp (^ 1))))
      (##build-cond (##list val1) val2))))

(define ##degen-cond-send
  (mk-degen ()
    (let ((val1 (##decomp (^ 0)))
          (val2 (##decomp (^ 1)))
          (val3 (##decomp (^ 2))))
      (##build-cond (##list val1 '=> val2) val3))))

(define (##build-cond clause rest)
  (cond ((##COND? rest)
         (##cons 'COND (##cons clause (##cdr rest))))
        ((##BEGIN? rest)
         (##cons 'COND (##list clause (##cons 'ELSE (##cdr rest)))))
        ((##unspecified-obj? rest)
         (##list 'COND clause))
        (else
         (##list 'COND clause (##list 'ELSE rest)))))

(define ##degen-or
  (mk-degen ()
    (let ((val1 (##decomp (^ 0)))
          (val2 (##decomp (^ 1))))
      (if (##OR? val2)
        (##cons 'OR (##cons val1 (##cdr val2)))
        (##list 'OR val1 val2)))))

(define ##degen-and
  (mk-degen ()
    (let ((val1 (##decomp (^ 0)))
          (val2 (##decomp (^ 1))))
      (if (##AND? val2)
        (##cons 'AND (##cons val1 (##cdr val2)))
        (##list 'AND val1 val2)))))

(define ##degen-case
  (mk-degen ()
    (let ((val1 (##decomp (^ 0)))
          (val2 (##decomp (^ 1))))
      (##cons 'CASE (##cons val1 val2)))))

(define ##degen-case-clause
  (mk-degen ()
    (let ((val1 (##decomp (^ 0)))
          (val2 (##decomp (^ 1))))
      (##cons (if (##BEGIN? val1)
                (##cons (^ 2) (##cdr val1))
                (##list (^ 2) val1))
              val2))))

(define ##degen-case-else
  (mk-degen ()
    (let ((val (##decomp (^ 0))))
      (if (##unspecified-obj? val)
        '()
        (##list (if (##BEGIN? val)
                  (##cons 'ELSE (##cdr val))
                  (##list 'ELSE val)))))))

(define ##degen-let
  (mk-degen ()
    (let ((n (code-length $code)))
      (let loop ((i (##fixnum.- n 2)) (vals '()))
        (if (##fixnum.< 0 i)
          (loop (##fixnum.- i 1)
                (##cons (##decomp (code-ref $code i)) vals))
          (let ((body (##decomp (^ 0)))
                (bindings (##make-bindings (code-ref $code (##fixnum.- n 1)) vals)))
            (if (##BEGIN? body)
              (##cons 'LET (##cons bindings (##cdr body)))
              (##list 'LET bindings body))))))))

(define (##make-bindings l1 l2)
  (if (##pair? l1)
    (##cons (##list (##car l1) (##car l2))
            (##make-bindings (##cdr l1) (##cdr l2)))
    '()))

(define ##degen-letrec
  (mk-degen ()
    (let ((n (code-length $code)))
      (let loop ((i (##fixnum.- n 2)) (vals '()))
        (if (##fixnum.< 0 i)
          (loop (##fixnum.- i 1)
                (##cons (##decomp (code-ref $code i)) vals))
          (let ((body (##decomp (^ 0)))
                (bindings (##make-bindings (code-ref $code (##fixnum.- n 1)) vals)))
            (if (##BEGIN? body)
              (##cons 'LETREC (##cons bindings (##cdr body)))
              (##list 'LETREC bindings body))))))))

(define ##degen-prc
  (mk-degen ()
    (let ((body (##decomp (^ 0)))
          (params (code-ref $code (##fixnum.- (code-length $code) 1))))
      (if (##BEGIN? body)
        (##cons 'LAMBDA (##cons params (##cdr body)))
        (##list 'LAMBDA params body)))))

(define ##degen-prc-rest
  (mk-degen ()
    (let ((body (##decomp (^ 0)))
          (params (##make-rest-params (^ 2))))
      (if (##BEGIN? body)
        (##cons 'LAMBDA (##cons params (##cdr body)))
        (##list 'LAMBDA params body)))))

(define (##make-rest-params l)
  (if (##null? (##cdr l))
    (##car l)
    (##cons (##car l) (##make-rest-params (##cdr l)))))

(define ##degen-app0
  (mk-degen ()
    (##list (##decomp (^ 0)))))

(define ##degen-app1
  (mk-degen ()
    (##list (##decomp (^ 0))
            (##decomp (^ 1)))))

(define ##degen-app2
  (mk-degen ()
    (##list (##decomp (^ 0))
            (##decomp (^ 1))
            (##decomp (^ 2)))))

(define ##degen-app3
  (mk-degen ()
    (##list (##decomp (^ 0))
            (##decomp (^ 1))
            (##decomp (^ 2))
            (##decomp (^ 3)))))

(define ##degen-app
  (mk-degen ()
    (let ((n (code-length $code)))
      (let loop ((i (##fixnum.- n 1)) (vals '()))
        (if (##not (##fixnum.< i 0))
          (loop (##fixnum.- i 1)
                (##cons (##decomp (code-ref $code i)) vals))
          vals)))))

(define ##degen-delay
  (mk-degen ()
    (##list 'DELAY (##decomp (^ 0)))))

(define ##degen-future
  (mk-degen ()
    (##list 'FUTURE (##decomp (^ 0)))))

;------------------------------------------------------------------------------

(define ##decomp-dispatch-table
  (##list
    (##cons ##cprc-top           ##degen-top)

    (##cons ##cprc-cst-null      (mk-degen () (degen ##degen-cst-x '())))
    (##cons ##cprc-cst-true      (mk-degen () (degen ##degen-cst-x #t)))
    (##cons ##cprc-cst-false     (mk-degen () (degen ##degen-cst-x #f)))
    (##cons ##cprc-cst--2        (mk-degen () (degen ##degen-cst-x -2)))
    (##cons ##cprc-cst--1        (mk-degen () (degen ##degen-cst-x -1)))
    (##cons ##cprc-cst-0         (mk-degen () (degen ##degen-cst-x 0)))
    (##cons ##cprc-cst-1         (mk-degen () (degen ##degen-cst-x 1)))
    (##cons ##cprc-cst-2         (mk-degen () (degen ##degen-cst-x 2)))
    (##cons ##cprc-cst           ##degen-cst)

    (##cons ##cprc-loc-ref-0-1   (mk-degen () (degen ##degen-loc-ref-x-y 0 1)))
    (##cons ##cprc-loc-ref-0-2   (mk-degen () (degen ##degen-loc-ref-x-y 0 2)))
    (##cons ##cprc-loc-ref-0-3   (mk-degen () (degen ##degen-loc-ref-x-y 0 3)))
    (##cons ##cprc-loc-ref-1-1   (mk-degen () (degen ##degen-loc-ref-x-y 1 1)))
    (##cons ##cprc-loc-ref-1-2   (mk-degen () (degen ##degen-loc-ref-x-y 1 2)))
    (##cons ##cprc-loc-ref-1-3   (mk-degen () (degen ##degen-loc-ref-x-y 1 3)))
    (##cons ##cprc-loc-ref-2-1   (mk-degen () (degen ##degen-loc-ref-x-y 2 1)))
    (##cons ##cprc-loc-ref-2-2   (mk-degen () (degen ##degen-loc-ref-x-y 2 2)))
    (##cons ##cprc-loc-ref-2-3   (mk-degen () (degen ##degen-loc-ref-x-y 2 3)))
    (##cons ##cprc-loc-ref       ##degen-loc-ref)
    (##cons ##cprc-glo-ref       ##degen-glo-ref)

    (##cons ##cprc-loc-set       ##degen-loc-set)
    (##cons ##cprc-glo-set       ##degen-glo-set)
    (##cons ##cprc-glo-def       ##degen-glo-def)

    (##cons ##cprc-if2           ##degen-if2)
    (##cons ##cprc-if3           ##degen-if3)
    (##cons ##cprc-seq           ##degen-seq)
    (##cons ##cprc-quasi-list->vector ##degen-quasi-list->vector)
    (##cons ##cprc-quasi-append  ##degen-quasi-append)
    (##cons ##cprc-quasi-cons    ##degen-quasi-cons)
    (##cons ##cprc-cond-if       ##degen-cond-if)
    (##cons ##cprc-cond-or       ##degen-cond-or)
    (##cons ##cprc-cond-send-red ##degen-cond-send)
    (##cons ##cprc-cond-send-sub ##degen-cond-send)

    (##cons ##cprc-or            ##degen-or)
    (##cons ##cprc-and           ##degen-and)

    (##cons ##cprc-case          ##degen-case)
    (##cons ##cprc-case-clause   ##degen-case-clause)
    (##cons ##cprc-case-else     ##degen-case-else)

    (##cons ##cprc-let           ##degen-let)
    (##cons ##cprc-letrec        ##degen-letrec)

    (##cons ##cprc-prc0          ##degen-prc)
    (##cons ##cprc-prc1          ##degen-prc)
    (##cons ##cprc-prc2          ##degen-prc)
    (##cons ##cprc-prc3          ##degen-prc)
    (##cons ##cprc-prc           ##degen-prc)
    (##cons ##cprc-prc-rest      ##degen-prc-rest)

    (##cons ##cprc-app0-red      ##degen-app0)
    (##cons ##cprc-app1-red      ##degen-app1)
    (##cons ##cprc-app2-red      ##degen-app2)
    (##cons ##cprc-app3-red      ##degen-app3)
    (##cons ##cprc-app-red       ##degen-app)
    (##cons ##cprc-app0-sub      ##degen-app0)
    (##cons ##cprc-app1-sub      ##degen-app1)
    (##cons ##cprc-app2-sub      ##degen-app2)
    (##cons ##cprc-app3-sub      ##degen-app3)
    (##cons ##cprc-app-sub       ##degen-app)

    (##cons ##cprc-delay         ##degen-delay)
    (##cons ##cprc-future        ##degen-future)
))

;------------------------------------------------------------------------------

(define (##decomp $code)
  (let ((cprc (code-cprc $code)))
    (let ((x (##assq cprc ##decomp-dispatch-table)))
      (if x
        (degen (##cdr x))
        '?))))

(define (##decompile proc)

  (define (decomp1 p)
    (if (##proc-subproc? p)
      (decomp2 (##proc-subproc-parent p) (##proc-subproc-tag p))
      (decomp2 p 0)))

  (define (decomp2 parent tag)
    (let ((info (##proc-debug-info parent)))
      (if info
        (let ((v (##vector-ref info 0)))
          (let loop ((i (##fixnum.- (##vector-length v) 1)))
            (if (##fixnum.< i 0)
              proc
              (let ((x (##vector-ref v i)))
                (if (##fixnum.= tag (##vector-ref x 0))
                  (source->expression (##vector-ref x 1))
                  (loop (##fixnum.- i 1)))))))
        proc)))

  (define (source-code x)
    (##vector-ref x 0))

  (define (source->expression source)

    (define (list->expression l)
      (cond ((##pair? l)
             (##cons (source->expression (##car l))
                     (list->expression (##cdr l))))
            ((##null? l)
             '())
            (else
             (source->expression l))))

    (define (vector->expression v)
      (let* ((len (##vector-length v))
             (x (##make-vector len #f)))
        (let loop ((i (##fixnum.- len 1)))
          (if (##not (##fixnum.< i 0))
            (begin
              (##vector-set! x i (source->expression (##vector-ref v i)))
              (loop (##fixnum.- i 1)))))
        x))

    (let ((code (source-code source)))
      (cond ((##pair? code)   (list->expression code))
            ((##vector? code) (vector->expression code))
            (else             code))))

  (cond ((##int-proc? proc)
         (##decomp (##int-proc-code proc)))
        ((##proc-closure? proc)
         (decomp1 (##proc-closure-body proc)))
        (else
         (decomp1 proc))))

;==============================================================================

; Debugger

;------------------------------------------------------------------------------

; Access to interpreter continuation frames

(define (##int-frame-non-subproblem? f)
  (let ((parent (##proc-subproc-parent (##frame-ret f))))
    (##assq parent ##decomp-dispatch-table)))

(define (##int-frame-subproblem? f)
  (let ((parent (##proc-subproc-parent (##frame-ret f))))
    (or (##eq? parent ##subproblem-apply0)
        (##eq? parent ##subproblem-apply1)
        (##eq? parent ##subproblem-apply2)
        (##eq? parent ##subproblem-apply3)
        (##eq? parent ##subproblem-apply))))

(define (##int-frame-subproblem-code f)
  (let ((parent (##proc-subproc-parent (##frame-ret f))))
    (if (##eq? parent ##subproblem-apply0)
      (##frame-stk-ref f 2)
      (##frame-stk-ref f 1))))

(define (##int-frame-subproblem-rte f)
  (let ((parent (##proc-subproc-parent (##frame-ret f))))
    (if (or (##eq? parent ##subproblem-apply2)
            (##eq? parent ##subproblem-apply3))
      (##frame-stk-ref f 2)
      (##frame-stk-ref f 3))))

;------------------------------------------------------------------------------

; Utilities

(define (##continuation->subproblems cont)
  (let loop ((f (##continuation->frame cont)) (l '()))
    (if f
      (if (##int-frame-non-subproblem? f)
        (loop (##frame-next f) l)
        (loop (##frame-next f) (##cons f l)))
      (##reverse l))))

(define (##eval-within expr f dyn-bindings)
  (let ((dyn-env (##cons dyn-bindings (##frame-dyn-env f))))
    (if (##int-frame-subproblem? f)
      (##eval expr
              (##extract-frames (##int-frame-subproblem-code f))
              (##int-frame-subproblem-rte f)
              dyn-env)
      (##eval expr #f #f dyn-env))))

(define (##procedure-name p)
  (or (##object->global-var-name p) p))

;------------------------------------------------------------------------------

; Read eval print loop

(define (##repl (in ##stdin) (out ##stdout) (prompt2 ": ") (prompt1 ""))
  (##call-with-current-continuation
    (lambda (cont) (##read-eval-print in out prompt2 prompt1 cont))))

(define ##repl-write #f)
(set! ##repl-write #f)

(define ##repl-read #f)
(set! ##repl-read #f)

(define (##read-eval-print in out prompt2 prompt1 cont)

  (define (repl-start subprobs repl-info dyn-bindings)

    (define (repl-read)
      (let ((proc ##repl-read))
        (if (##procedure? proc)
          (proc in)
          (##read in))))

    (define (repl-write val)
      (let ((proc ##repl-write))
        (if (##procedure? proc)
          (proc val out)
          (begin
            (##write val out (if-touches #t #f))
            (##newline out)))))

    (define (repl-n n)
      (let loop ((i 0) (s subprobs))
        (if (and (##fixnum.< n i) (##pair? (##cdr s)))
          (loop (##fixnum.- i 1) (##cdr s))
          (let ((f (##car s)))
            (##display-subproblem i f out)
            (repl i s f)))))

    (define (cmd-d)
      (let ((l (##cdr (##vector-ref repl-info 3))))
        (if (##pair? l)
          ((##car l) #f)
          (begin
            (##newline out)
            (##write-string "*** ^D again to exit" out)
            (##newline out)
            (if (##eof-object? (##peek-char in))
              (##quit))))))

    (define (cmd-t)
      (let loop ((l (##vector-ref repl-info 3)))
        (if (##pair? (##cdr l))
          (loop (##cdr l))
          ((##car l) #f))))

    (define (repl pos subprobs* f)

      (##call-with-current-continuation
        (lambda (abort)
          (##set-car! (##vector-ref repl-info 3) abort)))

      (let loop ()

        (##newline out)
        (##display prompt1 out #f)
        (if (##fixnum.< pos 0) (##display pos out #f))
        (##display prompt2 out #f)

        (let ((expr (repl-read)))
          (if (##eof-object? expr)
            (begin (cmd-d) (loop))
            (if (and (##pair? expr)
                     (##pair? (##cdr expr))
                     (##null? (##cddr expr))
                     (##eq? (##car expr) 'UNQUOTE))
              (let ((cmd (##cadr expr)))
                (if (##eof-object? cmd)
                  (begin (cmd-d) (loop))
                  (case cmd
                    ((?) (##cmd-? out) (loop))
                    ((-) (repl-n (##fixnum.- pos 1)))
                    ((+) (repl-n (##fixnum.+ pos 1)))
                    ((b) (##cmd-b pos subprobs* out) (loop))
                    ((p) (##cmd-p f out) (loop))
                    ((e) (##cmd-e f out) (loop))
                    ((l) (##cmd-l f out) (loop))
                    ((t) (cmd-t))
                    ((d) (cmd-d) (loop))
                    ((r) (##display "Value: " out #f)
                         (let ((expr (repl-read)))
                           (if (##eof-object? expr)
                             ##undef-object
                             (##eval-within expr f dyn-bindings))))
                    ((q) (##quit))
                    (else
                     (if (and (##fixnum? cmd) (##fixnum.< cmd 1))
                       (repl-n cmd)
                       (begin
                         (##write-string "Unknown command ," out)
                         (##write cmd out #f)
                         (##newline out)
                         (loop)))))))
            (let ((val (##eval-within expr f dyn-bindings)))
              (repl-write val)
              (loop)))))))

    (repl 0 subprobs (##car subprobs)))

  (let ((repl-info (##make-vector 4 #f)))
    (let ((prev-info (##dynamic-ref '##REPL-INFO #f))
          (dyn-bindings (##list (##cons '##REPL-INFO repl-info))))
      (##vector-set! repl-info 0 in)
      (##vector-set! repl-info 1 out)
      (##vector-set! repl-info 2
        (if prev-info
          (##fixnum.+ (##vector-ref prev-info 2) 1)
          0))
      (##vector-set! repl-info 3
        (##cons (lambda (x) (##quit))
                (if prev-info
                  (##vector-ref prev-info 3)
                  '())))
      (##dynamic-bind
        dyn-bindings
        (lambda ()
          (repl-start (##continuation->subproblems cont)
                      repl-info
                      dyn-bindings))))))

(define (##repl-out)
  (let ((repl-info (##dynamic-ref '##REPL-INFO #f)))
    (if repl-info
      (##vector-ref repl-info 1)
      ##stdout)))

(define (##debug-repl cont)
  (let ((repl-info (##dynamic-ref '##REPL-INFO #f)))
    (if repl-info
      (##read-eval-print (##vector-ref repl-info 0)
                         (##vector-ref repl-info 1)
                         ": "
                         (##fixnum.+ (##vector-ref repl-info 2) 1)
                         cont)
      (##read-eval-print ##stdin ##stdout ": " 0 cont))))

(define (##pop-repl)
  (let ((repl-info (##dynamic-ref '##REPL-INFO #f)))
    (if repl-info
      ((##car (##vector-ref repl-info 3)) #f)
      (##quit))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##cmd-? out)
  (##write-string ",?        : Summary of commands" out) (##newline out)
  (##write-string ",+ and ,- : Move to next or previous frame of continuation" out) (##newline out)
  (##write-string ",<n>      : Move to particular frame (<n> <= 0)" out) (##newline out)
  (##write-string ",b        : Display frames of continuation (i.e. backtrace)" out) (##newline out)
  (##write-string ",p        : Display procedure attached to current frame" out) (##newline out)
  (##write-string ",e        : Display subproblem of current frame" out) (##newline out)
  (##write-string ",l        : Display list of local variables accessible in current frame" out) (##newline out)
  (##write-string ",t        : Transfer to top-level REP loop" out) (##newline out)
  (##write-string ",d        : Transfer to previous REP loop" out) (##newline out)
  (##write-string ",r        : Return from REP loop" out) (##newline out)
  (##write-string ",q        : Quit" out) (##newline out))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##cmd-b pos subprobs* out)
  (define max-head 10)
  (define max-tail 6)
  (let loop ((i 0) (j (##fixnum.- (##length subprobs*) 1)) (l subprobs*))
    (if (##pair? l)
      (begin
        (cond ((or (##fixnum.< i max-head) (##fixnum.< j max-tail)
                   (and (##fixnum.= i max-head) (##fixnum.= j max-tail)))
               (##display-subproblem (##fixnum.- pos i) (##car l) out))
              ((##fixnum.= i max-head)
               (##write-string "..." out) (##newline out)))
        (loop (##fixnum.+ i 1) (##fixnum.- j 1) (##cdr l))))))

(define (##display-subproblem pos f out)
  (let ((x (##write pos out #f)))
    (##display-spaces (##fixnum.- 4 x) out)
    (##write-string " " out)

    (if (##int-frame-subproblem? f)

      (let ((code (##int-frame-subproblem-code f))
            (rte (##int-frame-subproblem-rte f)))
        (let ((proc (##extract-proc code rte)))
          (let ((x (if proc
                     (##write (##procedure-name proc) out #f)
                     (##display "(top level)" out #f))))
            (##display-spaces (##fixnum.- 25 x) out)
            (##write-string " " out)
            (##write-string (##object->string (##decomp code) 48 #f) out)
            (##newline out))))

      (let ((parent (##proc-subproc-parent (##frame-ret f))))
        (let ((x (##write (##procedure-name parent) out #f)))
          (let ((y (##decompile (##frame-ret f))))
            (if (##not (##eq? y (##frame-ret f)))
              (begin
                (##display-spaces (##fixnum.- 25 x) out)
                (##write-string " " out)
                (##write-string (##object->string y 48 #f) out)))
            (##newline out)))))))

(define (##display-spaces n out)
  (if (##fixnum.< 0 n)
    (let ((m (if (##fixnum.< 40 n) 40 n)))
      (##write-substring "                                        " 0 m out)
      (##display-spaces (##fixnum.- n m) out))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##cmd-l f out)

  (define (display-locals frames rte)
    (let loop1 ((l frames) (r rte))
      (if (##pair? l)
        (let loop2 ((frame (##car l)) (values (##cdr (##vector->list r))))
          (if (##pair? frame)
            (let ((var (##car frame)))
              (if (##not (or (##eq? var (self-var))
                             (##eq? var (selector-var))
                             (##eq? var (do-loop-var))))
                (let ((x (##write var out #f)))
                  (##write-string " = " out)
                  (##write-string (##object->string (##car values)
                                                    (##fixnum.- 76 x)
                                                    (if-touches #t #f))
                                  out)
                  (##newline out)))
              (loop2 (##cdr frame) (##cdr values)))
            (loop1 (##cdr l) (rte-up r)))))))

  (if (##int-frame-subproblem? f)
    (display-locals (##extract-frames (##int-frame-subproblem-code f))
                    (##int-frame-subproblem-rte f))
    (begin
      (##write-string "Sorry, can't display compiled code environment" out)
      (##newline out))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##cmd-e f out)
  (if (##int-frame-subproblem? f)
    (##pretty-print (##decomp (##int-frame-subproblem-code f)) out 79)
    (let ((x (##decompile (##frame-ret f))))
      (if (##eq? x (##frame-ret f))
        (begin
          (##write-string "Sorry, this code was compiled without the DEBUG option" out)
          (##newline out))
        (##pretty-print x out 79)))))

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(define (##cmd-p f out)
  (if (##int-frame-subproblem? f)

    (let ((code (##int-frame-subproblem-code f))
          (rte (##int-frame-subproblem-rte f)))
      (let ((proc (##extract-proc code rte)))
        (if proc
          (begin
            (##write proc out #f)
            (##write-string " =" out)
            (##newline out)
            (##pretty-print (##decompile proc) out 79))
          (begin
            (##write-string "(top level)" out)
            (##newline out)))))

    (let ((proc (##proc-subproc-parent (##frame-ret f))))
      (##write proc out #f)
      (let ((x (##decompile proc)))
        (if (##eq? x proc)
          (##newline out)
          (begin
            (##write-string " =" out)
            (##newline out)
            (##pretty-print x out 79)))))))

;==============================================================================
