;;; -*- 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.
;;;
;;; Early binding database for ORBIT.  DEFINE and its cousins generate early
;;; binding information, usually referred to as support, for the variable
;;; defined.  Currently the only information generated, besides the fact that
;;; the variable have been defined, is an integrable value and then only if
;;; the defining special form is of the proper type.
;;; (MAKE-SUPPORT-TABLE id primop-table)
;;;============================================================================
;;;     A support table contains the support information for a given locale.
;;;  PRIMOP-TABLE is a table containing all of the primops defined in the
;;;  locale.
;;;
;;;  (
 ) => the support for  in the locale.  This
;;;      is settable.
;;;  (WALK-SUPPORT  ) => (  ) for each
;;;       in the table.
;;;  (PRIMOP-SUPPORT  ) => Returns a primop if there is one
;;;      named  in this table's primop table.
;(define (make-support-table id primop-table)
;  (let ((table (make-table id)))
;    (object (lambda (name)
;              (table-entry table name))
;      ((setter self)
;       (lambda (name value)
;         (set (table-entry table name) value)))
;      ((primop-support self name)
;       (primop-table name))
;      ((walk-support self proc)
;       (table-walk table proc))
;      ((walk-primop-table self proc)
;       (if primop-table
;           (walk-primop-table primop-table proc)))
;      ((support-table? self) t)
;      ((identification self) id)
;      ((print self stream)
;       (format stream "#{Support-table ~D ~S}" (object-hash self) id)))))
(defstruct env-table
  id
  table)
(zl:defsubst identification (env-table)
  (env-table-id env-table))
(defstruct (support-table (:include env-table)
			  (:constructor %make-support-table (id table primop-table))
			  (:print-function (lambda (struct stream depth)
					     depth
					     (format stream "#{Support-table ~D ~S}"
						     (support-table-id struct)
						     (object-hash struct)))))
  primop-table)
(defun make-support-table (id primop-table)
  (%make-support-table id (make-table id) primop-table))
(defun walk-support (support-table proc)
  (table-walk (support-table-table support-table) proc))
(zl:defsubst support-table-lookup (table name)
  (table-entry (support-table-table table) name))
(defun make-empty-support-table (id)
  (make-support-table id (make-primop-table id)))
;;; (MAKE-PRIMOP-TABLE id)
;;;============================================================================
;;;    The primops defined in a locale have a distinct name space.  This allows
;;; primops to be anonymous in the regular name space and still be referred to
 ;;; by name by the support tables and files.  Primop tables are essentially
;;; identical to support tables.
;(define (make-primop-table id)
;  (let ((table (make-table id)))
;    (object (lambda (name)
;              (table-entry table name))
;            ((setter self)
;             (lambda (name value)
;               (set (table-entry table name) value)))
;            ((walk-primop-table self proc)
;             (table-walk table proc))
;            ((print self stream)
;             (format stream "#{Primop-table ~D ~S}" (object-hash self) id)))))
(defstruct (primop-table (:include env-table)
			 (:constructor %make-primop-table (id table))
			 (:print-function (lambda (struct stream depth)
					    depth
					     (format stream "#{Primop-table ~D ~S}"
						     (primop-table-id struct)
						     (object-hash struct)))))
  )
(zl:defsubst primop-table-lookup (table name)
  (table-entry (primop-table-table table) name))
(defun make-primop-table (id)
  (%make-primop-table id (make-table id)))
