git.fiddlerwoaroof.com
flic/ast-to-flic.scm
4e987026
 ;;; 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))