;;; -*- Mode:LISP; Package:NC; Base:10; Readtable:CL -*-

;;; Copyright (c) 1985 Yale University
;;;     Authors: N Adams, R Kelsey, D Kranz, J Philbin, J Rees.
;;; This material was developed by the T Project at the Yale University Computer 
;;; Science Department.  Permission to copy this software, to redistribute it, 
;;; and to use it for any purpose is granted, subject to the following restric-
;;; tions 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 T Project at Yale any improvements or extensions that they make,
;;;    so that these may be included in future releases; and (b) to inform
;;;    the T Project 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. Yale has made no warrantee or representation that the operation of
;;;    this software will be error-free, and Yale 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 Yale University nor of any
;;;    adaptation thereof in any advertising, promotional, or sales literature
;;;    without prior written consent from Yale in each case.
;;;

;;; Dealing with the top level of a module


(defun do-exps (exps support-env shape fshape)
  (let ((*variable-id* 0)
;	(*shape* (create-shape))
	(*value-table* (make-table '*value-table*))
	(*delayed-user-messages* '()))
    (let* (
;	   (shape *shape*)
;	   (fshape (create-shape))
           (exps (alphatize-module exps shape fshape))
           (env (create-env *new-support* shape)))
;      (format t "~&alphatized exps: ~a" exps)
      (let ((*support* support-env)
	    (*free-variables* (make-table '*free-variables*)))
        (multiple-value-bind (exps uses)
	    (do-integrable-exps exps shape)
          (let ((uses uses)
		(res '()))
	    (dolist (exp exps)
	      (cond ((node-p exp)
		     (push exp res))
		    (t (multiple-value-bind (node u)
                           (transmogrify-exp exp shape uses)
			 (setq uses u)
			 (push node res)))))
	    (values (map! #'fixup-node-tree (nreverse res))
		    env)))))))


(defun alphatize-module (exps shape fshape)
  (let ((exps (alpha-list exps shape fshape)))
     (do ((exps exps)
	  (res '()))
	 ((null exps) (nreverse res))
       (cond ((atom (car exps))
              (pop exps))
	     ((eq (caar exps) syntax/block)
	      (setq exps (append (cadar exps) (cdr exps))))
	     (t
	      (push (car exps) res)
	      (pop exps))))))

(defun create-env (env shape)
  (let ((vars '()))				;what's this for?
    (dolist (def (shape-defined shape))
      (let ((name (car def)))
	(when name
	  (let ((variant (cdr def))
		(support (support-table-lookup env name)))
	    (if support
		(check-multiple-defs support variant)
	      (let ((var (create-variable name)))
		(make-support-entry var env '() variant nil nil)
		(push var vars)))))))
    env))

(defun check-multiple-defs (support variant)
  (cond ((and (eq variant 'set)
              (or (eq (support-variant support) 'lset)
                  (eq (support-variant support) 'set))))
        ((and (eq variant 'lset)
              (eq (support-variant support) 'set))
         (setf (support-variant support) 'lset))
        ((not (eq 'multiple (support-variant support)))
         (orbit-warning "~S is multiply defined"
                        (variable-name (support-variable support)))
         (setf (support-variant support) 'multiple)))
  (undefined))

(defun do-integrable-exps (exps shape)
  (let ((count 0)
	(ints '())
	(plain '()))
    (dolist (exp exps)
      (cond ((integrable-exp? exp)
	     (push (transmogrify-integrable-exp exp shape count) ints)
	     (incf count))
	    (t
	     (incf count) 
	     (push (cons count exp) plain))))
    (multiple-value-bind (ints uses)
	(transmogrify-ints (sort-int-exps (nreverse ints))) 
      (values (map! #'cdr (sort (nconc ints plain)
				#'< :key #'car))
	      uses))))

(defun transmogrify-ints (ints)
  (let ((uses '()))
    (dolist (int ints)
      (multiple-value-bind (node u)
	  (transmogrify-node (cdr int) uses)
	node
	(setq uses u)))
    (values ints uses)))

(defvar defining-syntax
  (list)) ; syntax/define-variable-value syntax/define-constant))

(defun integrable-exp? (exp)
  (and (consp exp)
       (member (car exp) defining-syntax :test #'eq)
       (need-value? (cadr exp))))

(defun need-value? (name)
  (let ((support (if (variable-p name) 
                     (variable-support name)
                     (funcall *new-support* name))))
    (and support
         (eq 'constant (support-variant support)))))

(defstruct int-def
  node
  index
  def
  uses)

(defun transmogrify-integrable-exp (exp shape index)
  (let ((node (really-transmogrify-exp exp shape))
        (new (make-int-def)))
    (setf (int-def-node new) node)
    (setf (int-def-index new) index)
    (cond ((simple-thunk? node)
           (multiple-value-bind (def uses)
	       (quick-def-and-use-analyze node)
             (setf (int-def-def  new) def)
             (setf (int-def-uses new) uses)))
           (t
             (setf (int-def-def  new) nil)
             (setf (int-def-uses new) '())))
    new))

(defun sort-int-exps (ints)
  (let* ((int-table (make-table 'int-table)))
    (mapc #'(lambda (int)
	      (setf (table-entry int-table (int-def-def int)) int))
          ints)
    (do ((ints ints)
	 (res '()))
	((null ints)
	 (map! #'(lambda (int-def)
		   (cons (int-def-index int-def)
			 (int-def-node int-def)))
	       (nreverse res)))
      (multiple-value-bind (ready unready)
	  (really-sort-int-exps ints int-table)
	(mapc #'(lambda (int)
		  (setf (table-entry int-table (int-def-def int))
			nil))
	      ready)
	(setq ints unready res (nconc ready res))))))

(defun really-sort-int-exps (ints table)
  (let ((unready '())
	(ready '()))
    (dolist (int ints)
      (if (ready? int table)
	  (push int ready)
	(push int unready)))
    (if ready
	(values ready unready)
      (error "integration loop ~S"
	     (mapcar #'(lambda (int) (variable-name (int-def-def int)))
		     unready)))))

(defun ready? (int table)
  (null (setf (int-def-uses int)
	      (delete-if-not #'(lambda (use)
				 (table-entry table use))
			     (int-def-uses int)))))

(defun transmogrify-exp (exp shape uses)
    (debug :alpha
      (format t "~&Alphatized:~%")
      (pprint exp))
    (transmogrify-node (really-transmogrify-exp exp shape) uses))

;(defun really-transmogrify-exp (exp shape)
;  (let* ((exp `(,syntax/lambda t (nil ,(create-variable 'c))	;t?
;		    (,exp (,syntax/quote t))))
;         (node (->value-node exp)))
;;    (return-tree-to-freelist exp)
;    (dereference-lexical-vars shape)
;    (print-all-delayed-user-messages)
;    node))

(defun really-transmogrify-exp (exp shape)
  (prog1
    (->value-node exp)
;   (return-tree-to-freelist exp)
    (dereference-lexical-vars shape)
    (print-all-delayed-user-messages)))



(defun transmogrify-node (node uses)
   (debug :unsimplified
     (format t "~&Unsimplified node:~%")
     (pp-cps node))
  (simplify-call node)
  (debug :simplified
    (format t "~&Simplified tree:~%")
    (pp-cps node))
  (multiple-value-bind (defs new-uses)
      (def-and-use-analyze node)
;    (format t "~&defs: ~a" defs)
;    (format t "~&uses: ~a" defs)	     
    (let ((forward-uses (check-uses defs new-uses uses)))
      (mapc #'(lambda (var)
		(noise "~S~%" (variable-name var)))
            defs)
      (values node forward-uses))))

;;; this was only called by 
;;; replace-call-with-new-primop <- simplify-*primop in front;simplifiers
;(defun subexpression->code-tree (exp)
;  (let ((exp (alpha exp *syntax* *shape*)))
;    (if *front-debug*
;        (pprint exp *terminal-io*))
;    (let* ((exp `(,syntax/lambda t (nil ,(create-variable 'c)) (,exp)))
;           (node (->value-node exp)))
;;      (return-tree-to-freelist exp)
;      (dereference-lexical-vars *shape*)
;      (print-all-delayed-user-messages)
;      (if *front-debug*
;          (pp-cps node *terminal-io*))
;      node)))


;;; hacked version to not do assignment conversion
;;; since we are keeping original vars later things
;;; which use variable-flag must find it NIL
;;; (ie substitute-vars-in-node-tree) so we munge it
;;; here (all this stuff using 'lexical shape-lexical add-definition etc
;;; is now disabled ??? !)
(defun dereference-lexical-vars (shape)
  (dolist (v (shape-lexical shape))
    (setf (variable-flag v) nil)))

;;; assignment conversion here
;(defun dereference-lexical-vars (shape)
;  (setf (shape-lexical shape)
;	(remove-if-not #'(lambda (var)
;			   (cond ((variable-binder var)
;				  (introduce-cell var)
;				  nil)
;				 (t)))
;               (shape-lexical shape))))

(defun rebuild (nodes)
  (do ((first (car nodes) (attach first (car nodes)))
       (nodes (cdr nodes) (cdr nodes)))
      ((null nodes)
       (setf (node-parent first) empty)
       (let ((top (value-node->thunk first)))
         (setf (node-parent top) nil)
         top))))

(defun value-node->thunk (node)
  (let* ((c-var (create-variable 'k))
         (new-l (create-lambda-node 'b (list nil c-var)))
         (call (create-call-node 2 0)))
    (relate call-proc call (create-reference-node c-var))
    (relate (call-arg 1) call node)
    (relate lambda-body new-l call)
    new-l))

(defun attach (first second)
  (let ((new-l (create-lambda-node 'b (list (create-variable 'ignore))))
        (c-var (car (lambda-variables first))))
    (relate lambda-body new-l (detach (lambda-body second)))
    (case (length (variable-refs c-var))
      (0
       (bug "top level lambda ~S doesn't use its continuation" first))
      (1
       (let ((ref (car (variable-refs c-var))))
         (cond ((eq (node-role ref) call-proc)
                (replace-node (node-parent ref) (detach (lambda-body new-l)))
                (erase-all new-l))
               (t
                (replace-node ref new-l)))
         (relate lambda-body second (detach (lambda-body first)))
         (erase first)))
      (2
       (relate lambda-body
               second
               (make-call-with-exits 1 (list first new-l)))))
    second))

#|
(defun obtain-free-variable (name)
  (let ((v (funcall *base-support-env* name)))
    (cond (v
	   (support-variable v))
	  ((table-entry *free-variables* name))
	  (t 
	   (let ((var (create-variable name)))
	     (setf (table-entry *free-variables* name) var)
	     var)))))


(defun free-refs (name var)
  (format t "~&~S: ~S~%"
          name
          (let ((l
                 (mapcar #'(lambda (ref)
			     (let ((loc (containing-definition (node-parent ref))))
			       (if loc (variable-name loc) 'top-level)))
                      (variable-refs var))))
            (if (member 'top-level l :test #'eq)
                (cons 'top-level (delete 'top-level l :test #'eq))
                l))))



|#
