; .EnTete "Le-Lisp (c) version 15.2" " " "Les modules"
; .EnPied " " "%" " "
; .Chapitre 5 "Les Modules"
;
; .Centre "*****************************************************************"
; .Centre " Ce fichier est en lecture seule hors du projet ALE de l'INRIA.  "
; .Centre " Il est maintenu par ILOG SA, 2 Avenue Gallie'ni, 94250 Gentilly "
; .Centre " (c) Le-Lisp est une marque de'pose'e de l'INRIA                 "
; .Centre "*****************************************************************"

; .Centre "$Header: module.ll,v 4.9 89/01/11 20:40:19 nuyens Exp $"

(unless (>= (version) 15.2)
        (error 'load 'erricf 'module))

(defvar #:sys-package:colon 'module)

; .Section "Les variables globales"

;; Tous les modules charge's en me'moire sont pre'sents dans une
;; des 2 listes suivantes :

(unless (boundp ':compiled-list)
	(defvar :compiled-list ()))

(unless (boundp ':interpreted-list)
	(defvar :interpreted-list ()))

;; Les messages d'erreur.

(defvar :ERRNMD
        #- #:system:foreign-language "module inexistant"
	#+ #:system:foreign-language "module not found")
(defvar :ERRFPR
        #- #:system:foreign-language "fichier protege"
	#+ #:system:foreign-language "protected file")
(defvar :WARINM
        #- #:system:foreign-language "chargement du module interprete"
	#+ #:system:foreign-language "load interpreted module")


; .Section "Fonctions sur les de'finitions de module"

; Une <de'finition de module> est une A-liste de la forme :
;      ((key1 . val1) ... (keyN . valN) (:header . <liste de chai↑nes>))
; La clef interne :header contient une liste de chai↑nes correspondant
; au fichier de descripton de module jusqu'a` l'occurrence de la premie`re
; clef qui ne fait pas partie des clefs connus de l'utilisateur (cette liste
; est contenue dans la variable :list-of-user-key).
; Cette clef est remplace'e par :deadheader si les lignes repre'sentant
; la de'finition des clefs utilisateurs ne peuvent plus e↑tre utilise'es.
; Dans ce cas les lignes sont quand me↑me imprime'es, pre'ce'de'es d'un ;


;; Contient la liste des clefs connus de l'utilisateur.
(defvar :list-of-user-key '(defmodule files import export include))

;; Chai↑ne de se'paration des clefs utilisateur et des clefs syste`me.
;; Comme son nom l'indique, cette chai↑ne est ajoute'e automatiquement.
;; mais n'a qu'une valeur cosme'tique.
(defvar :EndOfHeader ";;; Added automatically, don't type beyond this line.")

;; Le stockage (durant la lecture du fichier de description des modules)
;; des lignes du fichier est re'alise' au moyen des IT programmables.
;; Il demande l'utilisation des 2 variables globales suivantes :

(defvar :header ())            ; contient la liste des lignes courantes.
(defvar :in-user-part ())      ; indicateur d'e'tat de l'automate.


; .SSection "Lecture d'une de'finition de module"

(defun readdefmodule (module-name)
   ; retourne une de'finition de module.
   (let ((module-file (probepathm module-name)))
        (ifn module-file
	     (error 'readdefmodule :ERRNMD module-name)
	     (with ((inchan (openi module-file)) )
		   (let ((defmod ())
			 (header ())
			 (keyheader ':header)
			 (#:sys-package:colon  #:sys-package:colon)
			 (#:sys-package:itsoft (cons 'module
						     #:sys-package:itsoft)))
		     ; pre'paration de l'automate
		     (setq :header () :in-user-part t)
		     ; lecture des clefs
		     (untilexit eof
			   ; lecture d'une clef
			   (newl defmod
				 (cons (let ((#:system:read-case-flag ()))
					    (read))
				       (let ((#:system:read-case-flag t))
					    (read))))
			   ; changement de :colon
			   (when (eq (caar defmod) 'defmodule)
				 (setq #:sys-package:colon (cdar defmod)))
			   (if (memq (caar defmod) :list-of-user-key)
			       (if :in-user-part
				   ; rajout des chai↑nes correspondant
				   ; a` la clef utilisateur
				   (setq header (append :header header)
					 :header ())
				   ; cas e'trange ou` des clefs
				   ; utilisateur apparaissent apre`s
				   ; les clefs syste`me 
				   (setq keyheader ':deadheader))
			       (setq :in-user-part ())))
		     ; fabrique la de'finition de module :
		     ; ((key1 . val1) .. (keyN . valN) (:header . strings))
		     (setq defmod (nreverse (acons keyheader
						   (nreverse header)
						   defmod)))
		     defmod)))))

(defun :bol ()
   (super-itsoft 'module 'bol ())
   (when :in-user-part
	 (newl :header (substring (inbuf) 0 (sub (inmax) 2)))))


; .SSection "Fonctions de manipulation des de'finitions de modules"

(defun getdefmodule (defmod key)
   (cassq key defmod) )

(defun setdefmodule (defmod key val)
   (let ((slot (assq key defmod)))
        (if (consp slot)
	    (progn (rplacd slot val) defmod)
	    (nconc1 defmod (cons key val)))))


; .SSection "Impression des de'finitions de modules"

(de printdefmodule (defmod mod)
    (let ((oldmod (readdefmodule mod))
	  (header (getdefmodule defmod ':header)))
      (unless (equal oldmod defmod)
	      ; ce n'est pas la me↑me description de module
	      ; sinon le fichier n'est pas me↑me touche'.
	      (let ((outchan (outchan))
		    (out (probepathm mod)))
		(ifn out
		     (error 'printdefmodule :ERRNMD mod)
		     (ifn (catcherror () (setq out (openo out)))
			  (error 'printdefmodule :ERRFPR out)
			  (outchan out)
			  (if (and header
				   (every (lambda (key)
					    (equal (getdefmodule defmod key)
						   (getdefmodule oldmod key)))
					  :list-of-user-key))
			      ; les clefs utilisateur n'ont pas change'
			      (with ((rmargin (1+ (slen (outbuf)))))
				    (let ((#:system:print-for-read ()))
				      (mapc 'print header)
				      (print)
				      (print :EndOfHeader)
				      (:print-rest-of-keys defmod
							   :list-of-user-key)))
			      ; les clefs utilisateur ont change'es
			      (when (getdefmodule defmod ':deadheader)
				    (let ((#:system:print-for-read ()))
				         (mapc (lambda (x) (print "; " x))
					       (getdefmodule defmod
							     ':deadheader))))
			      (:print-rest-of-keys defmod ()))))
		     (close (outchan))
		     (outchan outchan))))
    mod)

(defun :print-rest-of-keys (defmod except)
   (let ((#:system:print-for-read t))
        (mapc (lambda (slot)
		  (let ((key (car slot)))
		       (unless (or (memq key '(:header :deadheader))
				   (memq key except))
			       (print key)
			       (print (cdr slot)))))
	      defmod)))

; .Section "Chargement des modules"

(de loadmodule (name . flags)
   (let ( (loaded (cons () ())) )
      (:loadmodule-aux (concat name) loaded (car flags) (cadr flags))
      ; Traitement des modules charge's en compile'.
      (mapc
         (lambda (m)
            (setq :compiled-list (delq m :compiled-list))
            (setq :interpreted-list (delq m :interpreted-list))
            (newl :compiled-list m) )
         (car loaded) )
      ; Traitement des modules charge's en interpre'te'.
      (mapc
         (lambda (m)
            (setq :compiled-list (delq m :compiled-list))
            (setq :interpreted-list (delq m :interpreted-list))
            (newl :interpreted-list m) )
         (cdr loaded) )
      name ))

(defun :loadmodule-aux (module loaded clos? inter?)
   (let ( (def (readdefmodule module)) (file-obj? (probepatho module)) )
      ; On se place dans les fichiers charge's.
      (if (and file-obj? (not inter?))
         (rplaca loaded (cons module (car loaded)))
         (setq file-obj? ())
         (rplacd loaded (cons module (cdr loaded))) )
      (let ( (interp (cdr loaded)) )
         ; On charge si besoin tous les modules importe's.
         (mapc
            (lambda (m)
               (when (and (not (memq m (car loaded)))
                          (not (memq m (cdr loaded))) 
                          (or clos?
                              (not (memq m :compiled-list)) ))
                  (if (and (not clos?) (memq m :interpreted-list))
                     ; Un module importe' est de'ja` en interpre'te'.
                     (setq interp t)
                     ; Sinon on charge ce module.
                     (:loadmodule-aux m loaded clos? inter?) )))
            (getdefmodule def 'import) )
         ; Si le module est compile' on ve'rifie que les modules importe's
         ;  sont bien compile's et que le chargeur existe.
         (when (and file-obj?
                   (or (neq (cdr loaded) interp)
                       (not (featurep 'loader)) ))
            (setq file-obj? ())
            (rplaca loaded (delq module (car loaded)))
            (rplacd loaded (cons module (cdr loaded))) ))
      (if file-obj?
         ; Chargement module compile'.
         (let ( (deb (#:system:ccode)) )
            (protect
               (loadobjectfile module)
               (putprop module (cons deb (#:system:ccode)) ':limit) ))
         ; Chargement module interpre'te'.
         (print ";; " :WARINM " : " module)
         (mapc
            (lambda (f) (libloadfile f t))
            (getdefmodule def 'files) ))))

; .Section "De'finition des modules autoload"

(defun filegetdef (file symb)
   (let ((real-file (probepathf file)))
     (ifn real-file
	  (error 'filegetdef 'errfile file)
	  (let (us
		(def ()))
            (with ((inchan (openi real-file)))
		  (untilexit eof
		     (when (consp (setq us (read)))
			   (selectq (car us)
				    ((de defun df defmacro dm dmd)
				     (when (eq (cadr us) symb)
					   (close (inchan))
					   (exit eof (setq def us)) ))
				    (t ; Il faut traiter les synonym et ds.
				     )))))
            def ))))

(df autoloadmodule list-of-module
   (mapc (lambda (module)
	   (let ((defmod (readdefmodule module)))
	     (mapc (lambda (fnt)
		     (when (symbolp fnt)
			   (:makeautoload fnt module
					  (getdefmodule defmod 'files))))
		   (getdefmodule defmod 'export) )))
	 list-of-module))

(defun :makeautoload (fnt module files)
   (let ( (def ()) type )
      (while (and (null def) files)
         (setq def (filegetdef (nextl files) fnt)) )
      (ifn def
         (error 'autoload "can't find definition of" fnt)
         (selectq (car def)
            ((de defun)
               (setq def `(:args (:std-autoload ',fnt) (apply ',fnt :args))
                     type 'expr ))
            (df
               (setq def `(:args (:std-autoload ',fnt) (apply ',fnt :args))
                     type 'fexpr ))
            ((defmacro dmd)
               (setq def `(:args (:std-autoload ',fnt) (cons ',fnt :args))
                     type 'dmacro ))
            (dm
               (setq def `(:args (:std-autoload ',fnt) :args)
                     type 'macro ))
            (t (error 'autoload "Bad definition" def)) )
         (setfn fnt type def)
         (putprop fnt (or module files) 'autoload) )))

(defun :std-autoload (fnt)
   (let ( (of (valfn fnt)) (files (getprop fnt 'autoload)) rep )
      (if (consp files)
         (mapc (lambda (f) (libloadfile f t)) files)
         (loadmodule files) )
       (when (eq (valfn fnt) of)
          (error 'autoload "definition not found" (cons fnt files)) )))

(defun autoloadp (fnt)
   (getprop fnt 'autoload))