git.fiddlerwoaroof.com
Raw Blame History
;;; ast-to-flic.scm -- convert AST to flic structures.
;;;
;;; author :  Sandra Loosemore
;;; date   :  3 Apr 1992
;;;
;;;


;;; ====================================================================
;;; Support
;;; ====================================================================


(define-walker ast-to-flic ast-td-ast-to-flic-walker)

(define-local-syntax (define-ast-to-flic ast-type lambda-list . body)
  `(define-walker-method ast-to-flic ,ast-type ,lambda-list ,@body))

(define (ast-to-flic big-let)
  (ast-to-flic-let-aux (let-decls big-let) (make-flic-void) '#t))

(define (ast-to-flic-1 ast-node)
  (call-walker ast-to-flic ast-node))

(define (ast-to-flic/list l)
  (map (function ast-to-flic-1) l))

(define (init-flic-var var value toplevel?)
  (setf (var-value var) value)
  (setf (var-toplevel? var) toplevel?)
  (setf (var-simple? var)
	(and value
	     (or (is-type? 'flic-const value)
		 (is-type? 'flic-pack value))))
  (setf (var-strict? var) '#f)
  ;; Remember the strictness annotation.
  (let ((strictness-ann (lookup-annotation var '|Strictness|)))
    (setf (var-strictness var)
	  (if strictness-ann
	      (adjust-annotated-strictness var
		(parse-strictness (car (annotation-value-args strictness-ann))))
	      '#f)))
  ;; If the variable has an inline annotation, rewrite its value
  ;; from var = value
  ;; to   var = let temp = value in temp
  ;; (Necessary for inlining recursive definitions.)
  (let ((inline-ann (lookup-annotation var '|Inline|)))
    (when inline-ann
      (setf (var-force-inline? var) '#t)
      (setf (var-value var) (wrap-with-let var value))))
  var)

(define (wrap-with-let var value)
  (let ((temp  (copy-temp-var (def-name var))))
    (init-flic-var temp (copy-flic value (list (cons var temp))) '#f)
    (make-flic-let (list temp) (make-flic-ref temp) '#t)))


;;; ====================================================================
;;; ast expression structs
;;; ====================================================================


(define-ast-to-flic lambda (object)
  (make-flic-lambda
    (map (lambda (pat)
	   (init-flic-var 
	     (cond ((var-pat? pat)
		    (var-ref-var (var-pat-var pat)))
		   (else
		    (error "Bad lambda pattern: ~s." pat)))
	     '#f
	     '#f))
	 (lambda-pats object))
    (ast-to-flic-1 (lambda-body object))))


;;; For LET, the CFN has turned all of the definitions into
;;; simple assignments to a variable.  The dependency analyzer
;;; adds recursive-decl-groups for things which need to be bound
;;; with LETREC.

(define-ast-to-flic let (object)
  (ast-to-flic-let-aux
    (let-decls object)
    (ast-to-flic-1 (let-body object))
    '#f))

(define (ast-to-flic-let-aux decls body toplevel?)
  (multiple-value-bind (bindings newbody)
      (ast-to-flic-bindings decls body toplevel?)
    (if (null? bindings)
	newbody
	(make-flic-let bindings newbody toplevel?))))

(define (ast-to-flic-bindings decls body toplevel?)
  (if (null? decls)
      (values '() body)
      (multiple-value-bind (bindings newbody)
	  (ast-to-flic-bindings (cdr decls) body toplevel?)
	(cond ((is-type? 'valdef (car decls))
	       ;; Continue collecting bindings.
	       (let* ((decl  (car decls))
		      (pat   (valdef-lhs decl))
		      (exp   (single-definition-rhs decl)))
		 (values
		  (cond ((var-pat? pat)
			 (cons
			   (init-flic-var
			    (var-ref-var (var-pat-var pat))
			    (ast-to-flic-1 exp)
			    toplevel?)
			   bindings))
			(else
			 (error "Definition has invalid pattern: ~s." decl)))
		  newbody)))
	      ((not (is-type? 'recursive-decl-group (car decls)))
	       (error "Decl has weird value: ~s." (car decls)))
	      (toplevel?
	       ;; We don't do any of this mess with top level bindings.
	       ;; Turn it into one big letrec.
	       (multiple-value-bind (more-bindings newerbody)
		   (ast-to-flic-bindings
		     (recursive-decl-group-decls (car decls))
		     newbody
		     toplevel?)
		 (values (nconc more-bindings bindings)
			 newerbody)))
	      (else
	       ;; Otherwise, turn remaining bindings into a nested
	       ;; let or letrec, and put that in the body of a new
	       ;; letrec.
	       (multiple-value-bind (more-bindings newerbody)
		   (ast-to-flic-bindings
		     (recursive-decl-group-decls (car decls))
		     (if (null? bindings)
			 newbody
			 (make-flic-let bindings newbody '#f))
		     toplevel?)
		 (values
		   '()
		   (if (null? more-bindings)
		       newerbody
		       (make-flic-let more-bindings newerbody '#t)))))
	      ))))


(define (single-definition-rhs decl)
  (let* ((def-list  (valdef-definitions decl))
	 (def       (car def-list))
	 (rhs-list  (single-fun-def-rhs-list def))
	 (rhs       (car rhs-list)))
    ;; All of this error checking could be omitted for efficiency, since
    ;; none of these conditions are supposed to happen anyway.
    (cond ((not (null? (cdr def-list)))
	   (error "Decl has multiple definitions: ~s." decl))
	  ((not (null? (single-fun-def-where-decls def)))
	   (error "Definition has non-null where-decls list: ~s." decl))
	  ((not (null? (cdr rhs-list)))
	   (error "Definition has multiple right-hand-sides: ~s." decl))
	  ((not (is-type? 'omitted-guard (guarded-rhs-guard rhs)))
	   (error "Definition has a guard: ~s." decl)))
    (guarded-rhs-rhs rhs)))



;;; These are all straightforward translations.

(define-ast-to-flic if (object)
  (make-flic-if
    (ast-to-flic-1 (if-test-exp object))
    (ast-to-flic-1 (if-then-exp object))
    (ast-to-flic-1 (if-else-exp object))))

(define-ast-to-flic case-block (object)
  (make-flic-case-block
    (case-block-block-name object)
    (ast-to-flic/list (case-block-exps object))))

(define-ast-to-flic return-from (object)
  (make-flic-return-from
    (return-from-block-name object)
    (ast-to-flic-1 (return-from-exp object))))

(define-ast-to-flic and-exp (object)
  (make-flic-and (ast-to-flic/list (and-exp-exps object))))
  

;;; Applications.  Uncurry here.  It's more convenient to do the
;;; optimizer on fully uncurried applications.  After the optimizer
;;; has run, all applications are adjusted based on observed arity
;;; of the functions and the saturated? flag is set correctly.

(define-ast-to-flic app (object)
  (ast-to-flic-app-aux object '()))

(define (ast-to-flic-app-aux object args)
  (if (is-type? 'app object)
      (ast-to-flic-app-aux
        (app-fn object)
	(cons (ast-to-flic-1 (app-arg object)) args))
      (make-flic-app (ast-to-flic-1 object) args '#f)))


;;; References

(define-ast-to-flic var-ref (object)
  (make-flic-ref (var-ref-var object)))

(define-ast-to-flic con-ref (object)
  (make-flic-pack (con-ref-con object)))


;;; Constants

(define-ast-to-flic integer-const (object)
  (make-flic-const (integer-const-value object)))


;;; We should probably add a type field to flic-const but at the moment
;;; I'll force the value to be a list of numerator, denominator.

(define-ast-to-flic float-const (object)
  (let ((e (float-const-exponent object))
	(n (float-const-numerator object))
	(d (float-const-denominator object)))
    (make-flic-const
     (if (> e 0)
	 (list (* n (expt 10 e)) d)
	 (list n (* d (expt 10 (- e))))))))

(define-ast-to-flic char-const (object)
  (make-flic-const (char-const-value object)))


(define-ast-to-flic string-const (object)
  (let ((value  (string-const-value object)))
    (if (equal? value "")
	(make-flic-pack (core-symbol "Nil"))
	(make-flic-const value))))



;;; Random stuff

(define-ast-to-flic con-number (object)
  (make-flic-con-number
    (con-number-type object)
    (ast-to-flic-1 (con-number-value object))))

(define-ast-to-flic sel (object)
  (make-flic-sel
    (sel-constructor object)
    (sel-slot object)
    (ast-to-flic-1 (sel-value object))))

(define-ast-to-flic is-constructor (object)
  (make-flic-is-constructor
    (is-constructor-constructor object)
    (ast-to-flic-1 (is-constructor-value object))))

(define-ast-to-flic void (object)
  (declare (ignore object))
  (make-flic-void))


;;; This hack make strictness annotations work.  It adds #t's which correspond
;;; to the strictness of the dict params.

(define (adjust-annotated-strictness v s)
  (let* ((ty (var-type v))
	 (c (gtype-context ty)))
    (dolist (c1 c)
      (dolist (c2 c1)
        (declare (ignorable c2))
        (push '#t s)))
    s))