;;; -*- Mode:LISP; Package:PASCAL; Readtable:CL; Fonts:(CPTFONTB); Base:10 -*-


(defvar *debug* nil)
(defvar *indent-spaces* 5)

(defvar clone-ctr 0)
(defvar cycle-ctr 0)
(defvar cycle-ctr-limit 1000000)

(defvar max-processed-states 0)
(defvar max-processed-states-limit 128)

(defvar *left-corners*     nil)
(defvar *dominated-nodes*  nil)
(defvar *processed-states* nil)
(defvar *completed-states* nil)

(defvar max-tree-depth 64)
(defvar *collapse-single-branches* t)
(defvar *debug2* t)

(defsubst rule-mother-category (rule) (first rule))

(defsubst rule-left-corner (rule) (second rule))

(defsubst find-rules-with-left-corner (lc)
  (get lc :lc-rules))

(defstruct node
  (category  nil)
  (daughters nil)
  (translation nil))

(defsubst nodes-which-dominate (n)
  (get n :dominating-nodes))

(defun princ-at-level (obj level &optional (stream standard-output))
  (terpri stream)
  (dotimes (i (* level *indent-spaces*))
    (tyo #\space stream))
  (princ obj stream))


(defun print-tree (node level)
  (when (< level max-tree-depth)
    (princ-at-level (node-category node) level)
    (when (and *collapse-single-branches*
	       (= (length (node-daughters node)) 1))
      (do () ((not (= (length (node-daughters node)) 1)))
	(setq node (car (node-daughters node))))
      (format t "... ~S" (node-category node)))
    (if (and (node-translation node)
	     (not (eq (node-translation node)
		      (node-category node))))
	(format t " = ~A" (node-translation node)))
    (dolist (d (reverse (node-daughters node)))
      (print-tree d (1+ level)))))


(defstruct state
  (phrase-stack nil)
  (rule-stack   nil)
  (tree-stack   nil)
  (trans-stack  nil))

(defsubst current-phrase (state) (first (state-phrase-stack state)))
(defsubst pop-phrase (state) (pop (state-phrase-stack state)))
(defsubst push-phrase (phrase state) (push phrase (state-phrase-stack state)))

(defsubst current-tree (state) (first (state-tree-stack state)))
(defsubst pop-tree (state) (pop (state-tree-stack state)))

(defsubst push-tree (mother left-corner state)
  (push
   (make-node :category  mother
	      :daughters (list left-corner))
    (state-tree-stack state)))


(defsubst right-adjoin-phrase-to-tree (phrase tree)
  (push phrase (node-daughters tree)))


(defsubst current-rule-category (state)  (first (first (state-rule-stack state))))
(defsubst pop-rule (state) (pop (state-rule-stack state)))
(defsubst push-rule (rule state) (push (cddr rule) (state-rule-stack state)))
(defsubst advance-rule (state)  (pop (first (state-rule-stack state))))

(defsubst current-rule-complete? (state)
  (and (consp (state-rule-stack state))
       (null (first (state-rule-stack state)))))

(defsubst completely-parsed? (state)
  (and (null (state-tree-stack state))
       (= (length (state-phrase-stack state)) 1)
       (or (null (state-rule-stack state))
	   (and (= (length (state-phrase-stack state)) 1)
		(eq (current-rule-category state)
		    (node-category (car (state-phrase-stack state))))))))

(defsubst clone-state (state)
  (incf clone-ctr)
  (make-state :phrase-stack (state-phrase-stack state)
	      :rule-stack   (copy-list (state-rule-stack state))
	      :tree-stack   (mapcar #'(lambda (x)
					(make-node :category  (node-category  x)
						   :daughters (node-daughters x)))
				    (state-tree-stack state))))
  

(defsubst store-processed-state (state)
  (push state *processed-states*))


(defsubst possible-left-daughters (rule)
  (let ((l (car rule)))
    (if (not (consp l))
	(list l)
	(possible-left-daughters-1 rule))))


(defun possible-left-daughters-1 (rule)
  (cond ((null rule) nil)
	((not (consp (car rule)))
	 (if (not (eq (car rule) 'k*))
	     (list (car rule))
	     (list (cadr rule))))
	(t (append (possible-left-daughters-1 (car rule))
		   (possible-left-daughters-1 (cdr rule))))))



(defun add-a-rule (rule)
  (dolist (lc (possible-left-daughters (cdr rule)))
    (cond ((not (memq lc *left-corners*))
	   (remprop lc :lc-rules)
	   (putprop lc (list rule) :lc-rules)
	   (push lc *left-corners*))
	  (t (let ((entry  (get lc :lc-rules)))
	       (if entry
		   (push rule (cdr entry))
		   (putprop lc (list rule) :lc-rules)))))))


(defun add-rule (rule-with-options)
  (let ((mother-node (car rule-with-options)))
    (dolist (daughters (rule-list-from-options (cdr rule-with-options)))
      (add-a-rule (cons mother-node daughters)))))


(defun rule-list-from-options (rule-with-options) (list rule-with-options))

;(defun rule-list-from-options (rule-with-options)
;  (let ((expanded-list       (expand-options rule-with-options nil))
;	(*flattened-options* nil))
;    (declare (special *flattened-options*))
;    (flatten-options expanded-list)
;    *flattened-options*))


(defun expand-options (input output)
  (declare (special *flattened-options*))
  (cond ((null input) (reverse output))
	((consp (car input))
	 (list (expand-options (cdr input) (append (reverse (car input)) output))
	       (expand-options (cdr input) output)))
	(t (expand-options (cdr input) (cons (car input) output)))))


(defun flatten-options (list)
  (declare (special *flattened-options*))
  (cond ((null list) nil)
	((and (consp list) (not (consp (car list))))
	 (push list *flattened-options*))
	(t (flatten-options (car list))
	   (flatten-options (cdr list)))))




(defsubst rule-1-applicable? (state)
  (and (current-phrase state)
       (not (current-rule-complete? state))))

(defun rule-1 (lc lc-rules state)
  (do ((rules     lc-rules (cdr rules))
       (rule      nil)
       (new-state nil))
      ((null rules))
    (setq rule (car rules))
    (if (consp (cadr rule))
	(setq rule (cons (car rule) (lc-expanded-options (cdr rule) lc))))
    (cond ((check-domination (car rule) state)
	   (if *debug* (format t "~%Successfully applying rule 1 for ~S ..." rule))
	   (setq new-state (clone-state state))
	   (push-rule rule new-state)
	   (push-tree (rule-mother-category rule)
		      (pop-phrase new-state)
		      new-state)
	   (expand-rule-options new-state))
	  (t (if *debug* (format t "~%UNsuccessfully applying rule 1 for ~S ..." rule))))))



(defun lc-expanded-options (options lc)
  (cond ((eq (car options) lc) options)
	((consp (car options))
	 (or (lc-expanded-options (append (car options) (cdr options)) lc)
	     (lc-expanded-options (cdr options) lc)))))

(defsubst rule-2-applicable? (state)
  (and (current-phrase state)
       (current-tree state)
       (not (current-rule-complete? state))
       (eq (node-category (current-phrase state))
	   (current-rule-category state))))

(defun rule-2 (state)
  (if *debug* (format t "~%Applying rule 2 to ~S..." (car (state-rule-stack state))))
  (advance-rule state)
  (right-adjoin-phrase-to-tree (pop-phrase state) (current-tree state))
  (expand-rule-options state)
  t)


(defun expand-rule-options (state)
  (do ((states-to-expand (list state)))
      ((null states-to-expand))
    (let ((st (pop states-to-expand)))
      (cond ((not (consp (current-rule-category st)))
	     (store-processed-state st))
	    (t (let ((option (current-rule-category st)))
		 (advance-rule st)
		 (push (clone-state st) states-to-expand)
		 (if (eq (car option) 'k*)
		     (setf (state-rule-stack st)
			   (cons (append (cdr option) (cons option (car (state-rule-stack st))))
				 (cdr (state-rule-stack st))))
		     (setf (state-rule-stack st)
			   (cons (append option (car (state-rule-stack st)))
				 (cdr (state-rule-stack st)))))
		 (push st states-to-expand)))))))


(defsubst rule-3-applicable? (state)
  (current-rule-complete? state))


(defun rule-3 (state)
    (if *debug* (print "Applying rule 3 ..."))
    (pop-rule state)
    (push-phrase (pop-tree state) state)
    (store-processed-state state)
    t)



(defun parse (node-list &optional target-category)
  (setq *processed-states*
	(list (make-state :phrase-stack node-list
			  :rule-stack   (if target-category
					    (list (list target-category))
					  nil))))
  (setq *completed-states* nil)
  (setq clone-ctr 0)
  (setq max-processed-states 0)
  (setq cycle-ctr 0)
  (setq new-node-ctr 0
	new-state-ctr 0)
  (parse-1))
  

(defun parse-1 (&aux temp)

  (do () ((null *processed-states*))

    (let ((states *processed-states*)
	  (lc     nil)
	  (rules  nil)
	  (r1?    nil)
	  (r2?    nil)
	  (r3?    nil))
      (setq *processed-states* nil)
      (dolist (state states)
	(setq temp (current-phrase state))

	(setq lc    (if (not temp) nil (node-category temp))
	      rules (find-rules-with-left-corner lc))

	(setq r1? (and rules (rule-1-applicable? state))
	      r2? (rule-2-applicable? state)
	      r3? (rule-3-applicable? state))

	(cond ((not (or r1? r2? r3?))
	       )
	      (t (if r1?
		     (rule-1 lc rules (if r2? (clone-state state) state)))
		 (if r2? (rule-2 state))
		 (if r3? (rule-3 state)))))


      (incf cycle-ctr)
      (if (or (>= cycle-ctr cycle-ctr-limit)
	      (>= (length *processed-states*) max-processed-states-limit))
	  (setq *debug* t))

      (when *debug*
	(when *debug2* (dolist (state *processed-states*)
			 (format t "~%Rulestack = ~S" (state-rule-stack state))
			 (dolist (tree (state-tree-stack state))
			   (print-tree tree 0))))
	(read-char)
	(send standard-output :clear-window))
      
      (dolist (state *processed-states*)
	(if (completely-parsed? state)
	    (push state *completed-states*)))
      
      (setq max-processed-states
	    (max max-processed-states
		 (length *processed-states*)))))

;  (dolist (state *completed-states*)
;     (print-tree (car (state-phrase-stack state)) 0))
  )



; for each node X, add the nodes which can immediately dominate it to
; the entry for X and the entries of nodes which X can dominate

(defun calculate-domination ()

  (setq *dominated-nodes* nil)

  (dolist (lc *left-corners*)
    (dolist (rule (get lc :lc-rules))
      (dolist (node (possible-left-daughters (cdr rule)))
	(make-dominated (first rule) node))))

  (let ((*calculated-nodes* nil)
	(*nodes-under-consideration* nil))
    (declare (special *calculated-nodes* *nodes-under-consideration*))
    (dolist (dnode *dominated-nodes*)
      (calculate-full-domination-for dnode)))

  )

(defun calculate-full-domination-for (node)
  (declare (special *calculated-nodes* *nodes-under-consideration*))
  (let ((d-nodes (get node :dominating-nodes)))
    (dolist (n d-nodes)
      (when (and (not (member n *calculated-nodes*))
		 (not (member n *nodes-under-consideration*))) 
	(let ((*nodes-under-consideration* (cons n *nodes-under-consideration*)))
	  (declare (special *nodes-under-consideration*))
	  (calculate-full-domination-for n)
	  (push n *calculated-nodes*)))
      (dolist (n (nodes-which-dominate n))
	(make-dominated n node)))))


(defun make-dominated (d n)
  (if (not (eq d n))
      (cond ((not (memq n *dominated-nodes*))
	     (remprop n :dominating-nodes)
	     (putprop n (list d) :dominating-nodes)
	     (push n *dominated-nodes*))
	    (t (let ((domination-entry (get n :dominating-nodes)))
		 (if domination-entry
		     (if (not (member d domination-entry))
			 (push d (cdr domination-entry)))
		     (putprop n (list d) :dominating-nodes)))))))


(defun check-domination (lc state)
  (let ((nodes (nodes-which-dominate lc))) 
    (let ((rule (car (state-rule-stack state))))
      (if (and rule (not (dolist (ld (possible-left-daughters rule) nil)
			   (if (or (eq ld lc) (memq ld nodes))
			       (return t)))))
	  nil
	  t))))



(defmacro define-grammar (name documentation &body rules)
  `(*define-grammar ',name ,documentation ',rules))


(defun *define-grammar (name documentation rules)
  name
  documentation
  (setq *left-corners* nil)
  (dolist (rule rules)
    (add-rule rule))
  (calculate-domination))


