Remove with-fluids; replaced by with-fluid* and inlined push-fluid primops
[bpt/guile.git] / module / language / tree-il.scm
1 ;;;; Copyright (C) 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
2 ;;;;
3 ;;;; This library is free software; you can redistribute it and/or
4 ;;;; modify it under the terms of the GNU Lesser General Public
5 ;;;; License as published by the Free Software Foundation; either
6 ;;;; version 3 of the License, or (at your option) any later version.
7 ;;;;
8 ;;;; This library is distributed in the hope that it will be useful,
9 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
10 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 ;;;; Lesser General Public License for more details.
12 ;;;;
13 ;;;; You should have received a copy of the GNU Lesser General Public
14 ;;;; License along with this library; if not, write to the Free Software
15 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 ;;;;
17 \f
18
19 (define-module (language tree-il)
20 #:use-module (srfi srfi-1)
21 #:use-module (srfi srfi-11)
22 #:use-module (ice-9 match)
23 #:use-module (system base syntax)
24 #:export (tree-il-src
25
26 <void> void? make-void void-src
27 <const> const? make-const const-src const-exp
28 <primitive-ref> primitive-ref? make-primitive-ref primitive-ref-src primitive-ref-name
29 <lexical-ref> lexical-ref? make-lexical-ref lexical-ref-src lexical-ref-name lexical-ref-gensym
30 <lexical-set> lexical-set? make-lexical-set lexical-set-src lexical-set-name lexical-set-gensym lexical-set-exp
31 <module-ref> module-ref? make-module-ref module-ref-src module-ref-mod module-ref-name module-ref-public?
32 <module-set> module-set? make-module-set module-set-src module-set-mod module-set-name module-set-public? module-set-exp
33 <toplevel-ref> toplevel-ref? make-toplevel-ref toplevel-ref-src toplevel-ref-name
34 <toplevel-set> toplevel-set? make-toplevel-set toplevel-set-src toplevel-set-name toplevel-set-exp
35 <toplevel-define> toplevel-define? make-toplevel-define toplevel-define-src toplevel-define-name toplevel-define-exp
36 <conditional> conditional? make-conditional conditional-src conditional-test conditional-consequent conditional-alternate
37 <call> call? make-call call-src call-proc call-args
38 <primcall> primcall? make-primcall primcall-src primcall-name primcall-args
39 <seq> seq? make-seq seq-src seq-head seq-tail
40 <lambda> lambda? make-lambda lambda-src lambda-meta lambda-body
41 <lambda-case> lambda-case? make-lambda-case lambda-case-src
42 lambda-case-req lambda-case-opt lambda-case-rest lambda-case-kw
43 lambda-case-inits lambda-case-gensyms
44 lambda-case-body lambda-case-alternate
45 <let> let? make-let let-src let-names let-gensyms let-vals let-body
46 <letrec> letrec? make-letrec letrec-src letrec-in-order? letrec-names letrec-gensyms letrec-vals letrec-body
47 <fix> fix? make-fix fix-src fix-names fix-gensyms fix-vals fix-body
48 <let-values> let-values? make-let-values let-values-src let-values-exp let-values-body
49 <prompt> prompt? make-prompt prompt-src prompt-tag prompt-body prompt-handler
50 <abort> abort? make-abort abort-src abort-tag abort-args abort-tail
51
52 list->seq
53
54 parse-tree-il
55 unparse-tree-il
56 tree-il->scheme
57
58 tree-il-fold
59 make-tree-il-folder
60 post-order
61 pre-order
62
63 tree-il=?
64 tree-il-hash))
65
66 (define (print-tree-il exp port)
67 (format port "#<tree-il ~S>" (unparse-tree-il exp)))
68
69 (define-syntax borrow-core-vtables
70 (lambda (x)
71 (syntax-case x ()
72 ((_)
73 (let lp ((n 0) (out '()))
74 (if (< n (vector-length %expanded-vtables))
75 (lp (1+ n)
76 (let* ((vtable (vector-ref %expanded-vtables n))
77 (stem (struct-ref vtable (+ vtable-offset-user 0)))
78 (fields (struct-ref vtable (+ vtable-offset-user 2)))
79 (sfields (map
80 (lambda (f) (datum->syntax x f))
81 fields))
82 (type (datum->syntax x (symbol-append '< stem '>)))
83 (ctor (datum->syntax x (symbol-append 'make- stem)))
84 (pred (datum->syntax x (symbol-append stem '?))))
85 (let lp ((n 0) (fields fields)
86 (out (cons*
87 #`(define (#,ctor #,@sfields)
88 (make-struct #,type 0 #,@sfields))
89 #`(define (#,pred x)
90 (and (struct? x)
91 (eq? (struct-vtable x) #,type)))
92 #`(struct-set! #,type vtable-index-printer
93 print-tree-il)
94 #`(define #,type
95 (vector-ref %expanded-vtables #,n))
96 out)))
97 (if (null? fields)
98 out
99 (lp (1+ n)
100 (cdr fields)
101 (let ((acc (datum->syntax
102 x (symbol-append stem '- (car fields)))))
103 (cons #`(define #,acc
104 (make-procedure-with-setter
105 (lambda (x) (struct-ref x #,n))
106 (lambda (x v) (struct-set! x #,n v))))
107 out)))))))
108 #`(begin #,@(reverse out))))))))
109
110 (borrow-core-vtables)
111
112 ;; (<void>)
113 ;; (<const> exp)
114 ;; (<primitive-ref> name)
115 ;; (<lexical-ref> name gensym)
116 ;; (<lexical-set> name gensym exp)
117 ;; (<module-ref> mod name public?)
118 ;; (<module-set> mod name public? exp)
119 ;; (<toplevel-ref> name)
120 ;; (<toplevel-set> name exp)
121 ;; (<toplevel-define> name exp)
122 ;; (<conditional> test consequent alternate)
123 ;; (<call> proc args)
124 ;; (<primcall> name args)
125 ;; (<seq> head tail)
126 ;; (<lambda> meta body)
127 ;; (<lambda-case> req opt rest kw inits gensyms body alternate)
128 ;; (<let> names gensyms vals body)
129 ;; (<letrec> in-order? names gensyms vals body)
130
131 (define-type (<tree-il> #:common-slots (src) #:printer print-tree-il)
132 (<fix> names gensyms vals body)
133 (<let-values> exp body)
134 (<prompt> tag body handler)
135 (<abort> tag args tail))
136
137 \f
138
139 ;; A helper.
140 (define (list->seq loc exps)
141 (if (null? (cdr exps))
142 (car exps)
143 (make-seq loc (car exps) (list->seq #f (cdr exps)))))
144
145 \f
146
147 (define (location x)
148 (and (pair? x)
149 (let ((props (source-properties x)))
150 (and (pair? props) props))))
151
152 (define (parse-tree-il exp)
153 (let ((loc (location exp))
154 (retrans (lambda (x) (parse-tree-il x))))
155 (match exp
156 (('void)
157 (make-void loc))
158
159 (('call proc . args)
160 (make-call loc (retrans proc) (map retrans args)))
161
162 (('primcall name . args)
163 (make-primcall loc name (map retrans args)))
164
165 (('if test consequent alternate)
166 (make-conditional loc (retrans test) (retrans consequent) (retrans alternate)))
167
168 (('primitive (and name (? symbol?)))
169 (make-primitive-ref loc name))
170
171 (('lexical (and name (? symbol?)))
172 (make-lexical-ref loc name name))
173
174 (('lexical (and name (? symbol?)) (and sym (? symbol?)))
175 (make-lexical-ref loc name sym))
176
177 (('set! ('lexical (and name (? symbol?))) exp)
178 (make-lexical-set loc name name (retrans exp)))
179
180 (('set! ('lexical (and name (? symbol?)) (and sym (? symbol?))) exp)
181 (make-lexical-set loc name sym (retrans exp)))
182
183 (('@ ((and mod (? symbol?)) ...) (and name (? symbol?)))
184 (make-module-ref loc mod name #t))
185
186 (('set! ('@ ((and mod (? symbol?)) ...) (and name (? symbol?))) exp)
187 (make-module-set loc mod name #t (retrans exp)))
188
189 (('@@ ((and mod (? symbol?)) ...) (and name (? symbol?)))
190 (make-module-ref loc mod name #f))
191
192 (('set! ('@@ ((and mod (? symbol?)) ...) (and name (? symbol?))) exp)
193 (make-module-set loc mod name #f (retrans exp)))
194
195 (('toplevel (and name (? symbol?)))
196 (make-toplevel-ref loc name))
197
198 (('set! ('toplevel (and name (? symbol?))) exp)
199 (make-toplevel-set loc name (retrans exp)))
200
201 (('define (and name (? symbol?)) exp)
202 (make-toplevel-define loc name (retrans exp)))
203
204 (('lambda meta body)
205 (make-lambda loc meta (retrans body)))
206
207 (('lambda-case ((req opt rest kw inits gensyms) body) alternate)
208 (make-lambda-case loc req opt rest kw
209 (map retrans inits) gensyms
210 (retrans body)
211 (and=> alternate retrans)))
212
213 (('lambda-case ((req opt rest kw inits gensyms) body))
214 (make-lambda-case loc req opt rest kw
215 (map retrans inits) gensyms
216 (retrans body)
217 #f))
218
219 (('const exp)
220 (make-const loc exp))
221
222 (('seq head tail)
223 (make-seq loc (retrans head) (retrans tail)))
224
225 ;; Convenience.
226 (('begin . exps)
227 (list->seq loc (map retrans exps)))
228
229 (('let names gensyms vals body)
230 (make-let loc names gensyms (map retrans vals) (retrans body)))
231
232 (('letrec names gensyms vals body)
233 (make-letrec loc #f names gensyms (map retrans vals) (retrans body)))
234
235 (('letrec* names gensyms vals body)
236 (make-letrec loc #t names gensyms (map retrans vals) (retrans body)))
237
238 (('fix names gensyms vals body)
239 (make-fix loc names gensyms (map retrans vals) (retrans body)))
240
241 (('let-values exp body)
242 (make-let-values loc (retrans exp) (retrans body)))
243
244 (('prompt tag body handler)
245 (make-prompt loc (retrans tag) (retrans body) (retrans handler)))
246
247 (('abort tag args tail)
248 (make-abort loc (retrans tag) (map retrans args) (retrans tail)))
249
250 (else
251 (error "unrecognized tree-il" exp)))))
252
253 (define (unparse-tree-il tree-il)
254 (match tree-il
255 (($ <void> src)
256 '(void))
257
258 (($ <call> src proc args)
259 `(call ,(unparse-tree-il proc) ,@(map unparse-tree-il args)))
260
261 (($ <primcall> src name args)
262 `(primcall ,name ,@(map unparse-tree-il args)))
263
264 (($ <conditional> src test consequent alternate)
265 `(if ,(unparse-tree-il test)
266 ,(unparse-tree-il consequent)
267 ,(unparse-tree-il alternate)))
268
269 (($ <primitive-ref> src name)
270 `(primitive ,name))
271
272 (($ <lexical-ref> src name gensym)
273 `(lexical ,name ,gensym))
274
275 (($ <lexical-set> src name gensym exp)
276 `(set! (lexical ,name ,gensym) ,(unparse-tree-il exp)))
277
278 (($ <module-ref> src mod name public?)
279 `(,(if public? '@ '@@) ,mod ,name))
280
281 (($ <module-set> src mod name public? exp)
282 `(set! (,(if public? '@ '@@) ,mod ,name) ,(unparse-tree-il exp)))
283
284 (($ <toplevel-ref> src name)
285 `(toplevel ,name))
286
287 (($ <toplevel-set> src name exp)
288 `(set! (toplevel ,name) ,(unparse-tree-il exp)))
289
290 (($ <toplevel-define> src name exp)
291 `(define ,name ,(unparse-tree-il exp)))
292
293 (($ <lambda> src meta body)
294 (if body
295 `(lambda ,meta ,(unparse-tree-il body))
296 `(lambda ,meta (lambda-case))))
297
298 (($ <lambda-case> src req opt rest kw inits gensyms body alternate)
299 `(lambda-case ((,req ,opt ,rest ,kw ,(map unparse-tree-il inits) ,gensyms)
300 ,(unparse-tree-il body))
301 . ,(if alternate (list (unparse-tree-il alternate)) '())))
302
303 (($ <const> src exp)
304 `(const ,exp))
305
306 (($ <seq> src head tail)
307 `(seq ,(unparse-tree-il head) ,(unparse-tree-il tail)))
308
309 (($ <let> src names gensyms vals body)
310 `(let ,names ,gensyms ,(map unparse-tree-il vals) ,(unparse-tree-il body)))
311
312 (($ <letrec> src in-order? names gensyms vals body)
313 `(,(if in-order? 'letrec* 'letrec) ,names ,gensyms
314 ,(map unparse-tree-il vals) ,(unparse-tree-il body)))
315
316 (($ <fix> src names gensyms vals body)
317 `(fix ,names ,gensyms ,(map unparse-tree-il vals) ,(unparse-tree-il body)))
318
319 (($ <let-values> src exp body)
320 `(let-values ,(unparse-tree-il exp) ,(unparse-tree-il body)))
321
322 (($ <prompt> src tag body handler)
323 `(prompt ,(unparse-tree-il tag)
324 ,(unparse-tree-il body)
325 ,(unparse-tree-il handler)))
326
327 (($ <abort> src tag args tail)
328 `(abort ,(unparse-tree-il tag) ,(map unparse-tree-il args)
329 ,(unparse-tree-il tail)))))
330
331 (define* (tree-il->scheme e #:optional (env #f) (opts '()))
332 (values ((@ (language scheme decompile-tree-il)
333 decompile-tree-il)
334 e env opts)))
335
336 \f
337 (define-syntax-rule (make-tree-il-folder seed ...)
338 (lambda (tree down up seed ...)
339 (define (fold-values proc exps seed ...)
340 (if (null? exps)
341 (values seed ...)
342 (let-values (((seed ...) (proc (car exps) seed ...)))
343 (fold-values proc (cdr exps) seed ...))))
344 (let foldts ((tree tree) (seed seed) ...)
345 (let*-values
346 (((seed ...) (down tree seed ...))
347 ((seed ...)
348 (match tree
349 (($ <lexical-set> src name gensym exp)
350 (foldts exp seed ...))
351 (($ <module-set> src mod name public? exp)
352 (foldts exp seed ...))
353 (($ <toplevel-set> src name exp)
354 (foldts exp seed ...))
355 (($ <toplevel-define> src name exp)
356 (foldts exp seed ...))
357 (($ <conditional> src test consequent alternate)
358 (let*-values (((seed ...) (foldts test seed ...))
359 ((seed ...) (foldts consequent seed ...)))
360 (foldts alternate seed ...)))
361 (($ <call> src proc args)
362 (let-values (((seed ...) (foldts proc seed ...)))
363 (fold-values foldts args seed ...)))
364 (($ <primcall> src name args)
365 (fold-values foldts args seed ...))
366 (($ <seq> src head tail)
367 (let-values (((seed ...) (foldts head seed ...)))
368 (foldts tail seed ...)))
369 (($ <lambda> src meta body)
370 (if body
371 (foldts body seed ...)
372 (values seed ...)))
373 (($ <lambda-case> src req opt rest kw inits gensyms body
374 alternate)
375 (let-values (((seed ...) (fold-values foldts inits seed ...)))
376 (if alternate
377 (let-values (((seed ...) (foldts body seed ...)))
378 (foldts alternate seed ...))
379 (foldts body seed ...))))
380 (($ <let> src names gensyms vals body)
381 (let*-values (((seed ...) (fold-values foldts vals seed ...)))
382 (foldts body seed ...)))
383 (($ <letrec> src in-order? names gensyms vals body)
384 (let*-values (((seed ...) (fold-values foldts vals seed ...)))
385 (foldts body seed ...)))
386 (($ <fix> src names gensyms vals body)
387 (let*-values (((seed ...) (fold-values foldts vals seed ...)))
388 (foldts body seed ...)))
389 (($ <let-values> src exp body)
390 (let*-values (((seed ...) (foldts exp seed ...)))
391 (foldts body seed ...)))
392 (($ <prompt> src tag body handler)
393 (let*-values (((seed ...) (foldts tag seed ...))
394 ((seed ...) (foldts body seed ...)))
395 (foldts handler seed ...)))
396 (($ <abort> src tag args tail)
397 (let*-values (((seed ...) (foldts tag seed ...))
398 ((seed ...) (fold-values foldts args seed ...)))
399 (foldts tail seed ...)))
400 (_
401 (values seed ...)))))
402 (up tree seed ...)))))
403
404 (define (tree-il-fold down up seed tree)
405 "Traverse TREE, calling DOWN before visiting a sub-tree, and UP when
406 after visiting it. Each of these procedures is invoked as `(PROC TREE
407 SEED)', where TREE is the sub-tree considered and SEED is the current
408 result, intially seeded with SEED.
409
410 This is an implementation of `foldts' as described by Andy Wingo in
411 ``Applications of fold to XML transformation''."
412 ;; Multi-valued fold naturally puts the seeds at the end, whereas
413 ;; normal fold puts the traversable at the end. Adapt to the expected
414 ;; argument order.
415 ((make-tree-il-folder tree) tree down up seed))
416
417 (define (pre-post-order pre post x)
418 (let lp ((x x))
419 (post
420 (match (pre x)
421 (($ <void> src)
422 (make-void src))
423
424 (($ <const> src exp)
425 (make-const src exp))
426
427 (($ <primitive-ref> src name)
428 (make-primitive-ref src name))
429
430 (($ <lexical-ref> src name gensym)
431 (make-lexical-ref src name gensym))
432
433 (($ <lexical-set> src name gensym exp)
434 (make-lexical-set src name gensym (lp exp)))
435
436 (($ <module-ref> src mod name public?)
437 (make-module-ref src mod name public?))
438
439 (($ <module-set> src mod name public? exp)
440 (make-module-set src mod name public? (lp exp)))
441
442 (($ <toplevel-ref> src name)
443 (make-toplevel-ref src name))
444
445 (($ <toplevel-set> src name exp)
446 (make-toplevel-set src name (lp exp)))
447
448 (($ <toplevel-define> src name exp)
449 (make-toplevel-define src name (lp exp)))
450
451 (($ <conditional> src test consequent alternate)
452 (make-conditional src (lp test) (lp consequent) (lp alternate)))
453
454 (($ <call> src proc args)
455 (make-call src (lp proc) (map lp args)))
456
457 (($ <primcall> src name args)
458 (make-primcall src name (map lp args)))
459
460 (($ <seq> src head tail)
461 (make-seq src (lp head) (lp tail)))
462
463 (($ <lambda> src meta body)
464 (make-lambda src meta (and body (lp body))))
465
466 (($ <lambda-case> src req opt rest kw inits gensyms body alternate)
467 (make-lambda-case src req opt rest kw (map lp inits) gensyms (lp body)
468 (and alternate (lp alternate))))
469
470 (($ <let> src names gensyms vals body)
471 (make-let src names gensyms (map lp vals) (lp body)))
472
473 (($ <letrec> src in-order? names gensyms vals body)
474 (make-letrec src in-order? names gensyms (map lp vals) (lp body)))
475
476 (($ <fix> src names gensyms vals body)
477 (make-fix src names gensyms (map lp vals) (lp body)))
478
479 (($ <let-values> src exp body)
480 (make-let-values src (lp exp) (lp body)))
481
482 (($ <prompt> src tag body handler)
483 (make-prompt src (lp tag) (lp body) (lp handler)))
484
485 (($ <abort> src tag args tail)
486 (make-abort src (lp tag) (map lp args) (lp tail)))))))
487
488 (define (post-order f x)
489 (pre-post-order (lambda (x) x) f x))
490
491 (define (pre-order f x)
492 (pre-post-order f (lambda (x) x) x))
493
494 ;; FIXME: We should have a better primitive than this.
495 (define (struct-nfields x)
496 (/ (string-length (symbol->string (struct-layout x))) 2))
497
498 (define (tree-il=? a b)
499 (cond
500 ((struct? a)
501 (and (struct? b)
502 (eq? (struct-vtable a) (struct-vtable b))
503 ;; Assume that all structs are tree-il, so we skip over the
504 ;; src slot.
505 (let lp ((n (1- (struct-nfields a))))
506 (or (zero? n)
507 (and (tree-il=? (struct-ref a n) (struct-ref b n))
508 (lp (1- n)))))))
509 ((pair? a)
510 (and (pair? b)
511 (tree-il=? (car a) (car b))
512 (tree-il=? (cdr a) (cdr b))))
513 (else
514 (equal? a b))))
515
516 (define-syntax hash-bits
517 (make-variable-transformer
518 (lambda (x)
519 (syntax-case x ()
520 (var
521 (identifier? #'var)
522 (logcount most-positive-fixnum))))))
523
524 (define (tree-il-hash exp)
525 (let ((hash-depth 4)
526 (hash-width 3))
527 (define (hash-exp exp depth)
528 (define (rotate x bits)
529 (logior (ash x (- bits))
530 (ash (logand x (1- (ash 1 bits))) (- hash-bits bits))))
531 (define (mix h1 h2)
532 (logxor h1 (rotate h2 8)))
533 (define (hash-struct s)
534 (let ((len (struct-nfields s))
535 (h (hashq (struct-vtable s) most-positive-fixnum)))
536 (if (zero? depth)
537 h
538 (let lp ((i (max (- len hash-width) 1)) (h h))
539 (if (< i len)
540 (lp (1+ i) (mix (hash-exp (struct-ref s i) (1+ depth)) h))
541 h)))))
542 (define (hash-list l)
543 (let ((h (hashq 'list most-positive-fixnum)))
544 (if (zero? depth)
545 h
546 (let lp ((l l) (width 0) (h h))
547 (if (< width hash-width)
548 (lp (cdr l) (1+ width)
549 (mix (hash-exp (car l) (1+ depth)) h))
550 h)))))
551 (cond
552 ((struct? exp) (hash-struct exp))
553 ((list? exp) (hash-list exp))
554 (else (hash exp most-positive-fixnum))))
555
556 (hash-exp exp 0)))