;;; prec-parse.scm -- do precedence parsing of expressions and patterns
;;;
  author :   John & Sandra
  date    :   04 Feb 1992
;;;
;;;


;;; ==================================================================
;;; Handling for pp-exp-list
;;; ==================================================================

;;; This function is called during the scope phase after all of the
;;; exps in a pp-exp-list have already been walked.  Basically, the
;;; purpose is to turn the original pp-exp-list into something else.
  Look for the section cases first and treat them specially .

;;; Sections are handled by inserting a magic cookie (void) into the
;;; list where the `missing' operand of the section would be and then
;;; making sure the cookie stays at the top.

  Unary minus needs checking to avoid things like a*-a .

(define (massage-pp-exp-list exps)
 (let* ((first-term (car exps))
        (last-term (car (last exps)))
        (type (cond ((infix-var-or-con? first-term) 'section-l)
		    ((infix-var-or-con? last-term) 'section-r)
		    (else 'exp)))
	(exps1 (cond ((eq? type 'section-l)
		      (cons (make void) exps))
		     ((eq? type 'section-r)
		      (append exps (list (make void))))
		     (else exps)))
	(parsed-exp (parse-pp-list '#f exps1)))
   (if (eq? type 'exp)
       parsed-exp
       (if (or (not (app? parsed-exp))
	       (not (app? (app-fn parsed-exp))))
	   (begin
	     (signal-section-precedence-conflict
	      (if (eq? type 'section-l) first-term last-term))
	     (make void))
	   (let ((rhs (app-arg parsed-exp))
		 (op (app-fn (app-fn parsed-exp)))
		 (lhs (app-arg (app-fn parsed-exp))))
	     (if (eq? type 'section-l)
		 (if (void? lhs)
		     (make section-l (op op) (exp rhs))
		     (begin
		       (signal-section-precedence-conflict first-term)
		       (make void)))
		 (if (void? rhs)
		     (make section-r (op op) (exp lhs))
		     (begin
		       (signal-section-precedence-conflict last-term)
		       (make void)))))))))


;;; ==================================================================
;;; Handling for pp-pat-list
;;; ==================================================================

;;; In this case, we have to do an explicit walk of the pattern looking
;;; at all of its subpatterns.
;;;  ** This is a crock - the scope walker needs fixing.

(define (massage-pattern pat)
  (cond ((is-type? 'as-pat pat)
	 (setf (as-pat-pattern pat) (massage-pattern (as-pat-pattern pat)))
	 pat)
	((is-type? 'irr-pat pat)
	 (setf (irr-pat-pattern pat) (massage-pattern (irr-pat-pattern pat)))
	 pat)
	((is-type? 'plus-pat pat)
	 (setf (plus-pat-pattern pat) (massage-pattern (plus-pat-pattern pat)))
	 pat)
	((is-type? 'pcon pat)
	 (when (eq? (pcon-con pat) *undefined-def*)
	   (setf (pcon-con pat) (lookup-toplevel-name (pcon-name pat))))
	 (setf (pcon-pats pat) (massage-pattern-list (pcon-pats pat)))
	 pat)
	((is-type? 'list-pat pat)
	 (setf (list-pat-pats pat) (massage-pattern-list (list-pat-pats pat)))
	 pat)
	((is-type? 'pp-pat-list pat)
	 (parse-pp-list '#t (massage-pattern-list (pp-pat-list-pats pat))))
	(else
	 pat)))

(define (massage-pattern-list pats)
  (map (function massage-pattern) pats))


;;; ==================================================================
;;; Shared support
;;; ==================================================================

;;; This is the main routine.

(define (parse-pp-list pattern? l)
  (mlet (((stack terms) (push-pp-stack '() l)))
    (pp-parse-next-term pattern? stack terms)))

(define (pp-parse-next-term pattern? stack terms)
  (if (null? terms)
      (reduce-complete-stack pattern? stack)
      (let ((stack (reduce-stronger-ops pattern? stack (car terms))))
	(mlet (((stack terms)
		(push-pp-stack (cons (car terms) stack) (cdr terms))))
	   (pp-parse-next-term pattern? stack terms)))))

(define (reduce-complete-stack pattern? stack)
  (if (pp-stack-op-empty? stack)
      (car stack)
      (reduce-complete-stack pattern? (reduce-pp-stack pattern? stack))))

(define (reduce-pp-stack pattern? stack)
  (let ((term (car stack))
	(op (cadr stack)))
    (if pattern?
	(cond ((pp-pat-plus? op)
	       (let ((lhs (caddr stack)))
		 (cond ((or (not (const-pat? term))
			    (and (not (var-pat? lhs))
				 (not (wildcard-pat? lhs))))
			(signal-plus-precedence-conflict term)
			(cddr stack))
		       (else
			(cons (make plus-pat (pattern lhs)
				             (k (integer-const-value
						 (const-pat-value term))))
			      (cdddr stack))))))
	      ((pp-pat-negated? op)
	       (cond ((const-pat? term)
		      (let ((v (const-pat-value term)))
			(if (integer-const? v)
			    (setf (integer-const-value v)
				  (- (integer-const-value v)))
			    (setf (float-const-numerator v)
				  (- (float-const-numerator v)))))
		      (cons term (cddr stack)))
		     (else
		      (signal-minus-precedence-conflict term)
		      (cons term (cddr stack)))))
	      (else
	       (setf (pcon-pats op) (list (caddr stack) term))
	       (cons op (cdddr stack))))
	(cond ((negate? op)
	       (cons (**app (**var/def (core-symbol "negate")) term)
		     (cddr stack)))
	      (else
	       (cons (**app op (caddr stack) term) (cdddr stack)))))))

(define (pp-stack-op-empty? stack)
  (null? (cdr stack)))

(define (top-stack-op stack)
  (cadr stack))

(define (push-pp-stack stack terms)
  (let ((term (car terms)))
    (if (or (negate? term) (pp-pat-negated? term))
	(begin
	  (when (and stack (stronger-op? (car stack) term))
	      (unary-minus-prec-conflict term))
	  (push-pp-stack (cons term stack) (cdr terms)))
	(values (cons term stack) (cdr terms)))))

(define (reduce-stronger-ops pattern? stack op)
  (cond ((pp-stack-op-empty? stack) stack)
	((stronger-op? (top-stack-op stack) op)
	 (reduce-stronger-ops pattern? (reduce-pp-stack pattern? stack) op))
	(else stack)))

(define (stronger-op? op1 op2)
  (let ((fixity1 (get-op-fixity op1))
	(fixity2 (get-op-fixity op2)))
    (cond ((> (fixity-precedence fixity1) (fixity-precedence fixity2))
	   '#t)
	  ((< (fixity-precedence fixity1) (fixity-precedence fixity2))
	   '#f)
	  (else
	   (let ((a1 (fixity-associativity fixity1))
		 (a2 (fixity-associativity fixity2)))
	     (if (eq? a1 a2)
		 (cond ((eq? a1 'l)
			'#t)
		       ((eq? a1 'r)
			'#f)
		       (else
			(signal-precedence-conflict op1 op2)
			'#t))
		 (begin
		   (signal-precedence-conflict op1 op2)
		   '#t))))
	  )))
	     
(define (get-op-fixity op)
  (cond ((var-ref? op)
	 (pp-get-var-fixity (var-ref-var op)))
	((con-ref? op)
	 (pp-get-con-fixity (con-ref-con op)))
	((pcon? op)
	 (pp-get-con-fixity (pcon-con op)))
	((or (negate? op) (pp-pat-negated? op))
	 (pp-get-var-fixity (core-symbol "-")))
	((pp-pat-plus? op)
	 (pp-get-var-fixity (core-symbol "+")))
	(else
	 (error "Bad op ~s in pp-parse." op))))

(define (pp-get-var-fixity def)
  (if (eq? (var-fixity def) '#f)
      default-fixity
      (var-fixity def)))

(define (pp-get-con-fixity def)
  (if (eq? (con-fixity def) '#f)
      default-fixity
      (con-fixity def)))

;;; Error handlers

(define (signal-section-precedence-conflict op)
  (phase-error 'section-precedence-conflict
    "Operators in section body have lower precedence than section operator ~A."
   op))

(define (signal-precedence-conflict op1 op2)
  (phase-error 'precedence-conflict
    "The operators ~s and ~s appear consecutively, but they have the same~%~
     precedence and are not either both left or both right associative.~%
     You must add parentheses to avoid a precedence conflict."
    op1 op2))

(define (signal-plus-precedence-conflict term)
  (phase-error 'plus-precedence-conflict
    "You need to put parentheses around the plus-pattern ~a~%~
     to avoid a precedence conflict."
    term))

(define (signal-minus-precedence-conflict arg)
  (phase-error 'minus-precedence-conflict
    "You need to put parentheses around the negative literal ~a~%~
     to avoid a precedence conflict."
    arg))

(define (unary-minus-prec-conflict arg)
  (recoverable-error 'minus-precedence-conflict
     "Operator ~A too strong for unary minus - add parens please!~%"
     arg))


 prec-parse.scm -- do precedence parsing of expressions and patterns



 ==================================================================
 Handling for pp-exp-list
 ==================================================================
 This function is called during the scope phase after all of the
 exps in a pp-exp-list have already been walked.  Basically, the
 purpose is to turn the original pp-exp-list into something else.
 Sections are handled by inserting a magic cookie (void) into the
 list where the `missing' operand of the section would be and then
 making sure the cookie stays at the top.
 ==================================================================
 Handling for pp-pat-list
 ==================================================================
 In this case, we have to do an explicit walk of the pattern looking
 at all of its subpatterns.
  ** This is a crock - the scope walker needs fixing.
 ==================================================================
 Shared support
 ==================================================================
 This is the main routine.
 Error handlers
  author :   John & Sandra
  date    :   04 Feb 1992



  Look for the section cases first and treat them specially .


  Unary minus needs checking to avoid things like a*-a .

