; Copyright (c) 1993-1999 by Richard Kelsey.  See file COPYING.

; This file is obsolete and no longer used.

;----------------------------------------------------------------------------
;                           SPECIAL FORMS
;
;              QUOTE CALL RETURN BLOCK LAMBDA LETREC
;               + LET for reasons of type-checking
;
;----------------------------------------------------------------------------

(define-record-type quote-exp :quote-exp
  (make-quote-exp value type)
  quote-exp?
  (value quote-exp-value)
  (type quote-exp-type set-quote-exp-type!))

(define-record-type call-exp :call-exp
  (make-call-exp! proc exits type args source)
  call-exp?
  (proc call-exp-proc)
  (exits call-exp-exits)
  (type call-exp-type set-call-exp-type!)
  (args call-exp-args)
  (source call-exp-source))

(define-record-type let-exp :let-exp
  (make-let-exp vars vals body source)
  let-exp?
  (vars let-exp-vars)
  (vals let-exp-vals)
  (body let-exp-body set-let-exp-body!)
  (source let-exp-source))

(define-record-type return-exp :return-exp
  (make-return-exp protocol type args)
  return-exp?
  (protocol return-exp-protocol)
  (type return-exp-type)
  (args return-exp-args))

(define-record-type block-exp :block-exp
  (make-block-exp exps)
  block-exp?
  (exps block-exp-exps))

(define-record-type lambda-exp :lambda-exp
  (make-lambda-exp id return-type protocol vars body source)
  lambda-exp?
  (id lambda-exp-id)
  (return-type lambda-exp-return-type set-lambda-exp-return-type!)
  (protocol lambda-exp-protocol)
  (vars lambda-exp-vars)
  (body lambda-exp-body set-lambda-exp-body!)
  (source lambda-exp-source))

