summaryrefslogtreecommitdiff
path: root/flic/ast-to-flic.scm
blob: d756723b4e8ef172d69f8ea2699c2100f130ddd1 (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
;;; 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))