Remove $void CPS expression type
[bpt/guile.git] / module / language / cps.scm
index e0d708a..ee20197 100644 (file)
@@ -1,6 +1,6 @@
 ;;; Continuation-passing style (CPS) intermediate language (IL)
 
-;; Copyright (C) 2013, 2014 Free Software Foundation, Inc.
+;; Copyright (C) 2013, 2014, 2015 Free Software Foundation, Inc.
 
 ;;;; This library is free software; you can redistribute it and/or
 ;;;; modify it under the terms of the GNU Lesser General Public
@@ -27,8 +27,8 @@
 ;;; $letk binds a set of mutually recursive continuations, each one an
 ;;; instance of $cont.  A $cont declares the name of a continuation, and
 ;;; then contains as a subterm the particular continuation instance:
-;;; $kif for test continuations, $kargs for continuations that bind
-;;; values, etc.
+;;; $kargs for continuations that bind values, $ktail for the tail
+;;; continuation, etc.
 ;;;
 ;;; $continue nodes call continuations.  The expression contained in the
 ;;; $continue node determines the value or values that are passed to the
 ;;;     but which truncates them to some number of required values,
 ;;;     possibly with a rest list.
 ;;;
-;;;   - $kentry labels an entry point for a $fun (a function), and
+;;;   - $kfun labels an entry point for a $fun (a function), and
 ;;;     contains a $ktail representing the formal argument which is the
 ;;;     function's continuation.
 ;;;
