;	-*- Mode:LISP; Package:OBJ; Syntax:ZETALISP; Readtable:CL; Base:10 -*-

(in-package "OBJ")

(defmacro stickon (thing list list-end)
  `(if (null ,list-end)
       (setq ,list (setq ,list-end (ncons ,thing)))
       (rplacd ,list-end (setq ,list-end (ncons ,thing)))))

(defmacro stickon* (new-list list list-end)
  `(let ((new ,new-list))
     (unless (null new)
       (if (null ,list-end)
	   (setq ,list new)
	   (rplacd ,list-end new))
       (setq ,list-end (last new)))))

(defvar *nloop-collections)

(defmacro collect (form &optional (collection 'standard-nloop-collection))
  `(stickon ,form ,collection ,(cdr (assq collection *nloop-collections))))

(defmacro collect* (form &optional (collection 'standard-nloop-collection))
  `(stickon* ,form ,collection ,(cdr (assq collection *nloop-collections))))

;(collecting vars)
;(with var init)
;(for var init repeat)
;(for-on var list)
;(for-in var list)
;(collect form collection)
;(collect* form collection)
;(finally forms)
;(stop)
;(stop-if test)
;(stop-unless test)
;(inc-til var start limit inc)
;(inc-nottil var start limit inc)
;(dec-til var start limit inc)
;(dec-nottil var start limit inc)

;(defun foo (list)
;  (nloop (for-on l1 list) (for x (car l1)) (print x) (collect (1+ x))))

(defmacro nloop (&body body)
  (let (finally finally? collections aux-collections
	clauses clauses-end vars vars-end)
   (flet ((return-form ()	; Return >without< bypassing finally stuff.
	    (if (null finally?) '(return nil) '(return-from do-nloop t))))
    (do ((body body)
	 (clause (car body) (car body)))
	((null body))
      (pop body)
      (case (car clause)
	((finally collecting collect collect*)
	 (setq finally? t)
	 (return))))
    (do ((clause (car body) (car body)))
	((null body)
	 (setq aux-collections (mapcar #'(lambda (ignore)
					   (declare (ignore ignore))
					   (gensym))
				       collections))
	 (when (memq 'standard-nloop-collection collections)
	   (if (null finally)
	       (setq finally (ncons 'standard-nloop-collection))
	       (rplacd (last finally) (ncons 'standard-nloop-collection))))
	 `(compiler-let ((*nloop-collections
			   (mapcar #'(lambda (var aux) (cons var aux))
				   ',collections ',aux-collections)))
	    (let (,@collections ,@aux-collections)
	      ,(if (null finally?)
		   `(do* ,vars (nil)
		      ,@clauses)
		   `(let* ,vars
		      (do () ((block do-nloop ,@clauses nil)
			      ,@finally)
			))))))
      (pop body)
      (if (not (consp clause))
	  (stickon clause clauses clauses-end)
	  (case (car clause)
	    (collecting
	     (setq collections
		   (if (memq t (cdr clause))
		       `(standard-nloop-collection ,@(remq t (cdr clause)))
		       (cdr clause))))
	    (collect
	     (let ((var (or (caddr clause) 'standard-nloop-collection)))
	       (unless (memq var collections)
		 (push var collections)))
	     (stickon clause clauses clauses-end))
	    (collect*
	     (let ((var (or (caddr clause) 'standard-nloop-collection)))
	       (unless (memq var collections)
		 (push var collections)))
	     (stickon clause clauses clauses-end))
	    (finally
	      (setq finally (append (cdr clause) finally)))
	    (with
	     (stickon `(,(cadr clause) ,(caddr clause)) vars vars-end))
	    (for
	     (cond ((null (cdddr clause))
		    (stickon (cadr clause) vars vars-end)
		    (stickon `(setq ,(cadr clause) ,(caddr clause))
			     clauses clauses-end))
		   (t
		    (stickon `(,(cadr clause) 'nloop-uninit-token) vars vars-end)
		    (stickon `(if (eq ,(cadr clause) 'nloop-uninit-token)
				  (setq ,(cadr clause) ,(caddr clause))
				  (setq ,(cadr clause) ,(cadddr clause)))
			     clauses clauses-end))))
	    (for-in				; No check for duplicate popping!
	     (let ((listvar (gensym)))
	       (stickon `(,listvar ,(caddr clause)) vars vars-end)
	       (stickon (cadr clause) vars vars-end)
	       (stickon `(if (null ,listvar)
			     ,(return-form)
			     (setq ,(cadr clause) (pop ,listvar)))
			clauses clauses-end)))
	    (for-on				; No check for duplicate popping!
	     (let ((listvar (gensym)))
	       (stickon `(,listvar ,(caddr clause)) vars vars-end)
	       (stickon (cadr clause) vars vars-end)
	       (stickon `(setq ,(cadr clause) ,listvar) clauses clauses-end)
	       (stickon `(if (null ,listvar)
			     ,(return-form)
			     (setq ,listvar (cdr ,listvar)))
			clauses clauses-end)))
	    (stop-if
	     (stickon `(if ,(cadr clause)
			   ,(return-form))
		      clauses clauses-end))
	    (stop-unless
	     (stickon `(or ,(cadr clause)
			   ,(return-form))
		      clauses clauses-end))
	    (inc-til (pop clause)
	     (let ((var (pop clause))
		   (start (pop clause))
		   (limit (pop clause))
		   (inc (or (pop clause) 1))
		   (limitvar (gensym)) (incvar (gensym)))
	       (stickon `(,var nil) vars vars-end)
	       (stickon `(,limitvar ,limit) vars vars-end)
	       (stickon `(,incvar ,inc) vars vars-end)
	       (stickon `(cond ((null ,var) (setq ,var ,start))
			       ((< ,var ,limitvar) (incf ,var ,incvar))
			       (t ,(return-form)))
			clauses clauses-end)))
	    (inc-nottil (pop clause)
	     (let ((var (pop clause))
		   (start (pop clause))
		   (limit (pop clause))
		   (inc (or (pop clause) 1))
		   (limitvar (gensym)) (incvar (gensym)))
	       (stickon `(,var nil) vars vars-end)
	       (stickon `(,limitvar ,limit) vars vars-end)
	       (stickon `(,incvar ,inc) vars vars-end)
	       (stickon `(if (null ,var)
			     (setq ,var ,start)
			     (incf ,var ,incvar))
			clauses clauses-end)
	       (stickon `(if (not (< ,var ,limitvar))
			     ,(return-form))
			clauses clauses-end)))
	    (dec-til (pop clause)
	     (let ((var (pop clause))
		   (start (pop clause))
		   (limit (pop clause))
		   (inc (or (pop clause) 1))
		   (limitvar (gensym)) (incvar (gensym)))
	       (stickon `(,var nil) vars vars-end)
	       (stickon `(,limitvar ,limit) vars vars-end)
	       (stickon `(,incvar ,inc) vars vars-end)
	       (stickon `(cond ((null ,var) (setq ,var ,start))
			       ((> ,var ,limitvar) (decf ,var ,incvar))
			       (t ,(return-form)))
			clauses clauses-end)))
	    (dec-nottil (pop clause)
	     (let ((var (pop clause))
		   (start (pop clause))
		   (limit (pop clause))
		   (inc (or (pop clause) 1))
		   (limitvar (gensym)) (incvar (gensym)))
	       (stickon `(,var nil) vars vars-end)
	       (stickon `(,limitvar ,limit) vars vars-end)
	       (stickon `(,incvar ,inc) vars vars-end)
	       (stickon `(if (null ,var)
			     (setq ,var ,start)
			   (decf ,var ,incvar))
			clauses clauses-end)
	       (stickon `(if (not (> ,var ,limitvar))
			     ,(return-form))
			clauses clauses-end)))
	    (otherwise (stickon clause clauses clauses-end))))))))