(define (massage-pp-exp-list exps)
 (let* ((first-term (car exps))
        (last-term (car (last exps)))
        (type (cond ((infix-var-or-con? first-term) 'section-l)
		    ((infix-var-or-con? last-term) 'section-r)
		    (else 'exp)))
	(exps1 (cond ((eq? type 'section-l)
		      (cons (make void) exps))
		     ((eq? type 'section-r)
		      (append exps (list (make void))))
		     (else exps)))
	(parsed-exp (parse-pp-list '#f exps1)))
   (if (eq? type 'exp)
       parsed-exp
       (if (or (not (app? parsed-exp))
	       (not (app? (app-fn parsed-exp))))
	   (begin
	     (signal-section-precedence-conflict
	      (if (eq? type 'section-l) first-term last-term))
	     (make void))
	   (let ((rhs (app-arg parsed-exp))
		 (op (app-fn (app-fn parsed-exp)))
		 (lhs (app-arg (app-fn parsed-exp))))
	     (if (eq? type 'section-l)
		 (if (void? lhs)
		     (make section-l (op op) (exp rhs))
		     (begin
		       (signal-section-precedence-conflict first-term)
		       (make void)))
		 (if (void? rhs)
		     (make section-r (op op) (exp lhs))
		     (begin
		       (signal-section-precedence-conflict last-term)
		       (make void)))))))))




(define (massage-pattern pat)
  (cond ((is-type? 'as-pat pat)
	 (setf (as-pat-pattern pat) (massage-pattern (as-pat-pattern pat)))
	 pat)
	((is-type? 'irr-pat pat)
	 (setf (irr-pat-pattern pat) (massage-pattern (irr-pat-pattern pat)))
	 pat)
	((is-type? 'plus-pat pat)
	 (setf (plus-pat-pattern pat) (massage-pattern (plus-pat-pattern pat)))
	 pat)
	((is-type? 'pcon pat)
	 (when (eq? (pcon-con pat) *undefined-def*)
	   (setf (pcon-con pat) (lookup-toplevel-name (pcon-name pat))))
	 (setf (pcon-pats pat) (massage-pattern-list (pcon-pats pat)))
	 pat)
	((is-type? 'list-pat pat)
	 (setf (list-pat-pats pat) (massage-pattern-list (list-pat-pats pat)))
	 pat)
	((is-type? 'pp-pat-list pat)
	 (parse-pp-list '#t (massage-pattern-list (pp-pat-list-pats pat))))
	(else
	 pat)))

(define (massage-pattern-list pats)
  (map (function massage-pattern) pats))




(define (parse-pp-list pattern? l)
  (mlet (((stack terms) (push-pp-stack '() l)))
    (pp-parse-next-term pattern? stack terms)))

(define (pp-parse-next-term pattern? stack terms)
  (if (null? terms)
      (reduce-complete-stack pattern? stack)
      (let ((stack (reduce-stronger-ops pattern? stack (car terms))))
	(mlet (((stack terms)
		(push-pp-stack (cons (car terms) stack) (cdr terms))))
	   (pp-parse-next-term pattern? stack terms)))))

(define (reduce-complete-stack pattern? stack)
  (if (pp-stack-op-empty? stack)
      (car stack)
      (reduce-complete-stack pattern? (reduce-pp-stack pattern? stack))))

(define (reduce-pp-stack pattern? stack)
  (let ((term (car stack))
	(op (cadr stack)))
    (if pattern?
	(cond ((pp-pat-plus? op)
	       (let ((lhs (caddr stack)))
		 (cond ((or (not (const-pat? term))
			    (and (not (var-pat? lhs))
				 (not (wildcard-pat? lhs))))
			(signal-plus-precedence-conflict term)
			(cddr stack))
		       (else
			(cons (make plus-pat (pattern lhs)
				             (k (integer-const-value
						 (const-pat-value term))))
			      (cdddr stack))))))
	      ((pp-pat-negated? op)
	       (cond ((const-pat? term)
		      (let ((v (const-pat-value term)))
			(if (integer-const? v)
			    (setf (integer-const-value v)
				  (- (integer-const-value v)))
			    (setf (float-const-numerator v)
				  (- (float-const-numerator v)))))
		      (cons term (cddr stack)))
		     (else
		      (signal-minus-precedence-conflict term)
		      (cons term (cddr stack)))))
	      (else
	       (setf (pcon-pats op) (list (caddr stack) term))
	       (cons op (cdddr stack))))
	(cond ((negate? op)
	       (cons (**app (**var/def (core-symbol "negate")) term)
		     (cddr stack)))
	      (else
	       (cons (**app op (caddr stack) term) (cdddr stack)))))))

(define (pp-stack-op-empty? stack)
  (null? (cdr stack)))

(define (top-stack-op stack)
  (cadr stack))

(define (push-pp-stack stack terms)
  (let ((term (car terms)))
    (if (or (negate? term) (pp-pat-negated? term))
	(begin
	  (when (and stack (stronger-op? (car stack) term))
	      (unary-minus-prec-conflict term))
	  (push-pp-stack (cons term stack) (cdr terms)))
	(values (cons term stack) (cdr terms)))))

(define (reduce-stronger-ops pattern? stack op)
  (cond ((pp-stack-op-empty? stack) stack)
	((stronger-op? (top-stack-op stack) op)
	 (reduce-stronger-ops pattern? (reduce-pp-stack pattern? stack) op))
	(else stack)))

(define (stronger-op? op1 op2)
  (let ((fixity1 (get-op-fixity op1))
	(fixity2 (get-op-fixity op2)))
    (cond ((> (fixity-precedence fixity1) (fixity-precedence fixity2))
	   '#t)
	  ((< (fixity-precedence fixity1) (fixity-precedence fixity2))
	   '#f)
	  (else
	   (let ((a1 (fixity-associativity fixity1))
		 (a2 (fixity-associativity fixity2)))
	     (if (eq? a1 a2)
		 (cond ((eq? a1 'l)
			'#t)
		       ((eq? a1 'r)
			'#f)
		       (else
			(signal-precedence-conflict op1 op2)
			'#t))
		 (begin
		   (signal-precedence-conflict op1 op2)
		   '#t))))
	  )))
	     
(define (get-op-fixity op)
  (cond ((var-ref? op)
	 (pp-get-var-fixity (var-ref-var op)))
	((con-ref? op)
	 (pp-get-con-fixity (con-ref-con op)))
	((pcon? op)
	 (pp-get-con-fixity (pcon-con op)))
	((or (negate? op) (pp-pat-negated? op))
	 (pp-get-var-fixity (core-symbol "-")))
	((pp-pat-plus? op)
	 (pp-get-var-fixity (core-symbol "+")))
	(else
	 (error "Bad op ~s in pp-parse." op))))

(define (pp-get-var-fixity def)
  (if (eq? (var-fixity def) '#f)
      default-fixity
      (var-fixity def)))

(define (pp-get-con-fixity def)
  (if (eq? (con-fixity def) '#f)
      default-fixity
      (con-fixity def)))


(define (signal-section-precedence-conflict op)
  (phase-error 'section-precedence-conflict
    "Operators in section body have lower precedence than section operator ~A."
   op))

(define (signal-precedence-conflict op1 op2)
  (phase-error 'precedence-conflict
    "The operators ~s and ~s appear consecutively, but they have the same~%~
     precedence and are not either both left or both right associative.~%
     You must add parentheses to avoid a precedence conflict."
    op1 op2))

(define (signal-plus-precedence-conflict term)
  (phase-error 'plus-precedence-conflict
    "You need to put parentheses around the plus-pattern ~a~%~
     to avoid a precedence conflict."
    term))

(define (signal-minus-precedence-conflict arg)
  (phase-error 'minus-precedence-conflict
    "You need to put parentheses around the negative literal ~a~%~
     to avoid a precedence conflict."
    arg))

(define (unary-minus-prec-conflict arg)
  (recoverable-error 'minus-precedence-conflict
     "Operator ~A too strong for unary minus - add parens please!~%"
     arg))

