

(in-package "CL-USER")

;; COMP-3: closure conversion

(defun is-lambda-var-3 (v)
  (and (listp v) (eq (car v) 'lambda)))

(defun is-global-3 (v)
  (and (listp v) (eq (car v) 'global)))

(defun is-svref-3 (v)
  (and (listp v) (eq (car v) '%svref)))

(defun is-unquote-3 (v)
  (and (listp v) (eq (car v) 'unquote)))


(defun un-global-3 (v)
  (cadr v))



(defun comp-3 (e sl)
  (labels
      ((comp-3-number (e sl) e)
       (comp-3-string (e sl) e)
       (comp-3-other (e sl) e))
    (let ()
;     (format t "++++++++~%comp3: e = ~A ~%sl=~A~%+++++++++~%" e sl)
     (cond ((null e) e)
           ((symbolp e) (access-read-3 (comp-3-symbol e sl)))
           ((numberp e) (comp-3-number e sl))
           ((stringp e) (comp-3-string e sl))
           ((characterp e) (comp-3-other e sl))
           ((listp e)
            (cond
             ((is-quote e) e)
             ((is-special-form e) (comp-3-nohead e sl))
             ((is-do-until e) (comp-3-do-until e sl))
             ((is-primitive e) (comp-3-nohead e sl))
             ((is-system e) (comp-3-nohead e sl))
             ((is-setq e) (comp-3-setq e sl))
             ((is-lambda (car e)) (comp-3-let e sl))
             ((is-lambda e) (comp-3-lambda e sl))
             ((is-labels e) (comp-3-labels e sl))
             ((is-form e) (comp-3-form e sl))
             (t (comp-3-generic e sl))))))))

(defun comp-3-symbol (e sl)
;  (format t "e= ~A~% sl = ~A~%" e sl)
  (let ((subs (assoc e sl)))
;    (format t "subs = ~A~%*********************************~%" subs)
    (if subs
        (let ((res (cdr subs)))
          (if (listp res)
              res
            (error "comp-3-symbol: error 1")))
      (list 'global e))))
      

(defun comp-3-generic (e sl)
  (mapcar #'(lambda (z) (comp-3 z sl)) e))

(defun comp-3-do-until (e sl)
  (let ((second (cadr e))
        (body (cddr e)))
    (append 
     (list 'do-until
           (cons (comp-3 (car second) sl) (comp-3-generic (cdr second) sl)))
           (comp-3-generic body sl))))


(defun comp-3-nohead (e sl)
  (cons (car e)
        (comp-3-generic (cdr e) sl)))


(defun comp-3-form (e sl)
  (let ((head (car e))
        (rest (comp-3-generic (cdr e) sl)))
    (setf head (access-read-fun-3 (comp-3-symbol head sl)))
    (cons head rest)))





(defun comp-3-let (e sl)
  (let ((body (lambda-body (car e)))
        (vars (lambda-vars (car e)))
        (args (cdr e)))
    (let ((sl-1 (append
                 (mapcar #'(lambda (z) (cons z (list 'lambda z))) vars) sl)))
      (let ((e1 (mapcar #'(lambda (z) (comp-3 z sl)) args))
            (body-1 (comp-3 body sl-1)))
        (let ((res (cons (make-lambda vars body-1) e1)))
           res)))))

(defun access-read-3 (z)
  (if (atom z)
      z
    (cond ((is-lambda-var-3 z) (cadr z))
          ((is-global-3 z) (list '%symbol-value (list 'quote (cadr z))))
          ((is-unquote-3 z) (list '%symbol-value (cdr z)))
          ((is-svref-3 z) z)
          (t z))))

(defun access-read-fun-3 (z)
  (if (atom z)
      z
    (cond ((is-lambda-var-3 z) 
           (list '%symbol-function (list 'quote (cadr z))))
          ((is-global-3 z) 
           (list '%symbol-function (list 'quote (cadr z))))
          ((is-unquote-3 z) (list '%symbol-value (cdr z)))
          ((is-svref-3 z) z)
          (t z))))


(defun access-write-3 (z)
  (if (atom z)
      z
    (cond ((is-global-3 z) (list 'quote (cadr z)))
          ((is-lambda-var-3 z) (cadr z))
          ((is-unquote-3 z) (cdr z))
          ((is-svref-3 z) z)
          (t z))))

(defun comp-3-setq (e sl)
  (let ((v (cadr e))
        (e1 (caddr e)))
    (setf v (comp-3-symbol v sl))
    (setf e1 (comp-3 e1 sl))
    (cond ((is-global-3 v)
           (list '%set-symbol-value (access-write-3 v) e1))
          ((is-unquote-3 v)
           (list '%set-symbol-value (access-write-3 v) e1))
          ((is-svref-3 v)
           (list '%set-svref (cadr v) (caddr v) e1))
          ((is-lambda-var-3 v)
           (list 'setq (access-write-3 v) e1)))))
      

(defun prepend-lambda-var (v lam)
  (make-lambda (cons v (lambda-vars lam)) (lambda-body lam)))


(defun pair (l1 l2)
  (zip #'(lambda (x y) (cons x y)) l1 l2))


(defun comp-3-lambda (e sl)
  (let ((rds (annotate-prop e 'reads)))
    (let ((rds-subs (mapcar #'(lambda (z) (comp-3-symbol z sl)) rds)))
      (setf rds-subs (pair rds-subs rds))
      (setf rds-subs (remove-if #'(lambda (z) (is-global-3 (car z))) rds-subs))
      (setf rds (mapcar #'cdr rds-subs))
      (setf rds-subs (mapcar #'car rds-subs))
      (let ((clos-vect (mapcar #'(lambda (z) (access-read-3 z)) rds-subs)))
        (let ((self-var (get-unique-var 'self)))
          (let ((sl-a (enumerate rds 0)))
            (let ((sl-1 (append
                         (mapcar #'(lambda (z)
                                     (cons (car z) (list '%svref (list '%closure-ref self-var 1) (cdr z)))) sl-a) sl)))
              (let ((vars (lambda-vars e))
                    (body (lambda-body e)))
                (let ((sl-1 (append
                             (mapcar #'(lambda (z) (cons z (list 'lambda z))) vars) sl-1)))
                  (let ((vars-1 (cons self-var vars))
                        (body-1 (comp-3-generic body sl-1)))
                    (list '%make-closure (make-lambda vars-1 body-1)
                          (cons 'vector clos-vect))))))))))))
      

(defun comp-3-labels (e sl)
  (let ((e1 (un-annotate e))
        (rds (annotate-prop e 'reads)))
    (let ((e-body (labels-body e1))
          (fn-defs (cadr e1)))
      (let ((fn-lis (mapcar #'(lambda (z) (list 'lambda (cadr z) (caddr z) (cadddr z))) fn-defs))
            (fn-vars (mapcar #'car fn-defs)))
        (let ((rds-subs (mapcar #'(lambda (z) (comp-3-symbol z sl)) rds)))
          (setf rds-subs (pair rds-subs rds))
          (setf rds-subs (remove-if #'(lambda (z) (is-global-3 (car z))) rds-subs))
          (setf rds (mapcar #'cdr rds-subs))
          (setf rds-subs (mapcar #'car rds-subs))
          (do ((cc (reverse fn-vars) (cdr cc)))
              ((null cc))
              (setf rds (cons (car cc) rds))
              (setf rds-subs (cons nil rds-subs)))
          (let ((clos-vect (mapcar #'(lambda (z) (access-read-3 z)) rds-subs)))
            (let ((self-var (get-unique-var 'self))
                  (cv-var (get-unique-var 'cv)))
                (let ((sl-a (enumerate rds 0)))
                  (let ((sl-1 (append
                               (mapcar #'(lambda (z)
                                           (cons (car z) (list '%svref (list '%closure-ref self-var 1) (cdr z)))) sl-a) sl)))
                    (let ((let-body 
                           (mapcar #'(lambda (z) (list '%set-svref cv-var (cdr z)
                                                       (list '%make-closure
                                                             (make-lambda (cons self-var (lambda-vars (car z)))
                                                                          (comp-3 (lambda-body (car z)) 
                                                                                  (append
                                                                                   (mapcar #'(lambda (x) (cons x (list 'lambda x)))
                                                                                           (lambda-vars (car z)))
                                                                                   sl-1)))
                                                             cv-var))) (enumerate fn-lis 0))))
                      (setf let-body (append let-body
                                             (list (list (make-lambda (list self-var) (comp-3-generic e-body sl-1)) 
                                                         (list '%svref cv-var 0)))))
                      (make-let (list cv-var) (list (cons 'vector clos-vect))
                                let-body)))))))))))