(define (make-continuation-exp vars body)
  (make-lambda-exp #f #f #f vars body #f))

(define-record-type letrec-exp :letrec-exp
  (make-letrec-exp vars vals body source)
  letrec-exp?
  (vars letrec-exp-vars)
  (vals letrec-exp-vals)
  (body letrec-exp-body set-letrec-exp-body!)
  (source letrec-exp-source))

(define-record-type external-value :external-value
  (make-external-value type)
  external-value?
  (type external-value-type set-external-value-type!))

; Creating nodes and CPS converting calls and blocks.
;-------------------------------------------------------------------------------
; (CPS expression) => value + first-call + last-lambda
;  = the value of the expression
;    + the first of any calls that must be executed to get the value
;    + the continuation lambda of the last of the necessary calls
; The first call and the last lambda will be #F if the value is trivial.
;
; (TAIL-CPS expression continuation-variable) => call
;  = the first call to execute to return the value of the expression to
;    the continuation variable

(define (cps exp)
  (let ((value (cps-value exp)))
    (if value
	(values value #f #f)
	(generic-cps exp #f))))

(define (tail-cps exp cont-var)
  (receive (value type)
      (cps-value+type exp)
    (if value
	(make-value-return cont-var value type)
	(generic-cps exp cont-var))))

(define (cps-value exp)
  (receive (value type)
      (cps-value+type exp)
    value))

(define (cps-value+type exp)
  (cond ((variable? exp)
	 (values (make-reference-node exp) (variable-type exp)))
	((quote-exp? exp)
	 (values (make-literal-node (quote-exp-value exp)
				    (quote-exp-type exp))
		 (quote-exp-type exp)))
	((lambda-exp? exp)
	 (let ((node (lambda-exp->node exp)))
	   (values node (lambda-node-type node))))
	(else
	 (values #f #f))))

(define (generic-cps exp cont-var)
  (cond ((block-exp? exp)
	 (make-block (block-exp-exps exp) cont-var))
	((return-exp? exp)
	 (make-return-call exp cont-var))
	((call-exp? exp)
	 (make-primop-call exp cont-var))
	((let-exp? exp)
	 (make-lambda-call exp cont-var))
	((letrec-exp? exp)
	 (letrec-exp->node exp cont-var))
        (else
         (bug "unknown syntax~% ~S" exp))))

(define (lambda-exp->node exp)
  (let* ((cvar (make-variable 'c (lambda-exp-return-type exp)))
         (node (make-lambda-node (lambda-exp-id exp)
				 'proc
				 (cons cvar (copy-list (lambda-exp-vars exp))))))
    (set-lambda-protocol! node (lambda-exp-protocol exp))
    (set-lambda-source! node (lambda-exp-source exp))
    (attach-body node (tail-cps (lambda-exp-body exp) cvar))
    node))

(define (letrec-exp->node exp cont-var)
  (let ((vals (map cps-value (letrec-exp-vals exp)))
	(vars (letrec-exp-vars exp))
	(cont (make-lambda-node 'c 'cont '())))
    (let-nodes ((top (letrec1 1 l1))
		(l1 ((x #f) . vars) call2)
		(call2 (letrec2 1 cont (* x) . vals)))
      (set-call-source! top (letrec-exp-source exp))
      (happens-after top cont (letrec-exp-body exp) cont-var))))

; (CATCH id . body)
; (THROW primop rep id . args)

(define (make-undefined-value)
  (make-quote-exp the-undefined-value #f))

(define (exp->s-exp exp)
  (cond ((variable? exp)
	 (format #f "~S_~S" (variable-name exp) (variable-id exp)))
	((quote-exp? exp)
	 (list 'quote (quote-exp-value exp)))
	((block-exp? exp)
	 (cons 'begin (map exp->s-exp (block-exp-exps exp))))
	((return-exp? exp)
	 (cons 'return (map exp->s-exp (return-exp-args exp))))
	((call-exp? exp)
	 `(,(primop-id (call-exp-proc exp))
	   ,(call-exp-exits exp)
	   . ,(map exp->s-exp (call-exp-args exp))))
	((let-exp? exp)
	 `(let ,(map list
		     (map exp->s-exp (let-exp-vars exp))
		     (map exp->s-exp (let-exp-vals exp)))
	    ,(exp->s-exp (let-exp-body exp))))
	((lambda-exp? exp)
	 `(lambda ,(map exp->s-exp (lambda-exp-vars exp))
	    ,(exp->s-exp (lambda-exp-body exp))))
	((letrec-exp? exp)
	 `(letrec ,(map list
			(map exp->s-exp (letrec-exp-vars exp))
			(map exp->s-exp (letrec-exp-vals exp)))
	    ,(exp->s-exp (letrec-exp-body exp))))
        (else
         (error '"unknown syntax~% ~S" exp))))

(define (exp-source exp)
  (cond ((call-exp? exp)
	 (call-exp-source exp))
	((let-exp? exp)
	 (let-exp-source exp))
	((letrec-exp? exp)
	 (letrec-exp-source exp))
	((lambda-exp? exp)
	 (lambda-exp-source exp))
	(else
	 #f)))

(define (find-some-source top-exp exp)
  (or (exp-source exp)
      (call-with-current-continuation
	(lambda (exit)
	  (let recur ((at top-exp))
	    (let ((hit? (cond ((eq? at exp)
			       #t)
			      ((call-exp? at)
			       (or (recur (call-exp-proc at))
				   (any recur (call-exp-args at))))
			      ((let-exp? at)
			       (or (recur (let-exp-body at))
				   (any recur (let-exp-vals at))))
			      ((letrec-exp? at)
			       (or (recur (letrec-exp-body at))
				   (any recur (letrec-exp-vals at))))
			      ((return-exp? at)
			       (any recur (return-exp-args at)))
			      ((lambda-exp? at)
			       (recur (lambda-exp-body at)))
			      ((block-exp? at)
			       (any recur (block-exp-exps at)))
			      (else #f))))
	      (if (and hit? (exp-source at))
		  (exit (exp-source at)))
	      hit?))))))