;;; (MAKE-SUPPORT-ENV id support-tables)
;;;============================================================================
;;;  A support environment is made from a list of support tables.  It responds
;;; to information requests by trying each support table in turn.  The method
;;; for AUGMENT-CONTEXT is used by the HERALD special form.
;(define (make-support-env id support-tables)
;  (object (lambda (name)
;            (any (lambda (p)
;                   (p name))
;                 support-tables))
;   ((identification self) id)
;   ((primop-support self name)
;    (any (lambda (p)
;           (primop-support p name))
;         support-tables))
;   ((augment-context self . rest)
;    (get-support-environment id support-tables rest))
;   ((print self stream)
;    (format stream "#{Support-env ~D ~S}" (object-hash self) id))))
;;; identification should not properly be applied to these...
(defstruct (support-env (:constructor make-support-env (id support-tables))
			(:print-function (lambda (struct stream depth)
					   depth
					   (format stream "#{Support-env ~D ~S}"
						   (support-env-id struct)
						   (object-hash struct)))))
  id
  support-tables)
(defun argument-context (support-env &rest rest)
  (error "mumble blugh"))
(defun make-empty-support-env (id)
  (make-support-env id '()))
;;; (MAKE-FLAT-SUPPORT-ENV id support-tables)
;;;============================================================================ 
;;;   Makes an environment that uses one table to hold all support definitions.
;;; Used for permanent environments.
;(define (make-flat-support-env id support-tables)
;  (receive (support-table primop-table)
;           (combine-tables id support-tables)
;    (object (lambda (name)
;              (table-entry support-table name))
;      ((primop-support self name)
;       (table-entry primop-table name))
;      ((walk-support self proc)
;       (table-walk support-table proc))
;      ((walk-primop-table self proc)
;       (table-walk primop-table proc))
;      ((identification self) id)
;      ((augment-context self . rest)
;       (get-support-environment id `(,self) rest))
;      ((print self stream)
;       (format stream "#{Flat-support-env ~D ~S}" (object-hash self) id)))))
(defstruct (flat-support-env (:include support-table)
			     (:constructor %make-flat-support-env (id table primop-table))
			     (:print-function (lambda (struct stream depth)
						depth
						(format stream "#{Flat-support-env ~D ~S}"
							(support-env-id struct)
							(object-hash struct)))))
  )
(defun make-flat-support-env (id support-tables)
  (multiple-value-bind (support-table primop-table)
      (combine-tables id support-tables)
    (%make-flat-support-env id support-table primop-table)))
(defun combine-tables (id support-tables)
  (let ((support-table (make-table id))
        (primop-table (make-table id)))
    (mapc #'(lambda (table)
	      (walk-support table
			    #'(lambda (name support)
				(setf (table-entry support-table name)
				     support)))
	      (walk-primop-table table
				 #'(lambda (name primop)
				     (setf (table-entry primop-table name)
					   primop))))
          (reverse support-tables))
    (values support-table primop-table)))
(defun support-lookup (env-or-table name)
  (if (typep env-or-table 'support-env)
      (some #'(lambda (s) (support-lookup s name))
	    (support-env-support-tables env-or-table))	
    (support-table-lookup env-or-table name)))
; support-table
;      ((primop-support self name)
;       (primop-table name))
; flat-support-env
;      ((primop-support self name)
;       (table-entry primop-table name))
; support-env
;   ((primop-support self name)
;    (any (lambda (p)
;           (primop-support p name))
;         support-tables))
(defun primop-support (support-env-or-table name)
  (ecase (type-of support-env-or-table)
    (SUPPORT-TABLE
     (primop-table-lookup (support-table-primop-table support-env-or-table) name))
    (FLAT-SUPPORT-ENV
     (table-entry (flat-support-env-primop-table support-env-or-table) name))
    (SUPPORT-ENV
     (some #'(lambda (p)
	       (primop-table-lookup (support-table-primop-table p) name))
	(support-env-support-tables support-env-or-table)))))
;support-table
;      ((walk-primop-table self proc)
;       (if primop-table
;           (walk-primop-table primop-table proc)))
;primop-table
;      ((walk-primop-table self proc)
;       (table-walk table proc))
;flat-support-env
;      ((walk-primop-table self proc)
;       (table-walk primop-table proc))
(defun walk-primop-table (table proc)
  (ecase (type-of table)
    (SUPPORT-TABLE
     (if (support-table-primop-table table)
	 (table-walk (primop-table-table (support-table-primop-table table)) proc)))
    (PRIMOP-TABLE
     (table-walk (primop-table-table table) proc))
    (FLAT-SUPPORT-ENV
     (table-walk (flat-support-env-primop-table table) proc))))
;;; *PRIMITIVE-PRIMOP-TABLE*
;;; *PRIMITIVE-SUPPORT-TABLE*
;;; *PRIMITIVE-SUPPORT-ENV*
;;; *STANDARD-SUPPORT-ENV*
;;;===========================================================================
;;;   A starter set of tables and environments.
(defvar *primitive-primop-table*
  (make-primop-table '*primitive-primop-table*))
(defvar *primitive-support-table*
  (make-support-table '*primitive-support-table* *primitive-primop-table*))
(defvar *primitive-support-env*
  (make-support-env '*primitive-support-env* `(,*primitive-support-table*)))
(defvar *standard-support-env* (make-empty-support-env '*empty-env*)) ;lset
;;; SUPPORT
;;;============================================================================
;;;   Structure to hold support information for a symbol in a particular
;;; environment.
(defstruct (support (:constructor make-support (variable table data variant value type))
		    (:print-function (lambda (struct stream depth)
				       depth
				       (format stream "#{Support ~S}" (object-hash struct)))))
  variable  ; The variable being supported.
  table     ; The support table this support is in.
  variant   ; What kind of support this is, one of 'DEFINE etc.
  value     ; The value VARIABLE is bound to if VARIABLE is integrable.
  type      ; The type of the value VARIABLE is bound to.
  data      ; Not currently used.
  )
(defun make-support-entry (var table data variant value type)
  (let ((s (make-support var table data variant value type)))
    (setf (variable-support var) s)
    (setf (support-table-lookup table (variable-name var)) s)
    s))
;;; (CREATE-SUPPORT shape new-support support)
;;;============================================================================
;(define (create-support shape new-support support)
;  (table-walk (shape-table shape)
;              (lambda (key val)
;                (ignore key)
;                (if (or (cdr val)
;                        (variable-binder (car val)))
;                    (bug "lexical variable ~S still in shape ~S" val shape))))
;  (walk (lambda (var)
;          (find-var-support var
;                            (support (variable-name var))
;                            (new-support (variable-name var))
;                            new-support))
;        (shape-defined shape))
;  (shape-defined shape))
(defun get-variable-support (variable)
  (let ((support (variable-support variable)))
    (let ((value (and support
		      (eq 'constant (support-variant support))
		      (support-value support))))
      (if (and value (variable-p value))
	  (get-variable-support value)
	    support))))
;;; (SUPPORTS-DEFINITION ref)
;;;============================================================================
;;;  A predicate that determines if REF is the support value of some variable.
;;; Returns the support variant if it exists.
(defun supports-definition (ref)
  (and (call-arg? (node-role ref))
       (let ((proc (call-proc (node-parent ref))))
         (if (and (eq (call-arg 2) (node-role ref))
                  (primop-node? proc)
                  (primop.defines-support? (primop-value proc)))
             (primop.support-variant (primop-value proc))
             nil))))
;;; (FIX-MULTIPLE-DEFINITIONS var)
;;;============================================================================
;;;  Replace all more permanent definitions of VAR with DEFINE.
(defun fix-multiple-definitions (var)
  (orbit-warning "~S is multiply defined" (variable-name var))
  (dolist (ref (variable-refs var))
    (let ((variant (supports-definition ref)))
      (if (eq 'constant variant)
	  (setf (primop-value (call-proc (node-parent ref)))
	       primop/*define)))))
;;; 
(defun weaken-support (table)
  (walk-support table
                #'(lambda (name support)
		    (ignore name)
		    (cond ((eq 'constant (support-variant support))
			   (setf (support-value support) nil)
			   (setf (support-variant support) 'define)))))
  (values))