;;; -*- Mode:LISP; Package:(NC LISP); Base:10; Readtable:CL -*-

;;;; Top Level Functions and Temporary Sleazy Fasdumper

(export 'nlisp:compile-file 'nlisp)

(defun nca (lambda  &optional (env (make-compiler-env)))
  (really-compile lambda env))

(defun really-compile (exp env)
  (let ((tree (make-code-tree exp env)))
    (analyze tree)
    (multiple-value-bind (instructions entry-points)
	(generate tree)
      (assemble-instruction-list instructions entry-points))))


(defun c (defun)
  (if (eq (car defun) 'DEFUN)
      (nca `(NAMED-LAMBDA ,(second defun) ,(third defun)
			  (BLOCK ,(second defun)
			    ,@(cdddr defun))))
    (let ((new-defun (nlisp:macroexpand-1 defun)))
      (if (eq new-defun defun)
	  (error "~%This is not a defun:~%~a" defun)
	(c new-defun)))))


(defvar *env* nil "Compile time environment")

(defvar *kbin-output-stream*)

(defun kbin-out (thing)
  ;; is this cool?
  ;; print with package
  (let ((*package* nil)
	(*readtable* si:common-lisp-readtable))
    (cond ((null thing) (princ "()" *kbin-output-stream*))
	  ((consp thing) (prin1 thing *kbin-output-stream*))
	  (t (print thing *kbin-output-stream*)))))

(defun kbin-write-byte (byte)
  (write-byte byte *kbin-output-stream*))

(defun kbin-write-instruction (inst)
  (kbin-write-byte (ldb (byte 8.  0.) inst)) 
  (kbin-write-byte (ldb (byte 8.  8.) inst)) 
  (kbin-write-byte (ldb (byte 8. 16.) inst)) 
  (kbin-write-byte (ldb (byte 8. 24.) inst))
  (kbin-write-byte (ldb (byte 8. 32.) inst))
  (kbin-write-byte (ldb (byte 8. 40.) inst))
  (kbin-write-byte (ldb (byte 8. 48.) inst))
  (kbin-write-byte (ldb (byte 8. 56.) inst)))

(defconstant FASL-OP/DEFUN    0)
(defconstant FASL-OP/MACRO    1)
(defconstant FASL-OP/SUBST    2)
(defconstant FASL-OP/CONSTANT 3)
(defconstant FASL-OP/EVAL     4)
(defconstant n-fasl-ops 5)

;;; this doesn't belong here
(DEFUN PATHNAME-DEFAULT-BINARY-FILE-TYPE (PATHNAME)
  "Given a pathname, return the default binary file type (possibly canonical) to use with it.
This is computed from the SYSTEM which the pathname belongs to."
  (OR (zl:SEND (zl:SEND PATHNAME :GENERIC-PATHNAME) :GET :DEFAULT-BINARY-FILE-TYPE)
      "KBIN"))

(defun nlisp:compile-file (input-file &optional output-file)
  (let ((inpath (fs:merge-pathname-defaults input-file fs:load-pathname-defaults)))
    (let ((outpath
	    (cond ((typep output-file 'pathname)
		   (if (zl:send output-file :version)
		       output-file
		     (zl:send output-file :new-pathname
			      :version :newest)))
		  (output-file
		   (fs:merge-pathname-defaults
		     output-file inpath
		     (pathname-default-binary-file-type
		       (zl:send inpath :generic-pathname))
		     :newest))
		  (t
		   (zl:send inpath :new-pathname
			    :type (pathname-default-binary-file-type
				    (zl:send inpath :generic-pathname))
			    :version :newest)))))
      (with-open-file (input-stream inpath)
	(with-open-file (*kbin-output-stream* outpath :direction :output)
	  (let ((*env* (make-compiler-env))
		(*package* (let ((package-spec (progn (fs:read-attribute-list inpath input-stream)
						      (zl:send inpath :get :package))))
			     (or (if package-spec
				     (find-package package-spec))
				 *package*)))
		(*readtable* (si:find-readtable-named
				 (or (zl:send inpath :get :readtable)
				     :cl))))
	    (do* ((eof (list ()))
		  (form (read input-stream nil eof)
			(read input-stream nil eof)))
		 ((eq form eof))
	      (comp-form form))))))))

(defvar *top-level-form-handler-table* (make-table '*top-level-form-handler-table*))

(defmacro define-toplevel-form-handler (name pattern &body body)
  (let ((sym (concatenate-symbol 'toplevel/ name))
        (form (gensym 'form)))
    `(progn
       (defun ,sym (,form)
	 (destructure ((,pattern (cdr ,form)))
		      ,@body))
       (setf (table-entry *top-level-form-handler-table* ',name)
	     #',sym))))
	 
(defun comp-form (form)
  (if (not (consp form))
      (warn "The atom ~s was found at toplevel, this would do nothing" form)
    (let ((handler (table-entry *top-level-form-handler-table* (car form))))
      (if handler
	  (funcall handler form)
	(let ((new-form (nlisp:macroexpand-1 form *env*)))
	  (if (eq new-form form)
	      (arrange-for-eval form)
	    (comp-form new-form)))))))

(defun arrange-for-eval (form)
  (kbin-write-byte FASL-OP/EVAL)
  (kbin-out form))

(define-toplevel-form-handler DEFUN (name lambda-list . body)
  (multiple-value-bind (code local-refs refs immediates)
      (nca `(named-lambda ,name ,lambda-list (block ,name . ,body)) *env*)
    (kbin-write-byte FASL-OP/DEFUN)
    (kbin-out name)
    (kbin-out lambda-list)
    (kbin-out local-refs)
    (kbin-out refs)
    (kbin-out immediates)
    (kbin-out (length code))
    (map nil #'kbin-write-instruction code)))

(define-toplevel-form-handler PRIMS:DEFSUBST (name lambda-list . body)
  (eval `(GLOBAL::DEFSUBST ,name ,lambda-list . ,body))   ;crock 
  (multiple-value-bind (code local-refs refs immediates)
      (nca `(named-lambda ,name ,lambda-list . ,body) *env*)
    (kbin-write-byte FASL-OP/SUBST)
    (kbin-out name)
    (kbin-out lambda-list)
    (kbin-out body)
    (kbin-out local-refs)
    (kbin-out refs)
    (kbin-out immediates)
    (kbin-out (length code))
    (map nil #'kbin-write-instruction code)))

(define-toplevel-form-handler DEFAFUN (name lambda-list . body)
  (multiple-value-bind (code local-refs refs immediates)
      (assemble-instruction-list (cons name body) `((,(length lambda-list) . ,name)))
    (kbin-write-byte FASL-OP/DEFUN)
    (kbin-out name)
    (kbin-out lambda-list)
    (kbin-out local-refs)
    (kbin-out refs)
    (kbin-out immediates)
    (kbin-out (length code))
    (map nil #'kbin-write-instruction code)))
  
;;; value optional
;;; doc string
(define-toplevel-form-handler DEFCONSTANT (name value)
  (eval `(DEFCONSTANT ,name ,value)) ;crock
  (kbin-write-byte FASL-OP/EVAL)
  (kbin-out `(DEFCONSTANT ,name ,value)))

(define-toplevel-form-handler DEFSETF rest
  (eval `(DEFSETF . ,rest))
  (kbin-write-byte FASL-OP/EVAL)
  (kbin-out `(DEFSETF . ,rest)))
	    
(define-toplevel-form-handler PRIMS:DEFMACRO macrobody
  (let ((macrofun (si:expand-defmacro macrobody nil)))	;** env
    (multiple-value-bind (code local-refs refs immediates)
	(nca `(named-lambda . ,macrobody) *env*)
      (kbin-write-byte FASL-OP/MACRO)
      (kbin-out (first macrobody))
      (kbin-out (second macrobody))
      (kbin-out local-refs)
      (kbin-out refs)
      (kbin-out immediates)
      (kbin-out (length code))
      (map nil #'kbin-write-instruction code))
    (fbind (car macrobody) (cons 'MACRO macrofun) *env*)	;??bind?
    ))

(define-toplevel-form-handler PROGN body
  (dolist (form body)
    (comp-form form)))

(define-toplevel-form-handler IN-PACKAGE rest
  (eval (cons 'IN-PACKAGE rest))
  (arrange-for-eval `(IN-PACKAGE . ,rest)))  

(define-toplevel-form-handler EVAL-WHEN (when . body)
  (if (member 'compile when)
      (eval `(progn . ,body)))
  (if (member 'load when)
      (arrange-for-eval `(progn . ,body))))



;----------------------------------------------------------------
;;; Editor Interface


;;; more here
(defun compile-region-for-new-processor (form)
  (cc form))

(zwei:DEFCOM com-compile-region-for-new-processor "" ()
  (zwei:COMPILE-DEFUN-INTERNAL #'compile-region-for-new-processor "Hacking" "hacked.")
  zwei:DIS-NONE)


(zwei:COMMAND-STORE 'com-compile-region-for-new-processor #\s-sh-C zwei:*ZMACS-COMTAB*)



