;;; -*- Mode:LISP; Package:COMPILER; Readtable:ZL; Base:10 -*-

#|

FOREIGN-SYMBOL.LISP

Provides a compile-time customization mechanism for solving problems
with references to symbols in undefined packages or unloaded systems.

The real-word application of this is for the cross-compiler, when Lambda
system files contain references to symbols in Falcon packages that are
not known to Lambda.  (Got that?)

For each "foreign symbol," a property list of values is associated with
the foreign symbol tag, which is generally a keyword.  For each target
computer, a "native value" for the foreign symbol can be associated with
a particular tag.  (These native values can be anything, but the
existing requirement is to obtain a different symbol as the foreign
symbol value.)

Here's an example of the application of FOREIGN-SYMBOL.  Instead of
doing the following and losing:

  (do-something-with
    (cond
      ((eq *target-computer* 'k)
        ;;array:art-q ;;WKF: 5/18/88
        #+lambda k-array:art-q #+falcon array:art-q
        'art-q)
      ((eq *target-computer* 'lambda)
        ;;etc.
        )))

...one would do:

1: Define the Lambda native symbol, e.g., in "SYS:SYS;TYPES":

   #+lambda
   (def-foreign-symbol :art-q 'ZL:ART-Q)

2: Define the Falcon native symbol elsewhere:

   (def-foreign-symbol :art-q
     #+lambda 'K-ARRAY:ART-Q
     #+falcon 'ARRAY:ART-Q)

3: Refer to the foreign symbol:

(defun foo () (print (foreign-symbol :art-q)))

Additional features:

1. FOREIGN-SYMBOL is SETF'able; this translates into DEF-FOREIGN-SYMBOL,
which (when compiled) should be used as a top-level form only.

2. The source file of a FOREIGN-SYMBOL is recorded and checked, like DEFUN's, etc.

3. FOREIGN-SYMBOL-VALUE is also provided to provide compile-time lookups
with a further level of indirection: the SYMBOL-VALUE of the native
symbol is returned.

4. INLINE-FOREIGN-SYMBOL and INLINE-FOREIGN-SYMBOL-VALUE are equivalent
to FOREIGN-SYMBOL and FOREIGN-SYMBOL-VALUE, respectively, but they get
optimized into the at compile-time values of .  This is for code that
doesn't need to get the foreign symbols dynamically, at run time.  I
don't know if anything in the system files will need this.

|#

(defvar *foreign-symbol-table* (make-hash-table))

;;;Utility look-up function

(defun lookup-foreign-symbol (foreign-symbol &optional error-p)
  (declare (eh:error-reporter))
  (flet ((error-string ()
	  (format nil "There is no declared foreign-symbol value for ~S" foreign-symbol)))
    (or (car-safe (getf (gethash foreign-symbol *foreign-symbol-table*) *target-computer*))
	(if error-p (error (error-string))
	  ;;If allowed, user can set value for undefined foreign-symbol
	  (let ((result (cerror t  nil nil (error-string))))
	    (def-foreign-symbol foreign-symbol result)
	    result)))))

(defun foreign-symbol (FOREIGN-SYMBOL &optional error-p)
  "Returns the version of FOREIGN-SYMBOL appropriate to the compilation target
  computer, according to the appropriate CLAUSE in CLAUSES.
ERROR-P non-NIL means don't allow user to handle undefined foreign symbol
  by specifying a new value."
  (lookup-foreign-symbol foreign-symbol error-p))

(defun def-foreign-symbol (foreign-symbol native-value)
  (declare (zwei:indentation 2 1))
  (setf (getf (gethash foreign-symbol *foreign-symbol-table*) *target-computer*)
	(ncons native-value))
  (si:record-source-file-name foreign-symbol 'foreign-symbol))

(defsetf foreign-symbol def-foreign-symbol)

(defun foreign-symbol-value (foreign-symbol &optional error-p)
  (symbol-value (lookup-foreign-symbol foreign-symbol error-p)))

(deff inline-foreign-symbol 'foreign-symbol)
(deff inline-foreign-symbol-value 'foreign-symbol-value)

(defoptimizer compile-time-foreign-symbol inline-foreign-symbol (form)
  `(quote ,(foreign-symbol (second form) (third form))))

(defoptimizer compile-time-foreign-symbol-value inline-foreign-symbol-value (form)
  (foreign-symbol-value (second form) (third form)))

#| Tests:

(def-foreign-symbol :art-q
  #+(target falcon) 'k-array:art-q #-(target falcon) 'zl:art-q)

(defun foo () (print (foreign-symbol :art-q)))

(defun foo2 () (print (foreign-symbol-value :art-q)))

(defun foo3 () (print `(quote ,(foreign-symbol :art-q))))

(defun ifoo () (print (inline-foreign-symbol :art-q)))

(defun ifoo2 () (print (inline-foreign-symbol-value :art-q)))

(defun ifoo3 () (print `(quote ,(inline-foreign-symbol :art-q))))

|#

