;;; -*- Mode:LISP; Package:SYMBOL; Readtable:CL; Base:10 -*-



(export '(boundp
	  fboundp
	  fmakunbound
	  get
	  get-properties
	  getf
	  make-symbol
	  makunbound
	  remf
	  remprop
	  set
	  symbol-function
	  symbol-name
	  symbol-package
	  symbol-plist
	  symbol-value
 ))
	  
	  
(defconstant *symbol-size* 5)

(defconstant *symbol-pname*    0)
(defconstant *symbol-value*    1)
(defconstant *symbol-function* 2)
(defconstant *symbol-package*  3)
(defconstant *symbol-plist*    4)

;;; Isn't Common LISP wonderful, NIL is a symbol and a LIST

;; T if object is DTP-SYMBOL
(defsubst %symbol? (object)
  (hw:field= gr:*T*
	     object vinc::%%data-type))

;; T if object is DTP-SYMBOL or DTP-NIL
(defsubst symbol? (object)
  (hw:32logbitp (hw:ldb object vinc::%%data-type 0)
		(hw:unboxed-constant
		  #.(lisp:logior (lisp:ash 1 $$dtp-nil)
				 (lisp:ash 1 $$dtp-symbol)))))

;;; Print Name

;;; 1.  Pnames are not allowed to be altered, just read.
;;; 2.  PNAME of NIL is funny because of how we make CAR and CDR
;;;     of NIL fast.

(defun %symbol-pname (symbol)
  ;; Won't work on NIL.
  (contents-offset symbol *symbol-pname*))

(defun symbol-name (symbol)
  (cond ((%symbol? symbol) (make-pointer $$dtp-array (%symbol-pname symbol)))
	((null symbol)     "NIL")
	(t (li:error "Wasn't a symbol to symbol name." symbol))))

;;; Value Cell

;;; 1. SYMBOL-VALUE is the real symbol value
;;; 2. %SET does not check to see if the symbol is constant.
;;;    SET probably should
;;; 3. Therefore, we do not defsetf symbol-value to %set
;;; 4. Same thing goes for %make-unbound
;;; 5. Implement SETQ as a call to SET.

(defun %symbol-value (symbol)
  (contents-offset symbol *symbol-value*))

(defun symbol-value (symbol)
  (if (symbol? symbol)
      (contents-offset symbol *symbol-value*)
      (li:error "SYMBOL-VALUE not called on a symbol")))

