;;; 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))