; .EnTete "Le-Lisp (c) version 15.2" " " "Les appels externes"
; .EnPied "callext.ll" "I-%" " "
; .Annexe I "Les Appels Externes"
; .nr % 1
;
; .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 "*****************************************************************"

; $Header: callext.ll,v 4.9 89/01/11 14:28:19 kuczynsk Exp $

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

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

(add-feature 'callext)

(de #:system:cached-getglobal (s)
    (if (null #:system:defextern-cache)
        (getglobal s)
      (let ((res (cons 0 0)))
        (newl #:system:getglobal-cache
              (cons s res))
        res)))


; Toutes ces fonctions sont "autoload" dans le syste`me minimum.

(dmd defextern (nom ltype . type)
     (buildextern nom
		  (#:system:cached-getglobal nom)
		  ltype
		  (or (car type) 'fix)))

(de buildextern (nom adr ltype type)
     ; de'fini une proce'dure externe
     (let* (;; compteur d'arguments
	    (n -1)
	    ;; liste des parametres
            (lvar (mapcar (lambda (l)
                             (symbol ':callext
				     (concat "arg" (incr n))))
			  ltype))
	    ;; le corps de la fct de'finie par defextern
            (body `(callextern   ; appel externe
                      (precompile ,(if (numberp adr) adr `',adr)
				  ()
				  ()
				  (eval
				   (kwote (#:system:cached-getglobal ',nom))) )
		      ; numero du type de la valeur rendue
                      ,(:conv-extern-to-ll type)
		      ; plist (parametre1 numero-type1 ...)
                      ,@(mapcan (lambda (type var)
				  (if (eq type 'external)
                                      `((vag ,var) ,(:conv-ll-to-extern type))
				    `(,var ,(:conv-ll-to-extern type))))
                                ltype lvar))
		  ))
       (when (eq type 'external)
	     (setq body `(loc ,body)))
       (if (and (numberp adr) (zerop adr))
	   (error 'defextern 'errudf nom)
	 `(de ,nom ,lvar ,body))) )

(de :conv-ll-to-extern (type)
    ;; Tous ces nume'ros sont e'galement de'crits et utilise's
    ;;  dans llxxx.llm3 spe'cifique a` la machine utilise'e.
    (selectq type
             (external 0)
             (fix 1)
             (float 2)
             (string 3)
             (vector 4)
             (rfix 5)                 ; FIX par reference (FORTRAN)
             (rfloat 6)               ; FLOAT par reference (FORTRAN)
	     (fixvector 7)            ; vecteur d'entiers
	     (floatvector             ; vecteur de flottants.
	      (if (eq 0.0 0.0)        ;  On ne sait pas faire completement
		  8                   ;   les vecteurs de flottants
		(error 'defextern     ;   en 64bitfloats.
		       'errgen
		       "floatvector (64BITFLOATS)")))
             ((t) 0)                  ; T arre↑te les clauses!!
             (t (error 'defextern 'erroob type))))

(de :conv-extern-to-ll (type)
    ;; Tous ces nume'ros sont e'galement de'crits et utilise's
    ;;  dans llxxx.llm3 spe'cifique a` la machine utilise'e.
    ;; On ne sait pas ramener un tableau: comment connaitre sa longueur?
    (selectq type
	     (external 0)
	     (fix 1)
	     (float 2)
	     (string 3)
	     ((t) 0)
	     (t (error 'defextern 'errgen type))))

(unless (boundp '#:system:getglobal-cache)
        (defvar #:system:getglobal-cache ()))

(unless (boundp '#:system:defextern-cache)
        (defvar #:system:defextern-cache ()))

(de defextern-cache &nobind
    (selectq (arg)
             (0 #:system:defextern-cache)
             (1
              (when (and (null (arg 0))
                         #:system:defextern-cache)
                    (getglobal-flush-cache))
              (setq #:system:defextern-cache (arg 0)))
             (t (error 'defextern-cache 'errwna 1))))

(de getglobal-flush-cache ()
    (when #:system:getglobal-cache
          (let ((name-list
                 (mapcar (lambda (pair) (string (car pair)))
                         #:system:getglobal-cache))
                (errors ()))
            (←mgetglo name-list ())
            (mapc (lambda (pair value)
                    (if (eq 0 value)
                        (newl errors (car pair))
                      (setq value (loc value))
                      (rplac (cdr pair)
                             (if (fixp value) 0 (car value))
                             (if (fixp value) value (cdr value)))))
                  #:system:getglobal-cache
                  name-list)
            (setq #:system:getglobal-cache ())
            (when errors
                  (error 'defextern 'errudf (nreverse errors))))))

(de cload (s)
    (if #:system:cloadp
        (:ccode (←cload (string s) (:ccode) (:ecode)))
        (error 'cload
               #+ #:system:foreign-language "not implemented in the system"
               #- #:system:foreign-language "non implemente dans le systeme"
               (system))))

(defextern ←mgetglo (t t))

#+ #:system:cloadp
(defextern ←cload (string external external) external)

; Les tests de lelisp.c :

#|     en enlever en cas de test

(defextern-autoflush ())
   (defextern ←cchdir (string) fix)
   (defextern ←chome () string)
   (defextern ←cmoinsun () fix)
   (defextern ←ctest (string float fix vector) float)
(defextern-autoflush t)
   (setq vect #[a b])
   (←ctest "FooBar" 123.45 123 vect)


|#