-;;;   - $kentry also contains $kclause continuations, corresponding to
-;;;     the case-lambda clauses of the function.  $kclause actually
-;;;     contains the clause body.  This is because the $kclause
-;;;     logically matches or doesn't match a given set of actual
-;;;     arguments against a formal arity, then proceeds to a "body"
-;;;     continuation (which is a $kargs).
+;;;   - $kfun also contain a $kclause continuation, corresponding to
+;;;     the first case-lambda clause of the function.  $kclause actually
+;;;     contains the clause body, and the subsequent clause (if any).
+;;;     This is because the $kclause logically matches or doesn't match
+;;;     a given set of actual arguments against a formal arity, then
+;;;     proceeds to a "body" continuation (which is a $kargs).
 ;;;
 ;;;     That's to say that a $fun can be matched like this:
 ;;;
 ;;;     (match f
-;;;       (($ $fun src meta free
-;;;           ($ $cont kentry
-;;;              ($ $kentry self ($ $cont ktail _ ($ $ktail))
-;;;                 (($ $kclause arity
-;;;                     ($ $cont kbody _ ($ $kargs names syms body)))
-;;;                  ...))))
+;;;       (($ $fun free
+;;;           ($ $cont kfun
+;;;              ($ $kfun src meta self ($ $cont ktail ($ $ktail))
+;;;                 ($ $kclause arity
+;;;                    ($ $cont kbody ($ $kargs names syms body))
+;;;                    alternate))))
 ;;;         #t))
 ;;;
-;;;     A $continue to ktail is in tail position.  $kentry, $kclause,
+;;;     A $continue to ktail is in tail position.  $kfun, $kclause,
 ;;;     and $ktail will never be seen elsewhere in a CPS term.
 ;;;
 ;;;   - $prompt continues to the body of the prompt, having pushed on a
@@ -92,7 +92,7 @@
 ;;;   - $letk, $letrec, and $continue are terms.
 ;;;
 ;;;   - $cont is a continuation, containing a continuation body ($kargs,
-;;;     $kif, etc).
+;;;     $ktail, etc).
 ;;;
 ;;;   - $continue terms contain an expression ($call, $const, $fun,
 ;;;     etc).
   #:use-module ((srfi srfi-1) #:select (fold))
   #:use-module (srfi srfi-9)
   #:use-module (srfi srfi-9 gnu)
+  #:use-module (srfi srfi-11)
   #:export (;; Helper.
             $arity
             make-$arity
             $cont
 
             ;; Continuation bodies.
-            $kif $kreceive $kargs $kentry $ktail $kclause
+            $kreceive $kargs $kfun $ktail $kclause
 
             ;; Expressions.
-            $void $const $prim $fun $call $callk $primcall $values $prompt
+            $const $prim $fun $closure $branch
+            $call $callk $primcall $values $prompt
+
+            ;; First-order CPS root.
+            $program
+
+            ;; Fresh names.
+            label-counter var-counter
+            fresh-label fresh-var
+            with-fresh-name-state compute-max-label-and-var
+            let-fresh
 
             ;; Building macros.
-            let-gensyms
             build-cps-term build-cps-cont build-cps-exp
             rewrite-cps-term rewrite-cps-cont rewrite-cps-exp
 
             ;; Misc.
             parse-cps unparse-cps
-            fold-conts fold-local-conts))
+            make-global-cont-folder make-local-cont-folder
+            fold-conts fold-local-conts
+            visit-cont-successors))
 
 ;; FIXME: Use SRFI-99, when Guile adds it.
 (define-syntax define-record-type*
 ;; Terms.
 (define-cps-type $letk conts body)
 (define-cps-type $continue k src exp)
-(define-cps-type $letrec names syms funs body)
+(define-cps-type $letrec names syms funs body) ; Higher-order.
 
 ;; Continuations
 (define-cps-type $cont k cont)
-(define-cps-type $kif kt kf)
 (define-cps-type $kreceive arity k)
 (define-cps-type $kargs names syms body)
-(define-cps-type $kentry self tail clauses)
+(define-cps-type $kfun src meta self tail clause)
 (define-cps-type $ktail)
-(define-cps-type $kclause arity cont)
+(define-cps-type $kclause arity cont alternate)
 
 ;; Expressions.
-(define-cps-type $void)
 (define-cps-type $const val)
 (define-cps-type $prim name)
-(define-cps-type $fun src meta free body)
+(define-cps-type $fun free body) ; Higher-order.
+(define-cps-type $closure label nfree) ; First-order.
+(define-cps-type $branch k exp)
 (define-cps-type $call proc args)
-(define-cps-type $callk k proc args)
+(define-cps-type $callk k proc args) ; First-order.
 (define-cps-type $primcall name args)
 (define-cps-type $values args)
 (define-cps-type $prompt escape? tag handler)
 
-(define-syntax let-gensyms
-  (syntax-rules ()
-    ((_ (sym ...) body body* ...)
-     (let ((sym (gensym (symbol->string 'sym))) ...)
-       body body* ...))))
+;; The root of a higher-order CPS term is $cont containing a $kfun.  The
+;; root of a first-order CPS term is a $program.
+(define-cps-type $program funs)
+
+(define label-counter (make-parameter #f))
+(define var-counter (make-parameter #f))
+
+(define (fresh-label)
+  (let ((count (or (label-counter)
+                   (error "fresh-label outside with-fresh-name-state"))))
+    (label-counter (1+ count))
+    count))
+
+(define (fresh-var)
+  (let ((count (or (var-counter)
+                   (error "fresh-var outside with-fresh-name-state"))))
+    (var-counter (1+ count))
+    count))
+
+(define-syntax-rule (let-fresh (label ...) (var ...) body ...)
+  (let ((label (fresh-label)) ...
+        (var (fresh-var)) ...)
+    body ...))
+
+(define-syntax-rule (with-fresh-name-state fun body ...)
+  (call-with-values (lambda () (compute-max-label-and-var fun))
+    (lambda (max-label max-var)
+      (parameterize ((label-counter (1+ max-label))
+                     (var-counter (1+ max-var)))
+        body ...))))
 
 (define-syntax build-arity
   (syntax-rules (unquote)
      (make-$arity req opt rest kw allow-other-keys?))))
 
 (define-syntax build-cont-body
-  (syntax-rules (unquote $kif $kreceive $kargs $kentry $ktail $kclause)
+  (syntax-rules (unquote $kreceive $kargs $kfun $ktail $kclause)
     ((_ (unquote exp))
      exp)
-    ((_ ($kif kt kf))
-     (make-$kif kt kf))
     ((_ ($kreceive req rest kargs))
      (make-$kreceive (make-$arity req '() rest '() #f) kargs))
+    ((_ ($kargs (name ...) (unquote syms) body))
+     (make-$kargs (list name ...) syms (build-cps-term body)))
     ((_ ($kargs (name ...) (sym ...) body))
      (make-$kargs (list name ...) (list sym ...) (build-cps-term body)))
     ((_ ($kargs names syms body))
      (make-$kargs names syms (build-cps-term body)))
-    ((_ ($kentry self tail (unquote clauses)))
-     (make-$kentry self (build-cps-cont tail) clauses))
-    ((_ ($kentry self tail (clause ...)))
-     (make-$kentry self (build-cps-cont tail) (list (build-cps-cont clause) ...)))
+    ((_ ($kfun src meta self tail clause))
+     (make-$kfun src meta self (build-cps-cont tail) (build-cps-cont clause)))
     ((_ ($ktail))
      (make-$ktail))
-    ((_ ($kclause arity cont))
-     (make-$kclause (build-arity arity) (build-cps-cont cont)))))
+    ((_ ($kclause arity cont alternate))
+     (make-$kclause (build-arity arity) (build-cps-cont cont)
+                    (build-cps-cont alternate)))))
 
 (define-syntax build-cps-cont
   (syntax-rules (unquote)
 
 (define-syntax build-cps-exp
   (syntax-rules (unquote
-                 $void $const $prim $fun $call $callk $primcall $values $prompt)
+                 $const $prim $fun $closure $branch
+                 $call $callk $primcall $values $prompt)
     ((_ (unquote exp)) exp)
-    ((_ ($void)) (make-$void))
     ((_ ($const val)) (make-$const val))
     ((_ ($prim name)) (make-$prim name))
-    ((_ ($fun src meta free body))
-     (make-$fun src meta free (build-cps-cont body)))
+    ((_ ($fun free body)) (make-$fun free (build-cps-cont body)))
+    ((_ ($closure k nfree)) (make-$closure k nfree))
+    ((_ ($call proc (unquote args))) (make-$call proc args))
     ((_ ($call proc (arg ...))) (make-$call proc (list arg ...)))
     ((_ ($call proc args)) (make-$call proc args))
+    ((_ ($callk k proc (unquote args))) (make-$callk k proc args))
     ((_ ($callk k proc (arg ...))) (make-$callk k proc (list arg ...)))
     ((_ ($callk k proc args)) (make-$callk k proc args))
+    ((_ ($primcall name (unquote args))) (make-$primcall name args))
     ((_ ($primcall name (arg ...))) (make-$primcall name (list arg ...)))
     ((_ ($primcall name args)) (make-$primcall name args))
+    ((_ ($values (unquote args))) (make-$values args))
     ((_ ($values (arg ...))) (make-$values (list arg ...)))
     ((_ ($values args)) (make-$values args))
+    ((_ ($branch k exp)) (make-$branch k (build-cps-exp exp)))
     ((_ ($prompt escape? tag handler))
      (make-$prompt escape? tag handler))))
 
 (define-syntax build-cps-term
-  (syntax-rules (unquote $letk $letk* $letconst $letrec $continue)
+  (syntax-rules (unquote $letk $letk* $letconst $letrec $program $continue)
     ((_ (unquote exp))
      exp)
     ((_ ($letk (unquote conts) body))
     ((_ ($letconst () body))
      (build-cps-term body))
     ((_ ($letconst ((name sym val) tail ...) body))
-     (let-gensyms (kconst)
+     (let-fresh (kconst) ()
        (build-cps-term
          ($letk ((kconst ($kargs (name) (sym) ($letconst (tail ...) body))))
            ($continue kconst (let ((props (source-properties val)))
              ($const val))))))
     ((_ ($letrec names gensyms funs body))
      (make-$letrec names gensyms funs (build-cps-term body)))
+    ((_ ($program (unquote conts)))
+     (make-$program conts))
+    ((_ ($program (cont ...)))
+     (make-$program (list (build-cps-cont cont) ...)))
+    ((_ ($program conts))
+     (make-$program conts))
     ((_ ($continue k src exp))
      (make-$continue k src (build-cps-exp exp)))))
 
     (('k sym body)
      (build-cps-cont
        (sym ,(parse-cps body))))
-    (('kif kt kf)
-     (build-cont-body ($kif kt kf)))
     (('kreceive req rest k)
      (build-cont-body ($kreceive req rest k)))
     (('kargs names syms body)
      (build-cont-body ($kargs names syms ,(parse-cps body))))
-    (('kentry self tail clauses)
+    (('kfun src meta self tail clause)
      (build-cont-body
-      ($kentry self ,(parse-cps tail) ,(map parse-cps clauses))))
+      ($kfun (src exp) meta self ,(parse-cps tail)
+        ,(and=> clause parse-cps))))
     (('ktail)
      (build-cont-body
       ($ktail)))
     (('kclause (req opt rest kw allow-other-keys?) body)
      (build-cont-body
       ($kclause (req opt rest kw allow-other-keys?)
-        ,(parse-cps body))))
+        ,(parse-cps body)
+        ,#f)))
+    (('kclause (req opt rest kw allow-other-keys?) body alternate)
+     (build-cont-body
+      ($kclause (req opt rest kw allow-other-keys?)
+        ,(parse-cps body)
+        ,(parse-cps alternate))))
     (('kseq body)
      (build-cont-body ($kargs () () ,(parse-cps body))))
 
     ;; Calls.
     (('continue k exp)
      (build-cps-term ($continue k (src exp) ,(parse-cps exp))))
-    (('void)
-     (build-cps-exp ($void)))
     (('const exp)
      (build-cps-exp ($const exp)))
     (('prim name)
      (build-cps-exp ($prim name)))
-    (('fun meta free body)
-     (build-cps-exp ($fun (src exp) meta free ,(parse-cps body))))
+    (('fun free body)
+     (build-cps-exp ($fun free ,(parse-cps body))))
+    (('closure k nfree)
+     (build-cps-exp ($closure k nfree)))
     (('letrec ((name sym fun) ...) body)
      (build-cps-term
        ($letrec name sym (map parse-cps fun) ,(parse-cps body))))
+    (('program (cont ...))
+     (build-cps-term ($program ,(map parse-cps cont))))
     (('call proc arg ...)
      (build-cps-exp ($call proc arg)))
     (('callk k proc arg ...)
      (build-cps-exp ($callk k proc arg)))
     (('primcall name arg ...)
      (build-cps-exp ($primcall name arg)))
+    (('branch k exp)
+     (build-cps-exp ($branch k ,(parse-cps exp))))
     (('values arg ...)
      (build-cps-exp ($values arg)))
     (('prompt escape? tag handler)
      `(letk ,(map unparse-cps conts) ,(unparse-cps body)))
     (($ $cont sym body)
      `(k ,sym ,(unparse-cps body)))
-    (($ $kif kt kf)
-     `(kif ,kt ,kf))
     (($ $kreceive ($ $arity req () rest '() #f) k)
      `(kreceive ,req ,rest ,k))
     (($ $kargs () () body)
      `(kseq ,(unparse-cps body)))
     (($ $kargs names syms body)
      `(kargs ,names ,syms ,(unparse-cps body)))
-    (($ $kentry self tail clauses)
-     `(kentry ,self ,(unparse-cps tail) ,(map unparse-cps clauses)))
+    (($ $kfun src meta self tail clause)
+     `(kfun ,meta ,self ,(unparse-cps tail) ,(unparse-cps clause)))
     (($ $ktail)
      `(ktail))
-    (($ $kclause ($ $arity req opt rest kw allow-other-keys?) body)
-     `(kclause (,req ,opt ,rest ,kw ,allow-other-keys?) ,(unparse-cps body)))
+    (($ $kclause ($ $arity req opt rest kw allow-other-keys?) body alternate)
+     `(kclause (,req ,opt ,rest ,kw ,allow-other-keys?) ,(unparse-cps body)
+               . ,(if alternate (list (unparse-cps alternate)) '())))
 
     ;; Calls.
     (($ $continue k src exp)
      `(continue ,k ,(unparse-cps exp)))
-    (($ $void)
-     `(void))
     (($ $const val)
      `(const ,val))
     (($ $prim name)
      `(prim ,name))
-    (($ $fun src meta free body)
-     `(fun ,meta ,free ,(unparse-cps body)))
+    (($ $fun free body)
+     `(fun ,free ,(unparse-cps body)))
+    (($ $closure k nfree)
+     `(closure ,k ,nfree))
     (($ $letrec names syms funs body)
      `(letrec ,(map (lambda (name sym fun)
                       (list name sym (unparse-cps fun)))
                     names syms funs)
         ,(unparse-cps body)))
+    (($ $program conts)
+     `(program ,(map unparse-cps conts)))
     (($ $call proc args)
      `(call ,proc ,@args))
     (($ $callk k proc args)
      `(callk ,k ,proc ,@args))
     (($ $primcall name args)
      `(primcall ,name ,@args))
+    (($ $branch k exp)
+     `(branch ,k ,(unparse-cps exp)))
     (($ $values args)
      `(values ,@args))
     (($ $prompt escape? tag handler)
     (_
      (error "unexpected cps" exp))))
 
-(define (fold-conts proc seed fun)
-  (define (cont-folder cont seed)
+(define-syntax-rule (make-global-cont-folder seed ...)
+  (lambda (proc cont seed ...)
+    (define (cont-folder cont seed ...)
+      (match cont
+        (($ $cont k cont)
+         (let-values (((seed ...) (proc k cont seed ...)))
+           (match cont
+             (($ $kargs names syms body)
+              (term-folder body seed ...))
+
+             (($ $kfun src meta self tail clause)
+              (let-values (((seed ...) (cont-folder tail seed ...)))
+                (if clause
+                    (cont-folder clause seed ...)
+                    (values seed ...))))
+
+             (($ $kclause arity body alternate)
+              (let-values (((seed ...) (cont-folder body seed ...)))
+                (if alternate
+                    (cont-folder alternate seed ...)
+                    (values seed ...))))
+
+             (_ (values seed ...)))))))
+
+    (define (fun-folder fun seed ...)
+      (match fun
+        (($ $fun free body)
+         (cont-folder body seed ...))))
+
+    (define (term-folder term seed ...)
+      (match term
+        (($ $letk conts body)
+         (let-values (((seed ...) (term-folder body seed ...)))
+           (let lp ((conts conts) (seed seed) ...)
+             (if (null? conts)
+                 (values seed ...)
+                 (let-values (((seed ...) (cont-folder (car conts) seed ...)))
+                   (lp (cdr conts) seed ...))))))
+
+        (($ $continue k src exp)
+         (match exp
+           (($ $fun) (fun-folder exp seed ...))
+           (_ (values seed ...))))
+
+        (($ $letrec names syms funs body)
+         (let-values (((seed ...) (term-folder body seed ...)))
+           (let lp ((funs funs) (seed seed) ...)
+             (if (null? funs)
+                 (values seed ...)
+                 (let-values (((seed ...) (fun-folder (car funs) seed ...)))
+                   (lp (cdr funs) seed ...))))))))
+
+    (cont-folder cont seed ...)))
+
+(define-syntax-rule (make-local-cont-folder seed ...)
+  (lambda (proc cont seed ...)
+    (define (cont-folder cont seed ...)
+      (match cont
+        (($ $cont k (and cont ($ $kargs names syms body)))
+         (let-values (((seed ...) (proc k cont seed ...)))
+           (term-folder body seed ...)))
+        (($ $cont k cont)
+         (proc k cont seed ...))))
+    (define (term-folder term seed ...)
+      (match term
+        (($ $letk conts body)
+         (let-values (((seed ...) (term-folder body seed ...)))
+           (let lp ((conts conts) (seed seed) ...)
+             (match conts
+               (() (values seed ...))
+               ((cont) (cont-folder cont seed ...))
+               ((cont . conts)
+                (let-values (((seed ...) (cont-folder cont seed ...)))
+                  (lp conts seed ...)))))))
+        (($ $letrec names syms funs body) (term-folder body seed ...))
+        (_ (values seed ...))))
+    (define (clause-folder clause seed ...)
+      (match clause
+        (($ $cont k (and cont ($ $kclause arity body alternate)))
+         (let-values (((seed ...) (proc k cont seed ...)))
+           (if alternate
+               (let-values (((seed ...) (cont-folder body seed ...)))
+                 (clause-folder alternate seed ...))
+               (cont-folder body seed ...))))))
     (match cont
-      (($ $cont k cont)
-       (let ((seed (proc k cont seed)))
-         (match cont
-           (($ $kargs names syms body)
-            (term-folder body seed))
-
-           (($ $kentry self tail clauses)
-            (fold cont-folder (cont-folder tail seed) clauses))
-
-           (($ $kclause arity body)
-            (cont-folder body seed))
-
-           (_ seed))))))
+      (($ $cont k (and cont ($ $kfun src meta self tail clause)))
+       (let*-values (((seed ...) (proc k cont seed ...))
+                     ((seed ...) (if clause
+                                     (clause-folder clause seed ...)
+                                     (values seed ...))))
+         (cont-folder tail seed ...))))))
+
+(define (compute-max-label-and-var fun)
+  (match fun
+    (($ $cont)
+     ((make-global-cont-folder max-label max-var)
+      (lambda (label cont max-label max-var)
+        (values (max label max-label)
+                (match cont
+                  (($ $kargs names vars body)
+                   (let lp ((body body) (max-var (fold max max-var vars)))
+                     (match body
+                       (($ $letk conts body) (lp body max-var))
+                       (($ $letrec names vars funs body)
+                        (lp body (fold max max-var vars)))
+                       (_ max-var))))
+                  (($ $kfun src meta self)
+                   (max self max-var))
+                  (_ max-var))))
+      fun -1 -1))
+    (($ $program conts)
+     (define (fold/2 proc in s0 s1)
+      (if (null? in)
+          (values s0 s1)
+          (let-values (((s0 s1) (proc (car in) s0 s1)))
+            (fold/2 proc (cdr in) s0 s1))))
+     (let lp ((conts conts) (max-label -1) (max-var -1))
+       (if (null? conts)
+           (values max-label max-var)
+           (call-with-values (lambda ()
+                               ((make-local-cont-folder max-label max-var)
+                                (lambda (label cont max-label max-var)
+                                  (values (max label max-label)
+                                          (match cont
+                                            (($ $kargs names vars body)
+                                             (fold max max-var vars))
+                                            (($ $kfun src meta self)
+                                             (max self max-var))
+                                            (_ max-var))))
+                                (car conts) max-label max-var))
+             (lambda (max-label max-var)
+               (lp (cdr conts) max-label max-var))))))))
 
-  (define (fun-folder fun seed)
-    (match fun
-      (($ $fun src meta free body)
-       (cont-folder body seed))))
-
-  (define (term-folder term seed)
-    (match term
-      (($ $letk conts body)
-       (fold cont-folder (term-folder body seed) conts))
-
-      (($ $continue k src exp)
-       (match exp
-         (($ $fun) (fun-folder exp seed))
-         (_ seed)))
-
-      (($ $letrec names syms funs body)
-       (fold fun-folder (term-folder body seed) funs))))
-
-  (fun-folder fun seed))
+(define (fold-conts proc seed fun)
+  ((make-global-cont-folder seed) proc fun seed))
 
-(define (fold-local-conts proc seed cont)
-  (define (cont-folder cont seed)
-    (match cont
-      (($ $cont k cont)
-       (let ((seed (proc k cont seed)))
-         (match cont
-           (($ $kargs names syms body)
-            (term-folder body seed))
+(define (fold-local-conts proc seed fun)
+  ((make-local-cont-folder seed) proc fun seed))
 
-           (($ $kentry self tail clauses)
-            (fold cont-folder (cont-folder tail seed) clauses))
+(define (visit-cont-successors proc cont)
+  (match cont
+    (($ $kargs names syms body)
+     (let lp ((body body))
+       (match body
+         (($ $letk conts body) (lp body))
+         (($ $letrec names vars funs body) (lp body))
+         (($ $continue k src exp)
+          (match exp
+            (($ $prompt escape? tag handler) (proc k handler))
+            (($ $branch kt) (proc k kt))
+            (_ (proc k)))))))
 
-           (($ $kclause arity body)
-            (cont-folder body seed))
+    (($ $kreceive arity k) (proc k))
 
-           (_ seed))))))
+    (($ $kclause arity ($ $cont kbody) #f) (proc kbody))
 
-  (define (term-folder term seed)
-    (match term
-      (($ $letk conts body)
-       (fold cont-folder (term-folder body seed) conts))
+    (($ $kclause arity ($ $cont kbody) ($ $cont kalt)) (proc kbody kalt))
 
-      (($ $continue) seed)
+    (($ $kfun src meta self tail ($ $cont clause)) (proc clause))
 
-      (($ $letrec names syms funs body) (term-folder body seed))))
+    (($ $kfun src meta self tail #f) (proc))
 
-  (cont-folder cont seed))
+    (($ $ktail) (proc))))