; Copyright (c) 1994 by Richard Kelsey.  See file COPYING.

; This file has the Pre-Scheme compiler's code for dealing with the
; Scheme 48's module system.

; FILES is a list of files that contain structure definitions, including
; a definition for NAME.  The files are loaded into a config package
; containing:
;   - the procedures and macros for defining structures and interfaces
;   - a Pre-Scheme structure (called PRESCHEME)
;   - a ps-memory structure
;   - a ps-receive structure
;   - the STRUCTURE-REFS structure
; We then return:
;   1. a list of the packages required to implement the named structures
;   2. a list of the names exported by the named structures
;   3. a procedure that for looking up names defined in packages in the
;      config package (this is used to map user directives to their targets)

(define (package-specs->packages+exports struct-names files)
  (let ((config (make-very-simple-package 'config (list defpackage)))
	(old-config ((structure-ref package-commands-internal config-package))))
    (environment-define! config 'prescheme prescheme)
    (environment-define! config 'ps-memory ps-memory)
    (environment-define! config 'ps-receive ps-receive)
    (environment-define! config 'ps-record-types ps-record-types)
    (environment-define! config 'structure-refs structure-refs)
    (environment-define! config ':syntax (structure-ref meta-types syntax-type))
    (set-reflective-tower-maker! config (get-reflective-tower-maker old-config))
    (let-fluids (structure-ref packages-internal $get-location)
		get-variable
		(structure-ref reading-forms $note-file-package)
		(lambda (filename package) (values))
      (lambda () 
	(for-each (lambda (file)
		    (load file config))
		  files)))
    (values (collect-packages (map (lambda (name)
				     (environment-ref config name))
				   struct-names)
			      (lambda (package)
				#t))
	    (let ((names '()))
	      (for-each (lambda (struct-name)
			  (let ((my-names '()))
			    (for-each-declaration
			     (lambda (name type)
			       (set! my-names (cons name my-names)))
			     (structure-interface (environment-ref config struct-name)))
			    (set! names (cons (cons struct-name my-names) names))))
			struct-names)
	      names)
	    (make-lookup config))))

; This creates new variables as needed for packages.

(define (get-variable package name)
  ;(format #t "Making variable ~S for ~S~%" name package)
  ((structure-ref variable make-global-variable)
     name
     (structure-ref ps-types type/unknown)))

; Return something that will find the binding of ID in the package belonging
; to the structure PACKAGE-ID in the CONFIG package.

(define (make-lookup config)
  (lambda (package-id id)
    (let ((binding (package-lookup config package-id)))
      (if (and (binding? binding)
	       (location? (binding-place binding))
	       (structure? (contents (binding-place binding))))
	  (let* ((package (structure-package
			   (contents (binding-place binding))))
		 (binding (package-lookup package id)))
	    (if (binding? binding)
		(binding-place binding)
		#f))
	  #f))))
	       
;----------------------------------------------------------------
; Handy packages and package making stuff.

(define defpackage     (structure-ref built-in-structures defpackage))
(define structure-refs (structure-ref built-in-structures structure-refs))
(define scheme         (structure-ref built-in-structures scheme))

(define (make-env-for-syntax-promise . structures)
  (make-reflective-tower eval structures 'prescheme-linking))

(define (make-very-simple-package name opens)
  (make-simple-package opens
		       eval
		       (make-env-for-syntax-promise scheme)
		       name))

(define (get-reflective-tower-maker p)
  (environment-ref p (string->symbol ".make-reflective-tower.")))

;----------------------------------------------------------------
; The following stuff is used to define the DEFINE-RECORD-TYPE macro.
; We produce a structure that exports EXPAND-DEFINE-RECORD-TYPE.  The
; base package then includes that structure in its FOR-SYNTAX package.

(define defrecord-for-syntax-package
  (make-very-simple-package 'defrecord-for-syntax-package '()))

(define defrecord-for-syntax-structure
  (make-structure defrecord-for-syntax-package
		  (lambda () (export expand-define-record-type))
		  'defrecord-for-syntax-structure))

(define (define-for-syntax-value id value)
  (let ((loc (make-new-location defrecord-for-syntax-package id)))
    (set-contents! loc value)
    (package-define! defrecord-for-syntax-package
		     id
		     (structure-ref meta-types usual-variable-type)
		     loc
		     #f)))

(define-for-syntax-value 'expand-define-record-type expand-define-record-type)

;----------------------------------------------------------------
; BASE-PACKAGE contains all of the primitives, syntax, etc. for Pre-Scheme

(define (prescheme-unbound package name)
  (bug "~S has no binding in package ~S" name package))

(define base-package
;  (let-fluid (structure-ref packages-internal $get-location) prescheme-unbound
;    (lambda () ))
  (make-simple-package '()
		       eval
		       (make-env-for-syntax-promise
			  scheme
			  defrecord-for-syntax-structure)
		       'base-package))

; Add the operators.

(let ((syntax-type (structure-ref meta-types syntax-type)))
  (for-each (lambda (id)
	      (package-define! base-package
			       id
			       syntax-type
			       #f
			       (get-operator id syntax-type)))
	    '(if begin lambda letrec quote set!
		 define define-syntax let-syntax
		 goto real-external)))  ; special for Prescheme

; Add the usual macros.

(let ((syntax-type (structure-ref meta-types syntax-type)))
  (for-each (lambda (name)
	      (package-define! base-package
			       name
			       syntax-type
			       #f
			       (make-transform
			         (usual-transform name)
				 base-package
				 (structure-ref meta-types syntax-type)
				 `(usual-transform ',name)
				 name)))
	    '(and cond do let let* or quasiquote syntax-rules))) ; delay

; Plus whatever primitives are wanted.

(define (define-prescheme! name location static)
  (package-define! base-package
		   name
		   (structure-ref meta-types usual-variable-type)
		   location
		   static))

; Copy over the enumeration macros and the ERRORS enumeration.

(define (import-syntax! package-id name)
  (let ((config ((structure-ref package-commands-internal config-package)))
	(syntax-type (structure-ref meta-types syntax-type)))
    (let ((binding (structure-lookup (environment-ref config package-id)
				     name
				     #t)))
      (package-define! base-package
		       name
		       syntax-type
		       (binding-place binding)
		       (binding-static binding)))))

(import-syntax! 'enumerated 'define-enumeration)
(import-syntax! 'enumerated 'enum)
(import-syntax! 'enumerated 'name->enumerand)
(import-syntax! 'enumerated 'enumerand->name)
(import-syntax! 'prescheme  'errors)
(import-syntax! 'prescheme  'define-external-enumeration)

; define still more syntax

(load "prescheme/ps-syntax.scm" base-package)

(eval '(define-syntax define-record-type expand-define-record-type)
      base-package)

;----------------------------------------------------------------
; Make the Pre-Scheme structure and related structures

(define (get-interface name)
  (environment-ref ((structure-ref package-commands-internal config-package))
		   name))

(define prescheme
  (make-structure base-package
		  (lambda () (get-interface 'prescheme-interface))
		  'prescheme))

(define ps-memory
  (make-structure base-package
		  (lambda () (get-interface 'ps-memory-interface)) 
		  'ps-memory))

(define ps-receive
  (make-structure base-package
		  (lambda () (get-interface 'ps-receive-interface)) 
		  'ps-receive))

(define ps-record-types
  (make-structure base-package
                  (lambda () (export (define-record-type :syntax)))
                  'ps-record-types))

; and a handy environment

(define prescheme-compiler-env
  (package->environment base-package))