;;**** allow simple lambdas t have used rest arguments?
;;**** rethink substitution of multiple value continuations
(in-package "XLSCMP")
;;;;;
;;;;; Macro expansion, alphatization, and CPS Conversion
;;;;;
#|
Special forms done (sort of):
BLOCK CASE CATCH ERRSET FLET FUNCTION GO IF LABELS LAMBDA LET LET*
LOOP PROGN PROGV QUOTE RETURN-FROM SETQ TAGBODY THROW UNWIND-PROTECT
LOCALLY MULTIPLE-VALUE-CALL MULTIPLE-VALUE-PROG1 DECLARE
Special forms needed later:
GENERIC-FLET GENERIC-LABELS LOAD-TIME-VALUE WITH-ADDED-METHODS
|#
;;;;
;;;; Internal CPS node representation
;;;;
(setf (symbol-function 'leaf-node-p) #'cps-leaf-node-p)
(setf (symbol-function 'lambda-node-p) #'cps-lambda-node-p)
(setf (symbol-function 'call-node-p) #'cps-call-node-p)
(setf (symbol-function 'node-children) #'cps-node-children)
(setf (symbol-function 'node-parent) #'cps-node-parent)
(setf (symbol-function 'node-simplified-p) #'cps-node-simplified-p)
(setf (symbol-function 'node-note) #'cps-node-note)
(setf (symbol-function 'set-node-children) #'cps-set-node-children)
(setf (symbol-function 'set-node-parent) #'cps-set-node-parent)
(setf (symbol-function 'set-node-simplified) #'cps-set-node-simplified)
(setf (symbol-function 'set-node-note) #'cps-set-node-note)
(setf (symbol-function 'leaf-node-value) #'cps-leaf-node-value)
(setf (symbol-function 'leaf-node-count) #'cps-leaf-node-count)
(setf (symbol-function 'set-leaf-node-value) #'cps-set-leaf-node-value)
(setf (symbol-function 'set-leaf-node-count) #'cps-set-leaf-node-count)
(setf (symbol-function 'lambda-node-arglist) #'cps-lambda-node-arglist)
(setf (symbol-function 'lambda-node-lambda-list) #'cps-lambda-node-lambda-list)
(setf (symbol-function 'lambda-node-name) #'cps-lambda-node-name)
(setf (symbol-function 'set-lambda-node-arglist) #'cps-set-lambda-node-arglist)
(setf (symbol-function 'set-lambda-node-lambda-list) #'cps-set-lambda-node-lambda-list)
(setf (symbol-function 'set-lambda-node-name) #'cps-set-lambda-node-name)
(setf (symbol-function 'lambda-node-body) #'cps-lambda-node-body)
(setf (symbol-function 'call-node-function) #'cps-call-node-function)
(setf (symbol-function 'call-node-args) #'cps-call-node-args)
(setf (symbol-function 'any-references-p) #'cps-any-references-p)
(setf (symbol-function 'find-references) #'cps-find-references)
;;;;;
;;;;; Additions to internal CPS node representation
;;;;;
;; general nodes
(defun make-node () (make-cps-node nil))
(defun node-expression (e)
(cond
((leaf-node-p e) (leaf-node-value e))
((lambda-node-p e)
`(,(if (continuation-node-p e) 'continuation 'lambda)
,(mapcar #'leaf-node-value (lambda-node-arglist e))
,(node-expression (first (node-children e)))))
((call-node-p e) (mapcar #'node-expression (node-children e)))))
;; leaf nodes
(defun make-leaf-node (v)
(let ((n (make-cps-node 'leaf)))
(set-node-simplified n t)
(set-leaf-node-value n v)
n))
;; lambda nodes
(defun make-lambda-node (arglist body &optional lambda-list)
(let ((n (make-cps-node 'lambda)))
(set-lambda-node-arglist n arglist)
(set-node-children n (list body))
(dolist (a arglist) (set-symbol-node-owner a n))
(set-node-parent body n)
(set-lambda-node-lambda-list
n
(if lambda-list lambda-list (lambda-list-default arglist)))
n))
(defun lambda-node-delete-arg (n v)
(set-lambda-node-arglist n (delete v (lambda-node-arglist n)))
(set-lambda-node-lambda-list n
(lambda-list-default (lambda-node-arglist n))))
;;**** check use of this
(defun set-lambda-node-arglist-fix (n a)
(set-lambda-node-arglist n a)
(set-lambda-node-lambda-list n (lambda-list-default a)))
;; call nodes
(defun make-call-node (&rest args)
(let ((n (make-cps-node 'call)))
(set-node-children n args)
(dolist (c args) (set-node-parent c n))
(if (lambda-node-p (first args)) (fixup-lambda-call-node n))
n))
(defun call-node-add-arg (n a)
(set-node-children n (append (node-children n) (list a)))
n)
(defun call-node-arg (n k) (nth k (call-node-args n)))
(defun call-node-arg-count (n) (length (call-node-args n)))
;;**** this needs to be replaced completely by the index based one
(defun call-node-delete-arg (n v)
(set-node-children n (delete v (node-children n)))
(set-node-parent v nil))
;;**** this is a hack -- it should be done more efficiently
(defun call-node-delete-arg-index (n i)
(let* ((c (node-children n))
(v (nth (+ i 1) c))
(x (cons nil nil))) ;;**** a unique marker
(setf (nth (+ i 1) c) x)
(set-node-children n (delete x c))
(set-node-parent v nil)))
(defun set-call-node-args (n a)
(dolist (c a) (unless (leaf-node-p c) (set-node-parent c n)))
(set-node-children n (cons (first (node-children n)) a))
(fixup-lambda-call-node n))
(defun set-call-node-function (n f)
(unless (leaf-node-p f) (set-node-parent f n))
(set-node-children n (cons f (rest (node-children n))))
(fixup-lambda-call-node n))
(defun set-call-node-arg (n i a)
(unless (leaf-node-p a) (set-node-parent a n))
(setf (nth (+ i 1) (node-children n)) a)
(fixup-lambda-call-node n))
;; node moving and copying
(defun copy-node-internals (b a)
(if (leaf-node-p b) (error "can't copy a leaf node"))
(let ((pa (node-parent a)))
(cps-node-transform a b)
(set-node-parent a pa)))
(defun move-node-tree (b a)
(copy-node-internals b a)
(dolist (c (node-children a)) (set-node-parent c a))
(do ((p (node-parent a) (node-parent p)))
((not (and p (node-simplified-p p))))
(set-node-simplified p nil)))
(defun internal-insert-node-tree (tree form pos)
(setf (nth pos (node-children form)) tree)
(unless (leaf-node-p tree) (set-node-parent tree form)))
(defun insert-node-tree (tree form pos)
(internal-insert-node-tree tree form pos)
(do ((p form (node-parent p)))
((not (and p (node-simplified-p p))))
(set-node-simplified p nil)))
;;****** this may be dangerous as it does not replace variable nodes
;;****** variable node replacement is needed for multiple inlining
(defun copy-node-tree (tree)
(if (leaf-node-p tree)
tree
(let ((cp (make-node)))
(copy-node-internals tree cp)
(if (lambda-node-p cp)
(dolist (a (lambda-node-arglist cp))
(set-symbol-node-owner a cp)))
(let ((ch (copy-list (node-children cp))))
(set-node-children cp ch)
(dotimes (i (length ch))
(internal-insert-node-tree (copy-node-tree (nth i ch)) cp i))
cp))))
;; continuation nodes
(defun continuation-node-p (n)
(and (lambda-node-p n) (eq (node-note n) 'continuation)))
(defun make-continuation-node (arglist body)
(let ((n (make-lambda-node arglist body)))
(set-node-note n 'continuation)
n))
#|
(setf count 0)
(defun mark-tree-unsimplified (n)
(incf count)
(unless (leaf-node-p n) (set-node-simplified n nil))
(dolist (c (node-children n)) (mark-tree-unsimplified c)))
|#
;;;;
;;;; Representation-independent part
;;;;
(defun pp-cps (e) (pprint (node-expression e)))
;;;
;;; Conversion rules management
;;;
(defun set-cps-converter (s c) (setf (get s 'cps-converter) c))
;;***** check for local function binding
(defun get-cps-converter (s)
(if (and (symbolp s) (null (assoc s *cmp-fenv*)))
(get s 'cps-converter)))
(defmacro define-cps-converter (sym &rest body)
`(set-cps-converter ',sym #'(lambda ,@body)))
;;;
;;; CPS converter
;;;
(defvar *cmp-env* nil)
(defvar *cmp-fenv* nil)
(defvar *cmp-denv* nil)
(defvar *cmp-tenv* nil)
(defvar *cmp-specials* nil)
(defvar *cmp-macros* nil)
(defvar *cmp-setf* nil)
(defvar *cmp-structs* nil)
(defvar *cmp-gvars* nil)
(defvar *cmp-gfuns* nil)
(defvar *cmp-consts* nil)
(defvar *cmp-base-continuation-symbol* (gensym "K0-"))
(defun lfun-node-p (n) (lambda-node-p (find-lambda-binding n)))
(defun gfun-node-p (n) (and (leaf-node-p n) (eq (symbol-node-owner n) 'gfun)))
(defun gvar-node-p (n) (and (leaf-node-p n) (eq (symbol-node-owner n) 'gvar)))
(defun constant-node-p (n) (and (leaf-node-p n) (eq (node-note n) 'constant)))
(defun gfun-eq (n s) (and (gfun-node-p n) (eq (gfun-symbol n) s)))
(defun gfun-member (n sl) (and (gfun-node-p n) (member (gfun-symbol n) sl)))
(defun gfun-symbol (n) (leaf-node-value n))
(defun local-symbol-node-p (n)
(and (leaf-node-p n) (lambda-node-p (symbol-node-owner n))))
(defun make-variable-node (e) (make-leaf-node e))
(defun make-function-node (e) (make-leaf-node e))
(defun make-constant-node (e)
(let ((n (make-leaf-node e)))
(set-node-note n 'constant)
n))
(defun symbol-node-owner (n) (node-note n))
(defun set-symbol-node-owner (n o) (set-node-note n o))
(defun new-env-node (sym)
(let ((n (make-variable-node sym)))
(push (list sym n) *cmp-env*)
n))
(defun new-fenv-node (sym)
(let ((n (make-function-node sym)))
(push (list sym n) *cmp-fenv*)
n))
(defun make-gvar-node (sym)
(let ((n (make-variable-node sym)))
(set-symbol-node-owner n 'gvar)
n))
(defun make-gfun-node (sym)
(let ((n (make-function-node sym)))
(set-symbol-node-owner n 'gfun)
n))
(defun new-gvar-node (sym)
(let ((n (make-gvar-node sym)))
(push (list sym n) *cmp-gvars*)
n))
(defun new-gfun-node (sym)
(let ((n (make-gfun-node sym)))
(push (list sym n) *cmp-gfuns*)
n))
(defun new-constant-node (e)
(let ((n (make-constant-node e)))
(push (list e n) *cmp-consts*)
n))
(defun get-gvar-node (sym)
(let ((gnode (second (assoc sym *cmp-gvars*))))
(if gnode gnode (new-gvar-node sym))))
(defun get-gfun-node (sym)
(let ((gnode (second (assoc sym *cmp-gfuns*))))
(if gnode gnode (new-gfun-node sym))))
(defun get-variable-node (sym)
(if (is-special-variable sym)
(get-gvar-node sym)
(let ((lnode (second (assoc sym *cmp-env*))))
(if lnode lnode (get-gvar-node sym)))))
(defun get-function-node (sym)
(let ((lnode (second (assoc sym *cmp-fenv*))))
(if lnode lnode (get-gfun-node sym))))
(defun get-constant-node (e)
(let ((cnode (second (assoc e *cmp-consts* :test #'equal))))
(if cnode cnode (new-constant-node e))))
(defun is-special-variable (sym)
(or (specialp sym) (member sym *cmp-specials*)))
(defmacro with-saved-cmp-environments (&rest body)
`(progv '(*cmp-env* *cmp-fenv* *cmp-denv* *cmp-tenv* *cmp-specials*)
(list *cmp-env* *cmp-fenv* *cmp-denv* *cmp-tenv* *cmp-specials*)
,@body))
(defun cvt (e)
(progv '(*cmp-env* *cmp-fenv* *cmp-denv* *cmp-tenv* *cmp-specials*
*cmp-gvars* *cmp-gfuns* *cmp-consts*)
'(nil nil nil nil nil nil nil nil)
(let ((k0 (get-function-node *cmp-base-continuation-symbol*)))
(convert e k0))))
;;**** fix to use intrnal constant status
(defun constant-symbol-p (e) (member e '(t nil)))
(defun constant-value-form (e)
(let ((v (if e (symbol-value e) nil)))
(if (or (null v) (eq t v) (characterp v) (numberp v)) v `(quote ,v))))
(defun make-continuation-call-node (k &rest args)
(if (multiple-value-continuation-node-p k)
(apply #'make-call-node (get-gfun-node 'values) k args)
(apply #'make-call-node k args)))
#|
(defun convert (e k)
(setf e (cmp-macroexpand e))
(cond
((or (atom e) (eq (first e) 'quote))
(if (symbolp e)
(if (constant-symbol-p e)
(make-continuation-call-node
k
(get-constant-node (constant-value-form e)))
(let ((v (get-variable-node e)))
(if (gvar-node-p v)
(convert `(%symval ',e) k)
(make-continuation-call-node k v))))
(make-continuation-call-node k (get-constant-node e))))
(t (check-arg-count e)
(let ((c (get-cps-converter (first e))))
(if c
(funcall c e k)
(convert-funarg
(first e)
(with-saved-cmp-environments
(let ((p (new-env-node (gensym "P"))))
(make-continuation-node
(list p)
(convert-arguments (rest e)
(make-call-node p k)))))))))))
|#
;;***** includes initial variable substitution
;;***** make sure this initial substitution is OK
(defun pre-simplify-tree (n)
(when (and (call-node-p n) (lambda-node-p (call-node-function n)))
(lambda-call-substitute-variables n nil t)
(let ((f (call-node-function n)))
(if (null (lambda-node-arglist f))
(move-node-tree (lambda-node-body f) n)))))
(defun convert (e k)
(flet ((convert1 (e k)
(setf e (cmp-macroexpand e))
(cond
((or (atom e) (eq (first e) 'quote))
(if (symbolp e)
(if (constant-symbol-p e)
(make-continuation-call-node
k
(get-constant-node (constant-value-form e)))
(let ((v (get-variable-node e)))
(cond
((gvar-node-p v)
(unless (or (is-special-variable e)
(not *compile-warn-specials*))
(warn "variable ~s assumed special" e))
(convert `(%symval ',e) k));;////// warn if not special
(t (make-continuation-call-node k v)))))
(make-continuation-call-node k (get-constant-node e))))
(t (check-arg-count e)
(let ((c (get-cps-converter (first e))))
(if c
(funcall c e k)
(convert-funarg
(first e)
(with-saved-cmp-environments
(let ((p (new-env-node (gensym "P"))))
(make-continuation-node
(list p)
(convert-arguments (rest e)
(make-call-node p k))))))))))))
(let ((n (convert1 e k)))
(pre-simplify-tree n)
n)))
;;**** simplify symbol case?
(defun convert-funarg (f k)
(cond
((symbolp f)
(make-call-node k (get-function-node f)))
((and (consp f) (eq (first f) 'lambda)) (convert f k))
(t (error "bad function - ~s" f))))
#|
;****//// this causes deep recursions
(defun convert-arguments (args final-call)
(if (null args)
final-call
(with-saved-cmp-environments
(let ((k (new-env-node (gensym "K"))))
(convert (car args)
(make-continuation-node
(list k)
(convert-arguments (rest args)
(call-node-add-arg final-call k))))))))
|#
;;****** iterative version
(defun convert-arguments (args final-call)
(let ((vars nil))
(with-saved-cmp-environments
(dolist (a args) (push (new-env-node (gensym "V")) vars)))
;; because of substitution, adding the arguments MUST come first
(set-node-children final-call
(append (node-children final-call) (reverse vars)))
(do ((form final-call)
(rargs (reverse args) (rest rargs))
(vars vars (rest vars)))
((null rargs) form)
(let ((a (first rargs))
(v (first vars)))
(setf form (convert a (make-continuation-node (list v) form)))))))
;;;
;;; Specific conversion rules
;;;
(defun fixup-optional-argument (x)
(cond
((symbolp x) (list x nil nil))
((and (consp x) (symbolp (first x)))
(check-supplied-p-argument (third x))
(list (first x) (cmp-macroexpand (second x)) (third x)))
(t (error "bad optional argument entry -- ~s" x))))
(defun make-corresponding-keyword (s) (intern (symbol-name s) "KEYWORD"))
(defun check-supplied-p-argument (s)
(if s (unless (symbolp s) (error "bad supplied-p argument -- ~s" s))))
(defun fixup-keyword-argument (x)
(cond
((symbolp x) (list (make-corresponding-keyword x) x nil nil))
((and (consp x) (symbolp (first x)))
(check-supplied-p-argument (third x))
(list (make-corresponding-keyword (first x))
(first x)
(cmp-macroexpand (second x))
(third x)))
((and (consp x)
(consp (first x))
(symbolp (first (first x)))
(symbolp (second (first x)))
(null (rest (rest (first x)))))
(check-supplied-p-argument (third x))
(list (first (first x))
(second (first x))
(cmp-macroexpand (second x))
(third x)))
(t (error "bad keyword argument entry -- ~s" x))))
(defun fixup-aux-argument (x)
(cond
((symbolp x) (list x nil))
((and (consp x) (symbolp (first x))) (list (first x) (second x)))
(t (error "bad aux argument entry -- ~s" x))))
(defun is-lambda-key (s) (member s lambda-list-keywords))
;;**** put in better error checking here
(defun split-lambda-list (ll)
(let ((env (second (member '&environment ll)))
(req nil)
(opt nil)
(rest nil)
(allow-keys nil)
(key nil)
(allow-other-keys nil)
(aux nil))
(if env (setf ll (remove '&environment (remove env ll))))
(when ll
(loop
(let ((x (first ll)))
(if (is-lambda-key x) (return))
;;(assert (symbolp x))
(push x req)
(setf ll (rest ll))
(if (null ll) (return)))))
(when (eq (first ll) '&optional)
(setf ll (rest ll))
(loop
(let ((x (first ll)))
(if (or (null ll) (is-lambda-key x)) (return))
(push (fixup-optional-argument x) opt)
(setf ll (rest ll)))))
(when (eq (first ll) '&rest)
(setf ll (rest ll))
;;(assert (and (consp ll) (symbolp (first ll))))
(setf rest (first ll))
(setf ll (rest ll)))
(when (eq (first ll) '&key)
(setf ll (rest ll))
(setf allow-keys t)
(loop
(let ((x (first ll)))
(if (or (null ll) (is-lambda-key x)) (return))
(push (fixup-keyword-argument x) key)
(setf ll (rest ll)))))
(when (eq (first ll) '&allow-other-keys)
(setf ll (rest ll))
(setf allow-other-keys t))
(when (eq (first ll) '&aux)
(setf ll (rest ll))
(loop
(let ((x (first ll)))
(if (or (null ll) (is-lambda-key x)) (return))
(push (fixup-aux-argument x) aux)
(setf ll (rest ll)))))
(unless (null ll) (error "bad formal argument list"))
(list (reverse req)
(reverse opt)
rest
allow-keys
(reverse key)
(reverse aux)
allow-other-keys
env)))
(defun rewrite-lambda (lambda-list body)
(let* ((sl (split-lambda-list lambda-list))
(req (nth 0 sl))
(opt (nth 1 sl))
(rest (nth 2 sl))
(allow-keys (nth 3 sl))
(key (nth 4 sl))
(aux (nth 5 sl))
(allow-other-keys (nth 6 sl))
(env (nth 7 sl))
(db (split-declarations body))
(pdcl (parse-declarations (first db)))
(rdcl (mapcar #'(lambda (x) (assoc x (first pdcl))) req))
(odcl (set-difference (first pdcl) rdcl))
(body (second db))
(key-defaults nil)
(opt-defaults nil)
(variables nil)
(bindings aux))
(if env
(let ((ee (gensym "E")))
(push `(,env ,ee) bindings)
(push ee variables)))
(dolist (v (reverse key))
(let ((s (second v))
(ss (gensym "K"))
(dflt (third v))
(sp (fourth v)))
(push ss variables)
(cond
((and (constant-expression-p dflt) (null sp))
(push (strip-quote dflt) key-defaults)
(push `(,s ,ss) bindings))
(t
(push '%not-supplied key-defaults)
(push `(,s (if (%supplied-p ,ss) ,ss ,dflt)) bindings)
(if sp (push `(,sp (%supplied-p ,ss)) bindings))))))
(if rest
(let ((rr (gensym "R")))
(push `(,rest ,rr) bindings)
(push rr variables)))
(dolist (v (reverse opt))
(let ((s (first v))
(ss (gensym "O"))
(dflt (second v))
(sp (third v)))
(push ss variables)
(cond
((and (constant-expression-p dflt) (null sp))
(push (strip-quote dflt) opt-defaults)
(push `(,s ,ss) bindings))
(t
(push '%not-supplied opt-defaults)
(push `(,s (if (%supplied-p ,ss) ,ss ,dflt)) bindings)
(if sp (push `(,sp (%supplied-p ,ss)) bindings))))))
(dolist (v (reverse req)) (push v variables))
(list variables
(if bindings
`(,@(apply #'append
(mapcar #'unparse-variable-declarations rdcl))
(let* ,bindings
,@(apply #'append
(mapcar #'unparse-variable-declarations odcl))
,@(unparse-function-declarations (second pdcl))
,@(unparse-compiler-declarations (third pdcl))
,@body))
`(,@(apply #'append
(mapcar #'unparse-variable-declarations rdcl))
,@(apply #'append
(mapcar #'unparse-variable-declarations odcl))
,@(unparse-function-declarations (second pdcl))
,@(unparse-compiler-declarations (third pdcl))
,@body))
(length req)
(length opt)
opt-defaults
allow-keys
key-defaults
rest
allow-other-keys
(mapcar #'first key)
env)))
(defun lambda-list-default (args)
(append (list (length args) 0) (make-list 6)))
(defun lambda-list-data (args)
(let* ((sl (split-lambda-list args))
(req (nth 0 sl))
(opt (nth 1 sl))
(rest (nth 2 sl))
(allow-keys (nth 3 sl))
(key (nth 4 sl))
(aux (nth 5 sl))
(allow-other-keys (nth 6 sl))
(env (nth 7 sl)))
(list (length req)
(length opt)
(mapcar #'second opt)
allow-keys
(mapcar #'third key)
rest
allow-other-keys
(mapcar #'first key)
env)))
(defun find-lambda-list-data (f)
(if (and (consp f) (eq (first f) 'lambda))
(lambda-list-data (second f))
(let ((lf (second (assoc f *cmp-fenv*))))
(if lf
nil;;(lambda-node-lambda-list (find-lambda-binding lf));;////????
(get-lambda-list-data f)))))
(defmacro define-lambda-list (s ll)
`(setf (get ',s 'cmp-lambda-list-data) (lambda-list-data ',ll)))
(defun get-lambda-list-data (s) (get s 'cmp-lambda-list-data))
;;****** need lots more of this
;;****** may also need for setf methods, eg
;;(define-lambda-list (setf aref) (a i &rest args))
(define-lambda-list car (x))
(define-lambda-list aref (a i &rest args))
(define-lambda-list %set-aref (x y z &rest args)) ;;**** should be (setf aref)
(define-lambda-list - (x &rest args))
(define-lambda-list / (x &rest args))
(define-lambda-list slot-value (x &optional y))
(define-lambda-list setq (var val &rest more))
(define-lambda-list lambda (args &rest body))
(define-lambda-list let (bindings &rest body))
(define-lambda-list let* (bindings &rest body))
(define-lambda-list flet (bindings &rest body))
(define-lambda-list labels (bindings &rest body))
(define-lambda-list macrolet (bindings &rest body))
(define-lambda-list if (test cons &optional alt))
(define-lambda-list progn (&rest body))
(define-lambda-list locally (&rest body))
(define-lambda-list the (a b))
(define-lambda-list block (tag &rest body))
(define-lambda-list function (fun))
(define-lambda-list catch (tag &rest forms))
(define-lambda-list throw (tag form))
(define-lambda-list errset (form &optional print))
(define-lambda-list unwind-protect (pform &rest cforms))
(define-lambda-list tagbody (&rest body))
(define-lambda-list go (tag))
(define-lambda-list return-from (tag &optional form))
(define-lambda-list progv (syms vals &rest body))
(define-lambda-list case (key &rest forms))
(define-lambda-list multiple-value-call (funcion &rest forms))
(define-lambda-list nth-value (n form))
(define-lambda-list multiple-value-prog1 (form &rest forms))
(defun check-arg-count (e)
(let* ((f (first e))
(n (length (rest e)))
(ll (find-lambda-list-data f)))
(if ll
(let ((nr (nth 0 ll))
(no (nth 1 ll))
(allow-keys (nth 3 ll))
(rest (nth 5 ll)))
(if (< n nr) (error "too few arguments to ~s" f))
(unless (or rest allow-keys)
(if (> n (+ nr no))
(error "too many arguments to ~s" f)))))))
;;**** be more careful about non-self-evaluating forms
(defun constant-expression-p (e)
(cond
((atom e) (or (not (symbolp e)) (constant-symbol-p e)))
((eq (first e) 'quote))))
(defun strip-quote (x) (if (consp x) (second x) x))
;;**** fix these to allow inlining lambdas with rest arguments
;;**** if rest is unused, just drop.
;;**** otherwise, add code to make the list
#|
(defun rest-arg-only-p (n)
(let* ((ll (lambda-node-lambda-list n))
(r (nth 5 ll)))
(and r (every #'null (remove r (rest (rest (rest ll))))))))
|#
(defun simple-lambda-node-p (n)
(multiple-value-bind (nr no od ak kd rest aok)
(values-list (lambda-node-lambda-list n))
(and (null ak)
(null aok)
(or (null rest)
(not (any-references-p (first (last (lambda-node-arglist n)))
(lambda-node-body n)))))))
(defun fixup-lambda-call-node (n)
(let ((f (call-node-function n)))
(if (lambda-node-p f)
(let* ((ll (lambda-node-lambda-list f))
(na (call-node-arg-count n))
(nr (first ll))
(no (second ll))
(rest (sixth ll)))
(unless (simple-lambda-node-p n)
(error "can't inline a non-simple lambda"))
(if (and (not rest) (> na (+ nr no))) (error "too many arguments"))
(if (< na nr) (error "too few arguments"))
(if (< na (+ nr no))
(let ((extra (nthcdr (- na nr) (third ll))))
(set-node-children n
(append
(node-children n)
(mapcar #'(lambda (x)
(get-constant-node `',x))
extra)))))
(if (< (+ nr no) na)
(set-node-children n (butlast (node-children n) (- na nr no))))
(if rest
(lambda-node-delete-arg
f
(first (last (lambda-node-arglist f)))))))))
(defun lambda-list-num-variables (ll)
(let ((nr (nth 0 ll))
(no (nth 1 ll))
(nk (length (nth 4 ll)))
(rest (if (nth 5 ll) 1 0))
(env (if (nth 8 ll) 1 0)))
(+ nr no nk rest env)))
;;**** fix to use special declarations
(defun fix-lambda-specials (rl)
(let ((vars (first rl))
(db (split-declarations (second rl))))
(when (some #'is-special-variable vars)
(let ((bds nil)
(nv nil)
(sv nil))
(dolist (v vars)
(if (is-special-variable v)
(let ((s (gensym "SV")))
(when (constantp v)
(error "can't bind to a constant - ~s" v))
(push v bds)
(push s bds)
(push s nv)
(push v sv))
(push v nv)))
(setf (first rl) (nreverse nv))
(setf (second rl)
`(,@(first db)
(%dynamic-bind ',sv 't
#'(lambda ()
(setq ,@(nreverse bds))
,@(second db)))))))
rl))
(defun convert-lambda (e)
(with-saved-cmp-environments
(let* ((ksym (gensym "K"))
(lambda-list (cons ksym (second e)))
(body (rest (rest e)))
(rl (fix-lambda-specials (rewrite-lambda lambda-list body)))
(vars (mapcar #'new-env-node (first rl)))
(k (first vars))
(db (split-declarations (second rl)))
(new-body `(progn ,@(second db))))
(make-lambda-node vars (convert new-body k) (rest (rest rl))))))
(define-cps-converter lambda (e k)
(make-continuation-call-node k (convert-lambda e)))
(define-cps-converter let (e k)
(let ((bindings (second e))
(body (rest (rest e))))
(let ((vars (mapcar #'(lambda (x) (if (consp x) (first x) x)) bindings))
(vals (mapcar #'(lambda (x) (if (consp x) (second x))) bindings)))
(convert `((lambda ,vars ,@body) ,@vals) k))))
(define-cps-converter if (e k)
(let ((test (second e))
(consequent (third e))
(alternative (fourth e)))
(convert test
(with-saved-cmp-environments
(let ((v (new-env-node (gensym "V"))))
(make-continuation-node
(list v)
(make-call-node
(with-saved-cmp-environments
(let ((j (new-env-node (gensym "J"))))
(make-lambda-node
(list j)
(make-call-node
(get-gfun-node '%test)
(make-continuation-node () (convert consequent j))
(make-continuation-node () (convert alternative j))
v))))
k)))))))
#|
;****//// this causes deep recursions
(define-cps-converter progn (e k)
(let ((first (second e))
(rest (rest (rest e))))
(if rest
(convert first
(with-saved-cmp-environments
(make-continuation-node
(list (new-env-node (gensym "V")))
(convert `(progn ,@rest) k))))
(convert first k))))
|#
;;**** iterative version
(define-cps-converter progn (e k)
(let* ((rbody (reverse (rest e)))
(last (first rbody))
(rest (rest rbody))
(ce (convert last k)))
(dolist (ee rest ce)
(setf ce
(convert ee
(with-saved-cmp-environments
(make-continuation-node
(list (new-env-node (gensym "V")))
ce)))))))
(define-cps-converter locally (e k)
(convert (cons 'progn (rest e)) k))
(define-cps-converter the (e k)
(convert (third e) k))
(defun rewrite-let* (e)
(let* ((v (fixup-let-variables (second e)))
(db (split-declarations (rest (rest e))))
(body (second db))
(d (parse-declarations (first db)))
(vd (first d))
(fd (second d))
(cd (third d)))
(labels ((rw (v d)
(if (consp (rest v))
(let* ((vi (first v))
(vsym (first vi))
(vd (assoc vsym d)))
`(let (,vi)
,@(unparse-variable-declarations vd)
,(rw (rest v) (remove vd d))))
`(let ,v
,@(apply #'append
(mapcar #'unparse-variable-declarations d))
,@(unparse-function-declarations fd)
,@(unparse-compiler-declarations cd)
,@body))))
(rw v vd))))
(define-cps-converter let* (e k) (convert (rewrite-let* e) k))
(defun convert-named-lambda (f)
(let* ((name (first f))
(args (second f))
(d (split-declarations (rest (rest f))))
(decls (first d))
(body (second d)))
(convert-lambda `(lambda ,args ,@decls (block ,name ,@body)))))
;;**** should use named lambda's!!
;;**** deal with declarations
(define-cps-converter flet (e k)
(let* ((funs (second e))
(db (split-declarations (rest (rest e))))
(body `(progn ,@(second db))))
(flet ((cvtfun (f) (convert-named-lambda f))
(fname (f) (new-fenv-node (first f))))
(let ((cfuns (mapcar #'cvtfun funs)))
(with-saved-cmp-environments
(let* ((alist (mapcar #'fname funs))
(cbody (make-lambda-node alist (convert body k))))
(apply #'make-call-node cbody cfuns)))))))
;;**** should use named lambda's!!
;;**** deal with declarations
(define-cps-converter labels (e k)
(let* ((funs (second e))
(db (split-declarations (rest (rest e))))
(body `(progn ,@(second db))))
(flet ((cvtfun (f) (convert-named-lambda f))
(fname (f) (new-fenv-node (first f))))
(with-saved-cmp-environments
(let* ((alist (mapcar #'fname funs))
(cfuns (mapcar #'cvtfun funs))
(cbody (convert body k)))
(make-call-node
(get-gfun-node '%y)
(make-continuation-node
alist
(apply #'make-call-node
(get-gfun-node '%y-list)
(make-continuation-node nil cbody)
cfuns))))))))
(define-cps-converter block (e k)
(let ((name (second e))
(body (rest (rest e))))
(with-saved-cmp-environments
(let ((tag (new-env-node (gensym "T")))
(kk (new-env-node (gensym "K"))))
(push (list 'block name tag) *cmp-tenv*)
(make-call-node
(get-gfun-node '%catch-block)
k
(get-constant-node `(quote ,name))
(make-lambda-node (list kk tag) (convert `(progn ,@body) kk)))))))
(defun find-block-tag (name)
(dolist (a *cmp-tenv*)
(if (and (eq 'block (first a)) (eq name (second a)))
(return (first (rest (rest a)))))))
;;**** simplify this?
(define-cps-converter setq (e k)
(let ((sym (second e))
(val (third e))
(rest (rest (rest (rest e)))))
(if rest
(convert `(progn (setq ,sym ,val) (setq ,@rest)) k)
(convert val
(with-saved-cmp-environments
(let ((v (new-env-node (gensym "V")))
(sn (get-variable-node sym)))
(if (gvar-node-p sn)
(progn
(when (constantp sym)
(error
"can't change the value of a constant - ~s"
sym))
(make-continuation-node
(list v)
(make-call-node
(get-gfun-node '%set-symval)
k
(get-constant-node `(quote ,(leaf-node-value sn)))
v)))
(make-continuation-node
(list v)
(make-call-node (get-gfun-node '%setq) k sn v)))))))))
(define-cps-converter function (e k)
(let ((f (second e)))
(cond
((symbolp f)
(let ((fn (get-function-node f)))
(if (gfun-node-p fn)
(make-call-node (get-gfun-node '%symfun) k fn)
(make-continuation-call-node k fn))))
((and (consp f) (eq (first f) 'lambda)) (convert f k))
(t (error "bad argument for FUNCTION -- ~s" f)))))
(define-cps-converter catch (e k)
(convert `(%catch ,(second e) (lambda () ,@(rest (rest e)))) k))
(define-cps-converter errset (e k)
(let ((expr (second e))
(flag (if (= (length e) 3) (third e) t)))
(convert `(%errset (lambda () ,expr) ,flag) k)))
(define-cps-converter unwind-protect (e k)
(convert `(%unwind-protect #'(lambda () ,(second e))
#'(lambda () ,@(rest (rest e))))
k))
(define-cps-converter progv (e k)
(let ((vars (second e))
(vals (third e))
(body (rest (rest (rest e)))))
(convert `(%progv ,vars ,vals #'(lambda () ,@body)) k)))
;;**** is this the best way?
(define-cps-converter case (e k)
(let ((var (second e))
(sels (mapcar #'first (rest (rest e))))
(acts (mapcar #'rest (rest (rest e)))))
(if (member (first (last sels)) '(t otherwise))
(setf sels (butlast sels))
(setf acts (append acts (list nil))))
(convert var
(with-saved-cmp-environments
(let ((v (new-env-node (gensym "V"))))
(make-continuation-node
(list v)
(make-call-node
(with-saved-cmp-environments
(let ((j (new-env-node (gensym "K"))))
(flet ((mkfun (a)
(make-continuation-node
()
(convert `(progn ,@a) j))))
(make-continuation-node
(list j)
(apply #'make-call-node
(get-gfun-node '%case)
v
(get-constant-node
`(quote ,(coerce sels 'vector)))
(mapcar #'mkfun acts))))))
k)))))))
(defun split-tagbody-body (bd)
(unless (symbolp (first bd)) (push (gensym "S") bd))
(let ((pairs nil)
(tag (first bd))
(body nil))
(dolist (f (rest bd))
(cond
((consp f) (push f body))
(t (push (list tag (reverse body)) pairs)
(setf tag f)
(setf body nil))))
(push (list tag (reverse body)) pairs)
(setf pairs (reverse pairs))
(list (mapcar #'first pairs) (mapcar #'second pairs))))
(define-cps-converter tagbody (e k)
(let ((bd (rest e)))
(cond
((null bd) (convert nil k))
((every #'consp bd) (convert `(progn ,@bd nil) k))
(t (let* ((sb (split-tagbody-body bd))
(gtags (first sb))
(gbd (reverse (second sb))))
(with-saved-cmp-environments
(let ((gnodes (mapcar #'(lambda (x) (new-fenv-node (gensym x)))
gtags))
(fnodes nil)
(kk (new-env-node (gensym "K")))
(tag (new-env-node (gensym "T"))))
(with-saved-cmp-environments
(push (list 'tagbody gtags gnodes tag) *cmp-tenv*)
(push (make-continuation-node
nil
(convert `(progn ,@(first gbd))
(make-continuation-node
(list (new-env-node (gensym "V")))
(make-continuation-call-node
kk
(get-constant-node nil)))))
fnodes))
(mapc #'(lambda (x y)
(with-saved-cmp-environments
(push (list 'tagbody gtags gnodes tag) *cmp-tenv*)
(push (make-continuation-node
nil
(convert `(progn ,@x)
(make-continuation-node
(list (new-env-node (gensym "V")))
(make-call-node y))))
fnodes)))
(rest gbd)
(reverse (rest gnodes)))
(make-call-node
(get-gfun-node '%catch-tagbody)
k
(make-lambda-node
(list kk tag)
(make-call-node
(get-gfun-node '%y)
(make-continuation-node
gnodes
(apply #'make-call-node
(get-gfun-node '%y-list)
(make-continuation-node
nil
(make-call-node
(get-gfun-node '%do-catch-tagbody)
k
(first gnodes)
kk
tag))
fnodes))))))))))))
(defun find-go-tag (tag)
(dolist (a *cmp-tenv*)
(if (and (eq 'tagbody (first a)) (member tag (second a)))
(return (list (fourth a) (nth (position tag (second a)) (third a)))))))
(define-cps-converter go (e k)
(let* ((tag-name (second e))
(tag-info (find-go-tag tag-name))
(tag (first tag-info))
(fun (second tag-info)))
(if tag
(make-call-node (get-gfun-node '%throw-go) tag fun)
(error "no tag named ~s in current lexical context" tag-name))))
;;**** ought to push multiple values on stack to avoid consing for > 1 expr
(define-cps-converter multiple-value-call (e k)
(case (length e)
(2 (convert `(funcall ,(second e)) k))
(3
(let ((fun (second e))
(expr (third e)))
(convert fun
(with-saved-cmp-environments
(let ((v (new-env-node (gensym "F")))
(w (new-env-node (gensym "V"))))
(make-continuation-node
(list v)
(convert expr
(make-continuation-node
(list w)
(make-call-node
(get-gfun-node '%mv-collect)
(make-continuation-node
(list (new-env-node (gensym "D")))
(make-call-node (get-gfun-node '%mvc)
k
v))
w)))))))))
(t
(convert `(apply ,(second e)
(nconc ,@(mapcar #'(lambda (x) `(multiple-value-list ,x))
(rest (rest e)))))
k))))
;;****** uses multiple-value-continuation
(define-cps-converter nth-value (e k)
(let ((count (second e))
(expr (third e)))
(convert count
(with-saved-cmp-environments
(let ((v (new-env-node (gensym "C")))
(w (new-env-node (gensym "V"))))
(make-continuation-node
(list v)
(convert expr
(make-continuation-node
(list w)
(make-call-node
(get-gfun-node '%mv-collect)
(make-continuation-node
(list (new-env-node (gensym "D")))
(make-call-node (get-gfun-node '%nth-value)
k
v))
w)))))))))
(define-cps-converter throw (e k)
(let ((tag (second e))
(expr (third e)))
(convert tag
(with-saved-cmp-environments
(let ((v (new-env-node (gensym "C")))
(w (new-env-node (gensym "V"))))
(make-continuation-node
(list v)
(convert expr
(make-continuation-node
(list w)
(make-call-node
(get-gfun-node '%mv-collect)
(make-continuation-node
(list (new-env-node (gensym "D")))
(make-call-node (get-gfun-node '%throw) v))
w)))))))))
(define-cps-converter return-from (e k)
(let* ((name (second e))
(expr (third e))
(tag (find-block-tag name)))
(if tag
(with-saved-cmp-environments
(let ((w (new-env-node (gensym "V"))))
(convert expr
(make-continuation-node
(list w)
(make-call-node
(get-gfun-node '%mv-collect)
(make-continuation-node
(list (new-env-node (gensym "D")))
(make-call-node (get-gfun-node '%throw-return-from)
tag))
w)))))
(error "no block named ~s in current lexical context" name))))
(define-cps-converter multiple-value-prog1 (e k)
(let* ((expr (second e))
(body (rest (rest e))))
(with-saved-cmp-environments
(let ((w (new-env-node (gensym "V")))
(n (new-env-node (gensym "N"))))
(convert expr
(make-continuation-node
(list w)
(make-call-node
(get-gfun-node '%mv-collect)
(make-continuation-node
(list (new-env-node (gensym "D")))
(make-call-node
(get-gfun-node '%push-values)
(make-continuation-node
(list n)
(convert `(progn ,@body)
(make-continuation-node
(list (new-env-node (gensym "D")))
(make-call-node (get-gfun-node '%pop-values)
k
n))))))
w)))))))
;;***** this could use some decent error checking
(define-cps-converter macrolet (e k)
(let ((macs (second e))
(env (list nil *cmp-fenv* *cmp-macros* *cmp-global-macros*))
(frame nil)
(body (rest (rest e))))
(with-saved-cmp-environments
(dolist (m macs)
(push (cons (first m)
(coerce-to-macro
(parse-macro (first m) (second m) (rest (rest m)) env)))
frame))
(dolist (x frame) (push x *cmp-fenv*))
(convert `(progn ,@body) k))))
(defun substitute-all-variables (n)
(flet ((substitute1 (n)
(if (call-node-p n)
(let ((f (call-node-function n)))
(when (lambda-node-p f)
(lambda-call-substitute-variables n))))))
(do () ((not (substitute1 n))))
(dolist (c (node-children n)) (substitute-all-variables c))))
(defun collapse-null-lambda-calls (n)
(flet ((collapse1 (n)
(if (call-node-p n)
(let ((f (call-node-function n)))
(when (and (lambda-node-p f) (null (lambda-node-arglist f)))
(move-node-tree (lambda-node-body f) n)
t)))))
(do () ((not (collapse1 n))))
(dolist (c (node-children n)) (collapse-null-lambda-calls c))))
syntax highlighted by Code2HTML, v. 0.9.1