Fix first find-program-sources result
[bpt/guile.git] / module / language / cps.scm
CommitLineData
80b01fd0
AW
1;;; Continuation-passing style (CPS) intermediate language (IL)
2
3;; Copyright (C) 2013 Free Software Foundation, Inc.
4
5;;;; This library is free software; you can redistribute it and/or
6;;;; modify it under the terms of the GNU Lesser General Public
7;;;; License as published by the Free Software Foundation; either
8;;;; version 3 of the License, or (at your option) any later version.
9;;;;
10;;;; This library is distributed in the hope that it will be useful,
11;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13;;;; Lesser General Public License for more details.
14;;;;
15;;;; You should have received a copy of the GNU Lesser General Public
16;;;; License along with this library; if not, write to the Free Software
17;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19;;; Commentary:
20;;;
21;;; This is the continuation-passing style (CPS) intermediate language
22;;; (IL) for Guile.
23;;;
24;;; There are two kinds of terms in CPS: terms that bind continuations,
25;;; and terms that call continuations.
26;;;
27;;; $letk binds a set of mutually recursive continuations, each one an
28;;; instance of $cont. A $cont declares the name and source of a
29;;; continuation, and then contains as a subterm the particular
30;;; continuation instance: $kif for test continuations, $kargs for
31;;; continuations that bind values, etc.
32;;;
33;;; $continue nodes call continuations. The expression contained in the
34;;; $continue node determines the value or values that are passed to the
35;;; target continuation: $const to pass a constant value, $values to
36;;; pass multiple named values, etc.
37;;;
38;;; Additionally there is $letrec, a term that binds mutually recursive
39;;; functions. The contification pass will turn $letrec into $letk if
40;;; it can do so. Otherwise, the closure conversion pass will desugar
41;;; $letrec into an equivalent sequence of make-closure primcalls and
42;;; subsequent initializations of the captured variables of the
43;;; closures. You can think of $letrec as pertaining to "high CPS",
44;;; whereas later passes will only see "low CPS", which does not have
45;;; $letrec.
46;;;
47;;; This particular formulation of CPS was inspired by Andrew Kennedy's
48;;; 2007 paper, "Compiling with Continuations, Continued". All Guile
49;;; hackers should read that excellent paper! As in Kennedy's paper,
50;;; continuations are second-class, and may be thought of as basic block
51;;; labels. All values are bound to variables using continuation calls:
52;;; even constants!
53;;;
54;;; There are some Guile-specific quirks as well:
55;;;
56;;; - $ktrunc represents a continuation that receives multiple values,
57;;; but which truncates them to some number of required values,
58;;; possibly with a rest list.
59;;;
60;;; - $kentry labels an entry point for a $fun (a function), and
61;;; contains a $ktail representing the formal argument which is the
62;;; function's continuation.
63;;;
64;;; - $kentry also contains $kclause continuations, corresponding to
65;;; the case-lambda clauses of the function. $kclause actually
66;;; contains the clause body. This is because the $kclause
67;;; logically matches or doesn't match a given set of actual
68;;; arguments against a formal arity, then proceeds to a "body"
69;;; continuation (which is a $kargs).
70;;;
71;;; That's to say that a $fun can be matched like this:
72;;;
73;;; (match f
74;;; (($ $fun meta free
75;;; ($ $cont kentry src
76;;; ($ $kentry self ($ $cont ktail _ ($ $ktail))
77;;; (($ $kclause arity
78;;; ($ $cont kbody _ ($ $kargs names syms body)))
79;;; ...))))
80;;; #t))
81;;;
82;;; A $continue to ktail is in tail position. $kentry, $kclause,
83;;; and $ktail will never be seen elsewhere in a CPS term.
84;;;
85;;; - $prompt continues to the body of the prompt, having pushed on a
86;;; prompt whose handler will continue at its "handler"
87;;; continuation. The continuation of the prompt is responsible for
96af4a18
AW
88;;; popping the prompt. A $prompt also records the continuation
89;;; that pops the prompt, to make various static analyses easier.
80b01fd0
AW
90;;;
91;;; In summary:
92;;;
93;;; - $letk, $letrec, and $continue are terms.
94;;;
95;;; - $cont is a continuation, containing a continuation body ($kargs,
96;;; $kif, etc).
97;;;
98;;; - $continue terms contain an expression ($call, $const, $fun,
99;;; etc).
100;;;
101;;; See (language tree-il compile-cps) for details on how Tree-IL
102;;; converts to CPS.
103;;;
104;;; Code:
105
106(define-module (language cps)
107 #:use-module (ice-9 match)
108 #:use-module ((srfi srfi-1) #:select (fold))
109 #:use-module (srfi srfi-9)
110 #:use-module (srfi srfi-9 gnu)
111 #:export (;; Helper.
112 $arity
113 make-$arity
114
115 ;; Terms.
116 $letk $continue $letrec
117
118 ;; Continuations.
119 $cont
120
121 ;; Continuation bodies.
122 $kif $ktrunc $kargs $kentry $ktail $kclause
123
124 ;; Expressions.
125 $var $void $const $prim $fun $call $primcall $values $prompt
126
127 ;; Building macros.
128 let-gensyms
129 build-cps-term build-cps-cont build-cps-exp
130 rewrite-cps-term rewrite-cps-cont rewrite-cps-exp
131
132 ;; Misc.
133 parse-cps unparse-cps
134 fold-conts fold-local-conts))
135
136;; FIXME: Use SRFI-99, when Guile adds it.
137(define-syntax define-record-type*
138 (lambda (x)
139 (define (id-append ctx . syms)
140 (datum->syntax ctx (apply symbol-append (map syntax->datum syms))))
141 (syntax-case x ()
142 ((_ name field ...)
143 (and (identifier? #'name) (and-map identifier? #'(field ...)))
144 (with-syntax ((cons (id-append #'name #'make- #'name))
145 (pred (id-append #'name #'name #'?))
146 ((getter ...) (map (lambda (f)
147 (id-append f #'name #'- f))
148 #'(field ...))))
149 #'(define-record-type name
150 (cons field ...)
151 pred
152 (field getter)
153 ...))))))
154
155(define-syntax-rule (define-cps-type name field ...)
156 (begin
157 (define-record-type* name field ...)
158 (set-record-type-printer! name print-cps)))
159
160(define (print-cps exp port)
161 (format port "#<cps ~S>" (unparse-cps exp)))
162
163;; Helper.
164(define-record-type* $arity req opt rest kw allow-other-keys?)
165
166;; Terms.
167(define-cps-type $letk conts body)
168(define-cps-type $continue k exp)
169(define-cps-type $letrec names syms funs body)
170
171;; Continuations
172(define-cps-type $cont k src cont)
173(define-cps-type $kif kt kf)
174(define-cps-type $ktrunc arity k)
175(define-cps-type $kargs names syms body)
176(define-cps-type $kentry self tail clauses)
177(define-cps-type $ktail)
178(define-cps-type $kclause arity cont)
179
180;; Expressions.
181(define-cps-type $var sym)
182(define-cps-type $void)
183(define-cps-type $const val)
184(define-cps-type $prim name)
185(define-cps-type $fun meta free body)
186(define-cps-type $call proc args)
187(define-cps-type $primcall name args)
188(define-cps-type $values args)
96af4a18 189(define-cps-type $prompt escape? tag handler pop)
80b01fd0
AW
190
191(define-syntax let-gensyms
192 (syntax-rules ()
193 ((_ (sym ...) body body* ...)
194 (let ((sym (gensym (symbol->string 'sym))) ...)
195 body body* ...))))
196
197(define-syntax build-arity
198 (syntax-rules (unquote)
199 ((_ (unquote exp)) exp)
200 ((_ (req opt rest kw allow-other-keys?))
201 (make-$arity req opt rest kw allow-other-keys?))))
202
203(define-syntax build-cont-body
204 (syntax-rules (unquote $kif $ktrunc $kargs $kentry $ktail $kclause)
205 ((_ (unquote exp))
206 exp)
207 ((_ ($kif kt kf))
208 (make-$kif kt kf))
209 ((_ ($ktrunc req rest kargs))
210 (make-$ktrunc (make-$arity req '() rest '() #f) kargs))
211 ((_ ($kargs (name ...) (sym ...) body))
212 (make-$kargs (list name ...) (list sym ...) (build-cps-term body)))
213 ((_ ($kargs names syms body))
214 (make-$kargs names syms (build-cps-term body)))
215 ((_ ($kentry self tail (unquote clauses)))
216 (make-$kentry self (build-cps-cont tail) clauses))
217 ((_ ($kentry self tail (clause ...)))
218 (make-$kentry self (build-cps-cont tail) (list (build-cps-cont clause) ...)))
219 ((_ ($ktail))
220 (make-$ktail))
221 ((_ ($kclause arity cont))
222 (make-$kclause (build-arity arity) (build-cps-cont cont)))))
223
224(define-syntax build-cps-cont
225 (syntax-rules (unquote)
226 ((_ (unquote exp)) exp)
227 ((_ (k src cont)) (make-$cont k src (build-cont-body cont)))))
228
229(define-syntax build-cps-exp
230 (syntax-rules (unquote
231 $var $void $const $prim $fun $call $primcall $values $prompt)
232 ((_ (unquote exp)) exp)
233 ((_ ($var sym)) (make-$var sym))
234 ((_ ($void)) (make-$void))
235 ((_ ($const val)) (make-$const val))
236 ((_ ($prim name)) (make-$prim name))
237 ((_ ($fun meta free body)) (make-$fun meta free (build-cps-cont body)))
238 ((_ ($call proc (arg ...))) (make-$call proc (list arg ...)))
239 ((_ ($call proc args)) (make-$call proc args))
240 ((_ ($primcall name (arg ...))) (make-$primcall name (list arg ...)))
241 ((_ ($primcall name args)) (make-$primcall name args))
242 ((_ ($values (arg ...))) (make-$values (list arg ...)))
243 ((_ ($values args)) (make-$values args))
96af4a18
AW
244 ((_ ($prompt escape? tag handler pop))
245 (make-$prompt escape? tag handler pop))))
80b01fd0
AW
246
247(define-syntax build-cps-term
248 (syntax-rules (unquote $letk $letk* $letconst $letrec $continue)
249 ((_ (unquote exp))
250 exp)
251 ((_ ($letk (unquote conts) body))
252 (make-$letk conts (build-cps-term body)))
253 ((_ ($letk (cont ...) body))
254 (make-$letk (list (build-cps-cont cont) ...)
255 (build-cps-term body)))
256 ((_ ($letk* () body))
257 (build-cps-term body))
258 ((_ ($letk* (cont conts ...) body))
259 (build-cps-term ($letk (cont) ($letk* (conts ...) body))))
260 ((_ ($letconst () body))
261 (build-cps-term body))
262 ((_ ($letconst ((name sym val) tail ...) body))
263 (let-gensyms (kconst)
264 (build-cps-term
265 ($letk ((kconst #f ($kargs (name) (sym) ($letconst (tail ...) body))))
266 ($continue kconst ($const val))))))
267 ((_ ($letrec names gensyms funs body))
268 (make-$letrec names gensyms funs (build-cps-term body)))
269 ((_ ($continue k exp))
270 (make-$continue k (build-cps-exp exp)))))
271
272(define-syntax-rule (rewrite-cps-term x (pat body) ...)
273 (match x
274 (pat (build-cps-term body)) ...))
275(define-syntax-rule (rewrite-cps-cont x (pat body) ...)
276 (match x
277 (pat (build-cps-cont body)) ...))
278(define-syntax-rule (rewrite-cps-exp x (pat body) ...)
279 (match x
280 (pat (build-cps-exp body)) ...))
281
282(define (parse-cps exp)
283 (define (src exp)
284 (let ((props (source-properties exp)))
285 (and (pair? props) props)))
286 (match exp
287 ;; Continuations.
288 (('letconst k (name sym c) body)
289 (build-cps-term
290 ($letk ((k (src exp) ($kargs (name) (sym)
291 ,(parse-cps body))))
292 ($continue k ($const c)))))
293 (('let k (name sym val) body)
294 (build-cps-term
295 ($letk ((k (src exp) ($kargs (name) (sym)
296 ,(parse-cps body))))
297 ,(parse-cps val))))
298 (('letk (cont ...) body)
299 (build-cps-term
300 ($letk ,(map parse-cps cont) ,(parse-cps body))))
301 (('k sym body)
302 (build-cps-cont
303 (sym (src exp) ,(parse-cps body))))
304 (('kif kt kf)
305 (build-cont-body ($kif kt kf)))
306 (('ktrunc req rest k)
307 (build-cont-body ($ktrunc req rest k)))
308 (('kargs names syms body)
309 (build-cont-body ($kargs names syms ,(parse-cps body))))
310 (('kentry self tail clauses)
311 (build-cont-body
312 ($kentry self ,(parse-cps tail) ,(map parse-cps clauses))))
313 (('ktail)
314 (build-cont-body
315 ($ktail)))
316 (('kclause (req opt rest kw allow-other-keys?) body)
317 (build-cont-body
318 ($kclause (req opt rest kw allow-other-keys?)
319 ,(parse-cps body))))
320 (('kseq body)
321 (build-cont-body ($kargs () () ,(parse-cps body))))
322
323 ;; Calls.
324 (('continue k exp)
325 (build-cps-term ($continue k ,(parse-cps exp))))
326 (('var sym)
327 (build-cps-exp ($var sym)))
328 (('void)
329 (build-cps-exp ($void)))
330 (('const exp)
331 (build-cps-exp ($const exp)))
332 (('prim name)
333 (build-cps-exp ($prim name)))
334 (('fun meta free body)
335 (build-cps-exp ($fun meta free ,(parse-cps body))))
336 (('letrec ((name sym fun) ...) body)
337 (build-cps-term
338 ($letrec name sym (map parse-cps fun) ,(parse-cps body))))
339 (('call proc arg ...)
340 (build-cps-exp ($call proc arg)))
341 (('primcall name arg ...)
342 (build-cps-exp ($primcall name arg)))
343 (('values arg ...)
344 (build-cps-exp ($values arg)))
96af4a18
AW
345 (('prompt escape? tag handler pop)
346 (build-cps-exp ($prompt escape? tag handler pop)))
80b01fd0
AW
347 (_
348 (error "unexpected cps" exp))))
349
350(define (unparse-cps exp)
351 (match exp
352 ;; Continuations.
353 (($ $letk (($ $cont k src ($ $kargs (name) (sym) body)))
354 ($ $continue k ($ $const c)))
355 `(letconst ,k (,name ,sym ,c)
356 ,(unparse-cps body)))
357 (($ $letk (($ $cont k src ($ $kargs (name) (sym) body))) val)
358 `(let ,k (,name ,sym ,(unparse-cps val))
359 ,(unparse-cps body)))
360 (($ $letk conts body)
361 `(letk ,(map unparse-cps conts) ,(unparse-cps body)))
362 (($ $cont sym src body)
363 `(k ,sym ,(unparse-cps body)))
364 (($ $kif kt kf)
365 `(kif ,kt ,kf))
366 (($ $ktrunc ($ $arity req () rest '() #f) k)
367 `(ktrunc ,req ,rest ,k))
368 (($ $kargs () () body)
369 `(kseq ,(unparse-cps body)))
370 (($ $kargs names syms body)
371 `(kargs ,names ,syms ,(unparse-cps body)))
372 (($ $kentry self tail clauses)
373 `(kentry ,self ,(unparse-cps tail) ,(map unparse-cps clauses)))
374 (($ $ktail)
375 `(ktail))
376 (($ $kclause ($ $arity req opt rest kw allow-other-keys?) body)
377 `(kclause (,req ,opt ,rest ,kw ,allow-other-keys?) ,(unparse-cps body)))
378
379 ;; Calls.
380 (($ $continue k exp)
381 `(continue ,k ,(unparse-cps exp)))
382 (($ $var sym)
383 `(var ,sym))
384 (($ $void)
385 `(void))
386 (($ $const val)
387 `(const ,val))
388 (($ $prim name)
389 `(prim ,name))
390 (($ $fun meta free body)
391 `(fun ,meta ,free ,(unparse-cps body)))
392 (($ $letrec names syms funs body)
393 `(letrec ,(map (lambda (name sym fun)
394 (list name sym (unparse-cps fun)))
395 names syms funs)
396 ,(unparse-cps body)))
397 (($ $call proc args)
398 `(call ,proc ,@args))
399 (($ $primcall name args)
400 `(primcall ,name ,@args))
401 (($ $values args)
402 `(values ,@args))
96af4a18
AW
403 (($ $prompt escape? tag handler pop)
404 `(prompt ,escape? ,tag ,handler ,pop))
80b01fd0
AW
405 (_
406 (error "unexpected cps" exp))))
407
408(define (fold-conts proc seed fun)
409 (define (cont-folder cont seed)
410 (match cont
411 (($ $cont k src cont)
412 (let ((seed (proc k src cont seed)))
413 (match cont
414 (($ $kargs names syms body)
415 (term-folder body seed))
416
417 (($ $kentry self tail clauses)
418 (fold cont-folder (cont-folder tail seed) clauses))
419
420 (($ $kclause arity body)
421 (cont-folder body seed))
422
423 (_ seed))))))
424
425 (define (fun-folder fun seed)
426 (match fun
427 (($ $fun meta free body)
428 (cont-folder body seed))))
429
430 (define (term-folder term seed)
431 (match term
432 (($ $letk conts body)
433 (fold cont-folder (term-folder body seed) conts))
434
435 (($ $continue k exp)
436 (match exp
437 (($ $fun) (fun-folder exp seed))
438 (_ seed)))
439
440 (($ $letrec names syms funs body)
441 (fold fun-folder (term-folder body seed) funs))))
442
443 (fun-folder fun seed))
444
445(define (fold-local-conts proc seed cont)
446 (define (cont-folder cont seed)
447 (match cont
448 (($ $cont k src cont)
449 (let ((seed (proc k src cont seed)))
450 (match cont
451 (($ $kargs names syms body)
452 (term-folder body seed))
453
454 (($ $kentry self tail clauses)
455 (fold cont-folder (cont-folder tail seed) clauses))
456
457 (($ $kclause arity body)
458 (cont-folder body seed))
459
460 (_ seed))))))
461
462 (define (term-folder term seed)
463 (match term
464 (($ $letk conts body)
465 (fold cont-folder (term-folder body seed) conts))
466
467 (($ $continue) seed)
468
469 (($ $letrec names syms funs body) (term-folder body seed))))
470
471 (cont-folder cont seed))