(defun %%set (symbol value)
  (cond ((eq symbol 'T)
	 (trap::illop "Veritas aeterae: don't set T"))
	((eq symbol 'NIL)
	 (trap::illop "Nihil ex nilhil: don't set NIL"))
	((%symbol? symbol)
	 (store-contents-offset symbol *symbol-value* value))
	(t (li:error "SET not called on a symbol")))
  ;; @@@ return to this when we are sure it is safe to go back in the water ... 
;   (store-contents-offset symbol *symbol-value* value)
  )

(defun %set (symbol value)
  (cond ((eq symbol 'T)
	 (trap::illop "Veritas aeterae: don't set T"))
	((eq symbol 'NIL)
	 (trap::illop "Nihil ex nilhil: don't set NIL"))
	((%symbol? symbol)
	 (store-contents-offset symbol *symbol-value* value))
	(t (li:error "SET not called on a symbol")))
  ;; @@@ return to this when we are sure it is safe to go back in the water ... 
;  (if (%symbol? symbol)
;      (store-contents-offset symbol *symbol-value* value)
;    (li:error "SET not called on a symbol"))
  )

(defun set (symbol value)
  (cond ((eq symbol 'T)
	 (trap::illop "Veritas aeterae: don't set T"))
	((eq symbol 'NIL)
	 (trap::illop "Nihil ex nilhil: don't set NIL"))
	((%symbol? symbol)
	 (store-contents-offset symbol *symbol-value* value))
	(t (li:error "SET not called on a symbol"))))

(defun %make-unbound (symbol)
  (%set symbol (make-pointer $$dtp-unbound symbol))
  symbol)

(defsubst cell-bound? (cell)
  (hw:vma-start-read-will-write-vma-unboxed-md-boxed cell)
  (not (vinc:data-type= (hw:read-md)
			(hw:unboxed-constant #.(lisp:ash $$dtp-unbound
							 (byte-position vinc:%%data-type))))))

(defun boundp (symbol)
  (if (symbol? symbol)
      (cell-bound? (hw:24+ *symbol-value* symbol)) 		;*** 24+ is not really sufficient
      (li:error "Boundp not called on a symbol")))

(defun makunbound (symbol)
  (set symbol (make-pointer $$dtp-unbound symbol))
  symbol)

;;; Function Cell

;;; 1. SYMBOL-FUNCTION is the common lisp function
;;; 2. When bashing the function cell of a symbol, it is important
;;;    to unlink any FEF it may be pointing at.

(defun %symbol-function (symbol)  ;;;@@@ Turn into a macro for speed. --wkf
  (contents-offset symbol *symbol-function*))

(defun symbol-function (symbol)
  (if (symbol? symbol)
      (%symbol-function symbol)
      (li:tail-error "SYMBOL-FUNCTION not called on a symbol")))

(defun %set-symbol-function (symbol value)  ;;@@@ Turn into a macro for speed. --wkf
  "Returns value."
  (cons:%store-contents-offset symbol *symbol-function* value))

(defun set-symbol-function (symbol value)
  (if (symbol? symbol)
      (if (%fboundp symbol)
	  (let ((old-function (%symbol-function symbol)))
	    (if (eq old-function value)
		value
	      (progn (%set-symbol-function symbol value)
		     (k2:kill-old-function old-function))))
	(%set-symbol-function symbol value))
    (li:tail-error "~A is not a symbol in SET-SYMBOL-FUNCTION" symbol)))

(defun %fmakunbound (symbol)
  (%set-symbol-function symbol (make-pointer $$dtp-unbound symbol))
  symbol)

(defun fmakunbound (symbol)
  (set-symbol-function symbol (make-pointer $$dtp-unbound symbol))
  symbol)

(defun %fboundp (symbol)
  (cell-bound? (hw:24+ *symbol-function* symbol)))

(defun fboundp (symbol)
  (if (symbol? symbol)
      (cell-bound? (hw:24+ *symbol-function* symbol))
      (li:error "FBOUNDP not called on a symbol")))

(defsetf symbol-function set-symbol-function)

;;; Package cells

;;; 1. No restrictions on SYMBOL-PACKAGE

(defun symbol-package (symbol)
  (if (symbol? symbol)
      (contents-offset symbol *symbol-package*)
      (li:error "SYMBOL-PACKAGE not called on a symbol")))

(defun set-symbol-package (symbol value)
  (if (symbol? symbol)
      (progn (store-contents-offset symbol *symbol-package* value)
	     value)
      (li:error "SET-SYMBOL-PACKAGE not called on a symbol")))

(defsetf symbol-package set-symbol-package)


;;; Property List

(defun symbol-plist (symbol)
  (if (symbol? symbol)
      (contents-offset symbol *symbol-plist*)
      (li:error "SYMBOL-PLIST not called on a symbol")))

(defun set-symbol-plist (symbol value)
  (if (symbol? symbol)
      (progn (store-contents-offset symbol *symbol-plist* value)
	     value)
      (li:error "SET-SYMBOL-PLIST not called on a symbol")))

(defsetf symbol-plist set-symbol-plist)


(defun get (symbol property &optional default)
  (do ((plist (symbol-plist symbol) (cons:cddr plist)))
      ((null plist) default)
    (when (eq property (cons:car plist))
      (return (cons:cadr plist)))))

(defun getf (list property &optional default)
  (do ((plist list (cons:cddr plist)))
      ((null plist) default)
    (when (eq property (cons:car plist))
      (return (cons:cadr plist)))))

(defun %putf (plist property data)
  (if (null plist)
      (cons:cons property (cons:cons data nil))
    (do () (())
      (if (eq property (cons:car plist))
	  (progn
	    (cons:rplaca (cons:cdr plist) data)
	    (return plist))
	(let ((new-plist (cons:cddr plist)))
	  (if new-plist
	      (setq plist new-plist)
	    (progn
	      (cons:rplacd (cons:cdr plist) (cons:cons property (cons:cons data nil)))
	      (return plist))))))))


(defun %put (symbol property data &optional default)
  (store-contents-offset symbol *symbol-plist* (%putf (symbol-plist symbol) property data))
  data)


(select-processor
  (:lambda )
  (:k
    (defsetf get  (symbol property &optional default) (value)
      `(%put ,symbol ,property ,value))))

(defun remprop (symbol property)
  (incf gr:*allow-sequence-break*)
  (multiple-value-bind (new-plist found-it)
      (%remprop (symbol-plist symbol) property)
    (setf (symbol-plist symbol) new-plist)
    (decf gr:*allow-sequence-break*)
    found-it))

(defun %remprop (plist property)
  (if (eq (cons:car plist) property)
      (values (cons:cddr plist) t)
    (do ((plist (cons:cdr plist) (cons:cddr plist)))
	((null plist) nil)
      (when (eq property (cons:cadr plist))
	(cons:rplacd plist (cons:cddr plist))
	(values plist t)))))

(defmacro remf (plist property)
  `(setf ,plist (%remprop ,plist ,property)))

(defun get-properties (plist properties)
  (do* ((plist plist (cons:cddr plist))
	(this-p (cons:car plist) (cons:car plist)))
       ((null plist) nil)
    (do* ((properties properties (cons:cdr properties))
	  (property (cons:car properties) (cons:car properties)))
	 ((null properties))
      (when (eq this-p property)
	(return-from get-properties (values property (cons:cadr plist) plist))))))


;;; Making symbols

(defun %make-symbol (pname)
  (let ((symbol
	  (allocate-structure *symbol-size* 0
			      $$dtp-symbol
			      (make-header $$dtp-symbol-header pname))))
    ;; PNAME is set up by allocate structure
    ;; We know that the symbol is not constant because we just made it.
    (%make-unbound symbol)
    (%fmakunbound symbol)
    ;; These two not strictly necessary as structures are filled with NIL
    ;; when created.
    (setf (symbol-plist symbol)   nil)
    (setf (symbol-package symbol) nil)
    symbol))
	  
(defun make-symbol (pname)
  (if (array:stringp pname)
      (%make-symbol pname)
    (li:error "~a is not a string" pname)))

