; Copyright 2009 Uwe Hollerbach ; $Id: delcont.scm,v 1.15 2009-08-09 05:38:50 uwe Exp $ ; BSD3 ; Delimited continuations: these look simpler than call/cc, just a fairly ; simple code transformation. This code implements that code transformation, ; however it's not quite full-strength: in real code, the (shift) could be ; hidden inside a function, and in that case this version would not work. But ; for reset/shift that's all visible in one s-expr, I think this does pretty ; much the right thing. ; From CC Shan, "Shift to Control" ; M[(reset V)] -> M[V] ; M[(reset C [(shift f E)])] -> M[(reset E')] ; where E' = E{f -> (lambda (x) (reset C[x]))} ; M[(reset C [(control f E)])] -> M[(reset E')] ; where E' = E{f -> (lambda (x) C[x])} ; M[(reset C [(shift0 f E)])] -> M[E'] ; where E' = E{f -> (lambda (x) (reset C[x]))} ; M[(reset C [(control0 f E)])] -> M[E'] ; where E' = E{f -> (lambda (x) C[x])} ; TODO: the multiple-expressions handling in reset and int-s/c is ; seriously hacky: reset just wraps its args in a '(begin ...) if ; there are multiple args... gotta be a better way to do that? ; Still, this works and allows testing. ; These are renamed to reset/m etc, all with trailing /m, to denote ; that they're macros, and to not interfere with the native versions ; of at least reset and shift. (define *delcont-list* '()) (defmacro (reset/m . expr) (letrec* ((id (gensym)) (ret (gensym)) (err (gensym)) (flag #f) (elist (cond ((null? expr) '()) ((null? (cdr expr)) (car expr)) (else (cons 'begin expr)))) (r-aux (lambda (ex) (cond ((not (list? ex)) ex) ((null? ex) ex) ((or (eqv? 'shift/m (car ex)) (eqv? 'shift0/m (car ex)) (eqv? 'control/m (car ex)) (eqv? 'control0/m (car ex))) (let ((f1 (or (eqv? 'shift/m (car ex)) (eqv? 'shift0/m (car ex)))) (f2 (or (eqv? 'shift/m (car ex)) (eqv? 'control/m (car ex))))) (set! flag #t) (if (and (> (length ex) 2) (symbol? (cadr ex))) (append (list 'int-s/c id (gensym) f1 f2) (r-aux (cdr ex))) (raise "malformed shift/control expression!" ex)))) ((eqv? 'int-s/c (car ex)) (set! flag #t) (append (list 'int-s/c id) (cddr ex))) (else (map r-aux ex))))) (ex (r-aux elist))) (if flag (begin (set! *delcont-list* (cons ex *delcont-list*)) `(let ((,ret (guard (,err ((and (pair? ,err) (eqv? ',id (car ,err))) (cadr ,err))) ,ex))) (set! *delcont-list* (cdr *delcont-list*)) ,ret)) elist))) (defmacro (int-s/c mark id f1 f2 name . expr) (letrec* ((var (gensym)) (s-aux (lambda (ex) (cond ((not (list? ex)) ex) ((null? ex) ex) ((eqv? 'int-s/c (car ex)) (if (eqv? id (caddr ex)) var ex)) (else (map s-aux ex))))) (subst-expr (lambda (ex) (let ((body (s-aux ex))) (if f1 `(lambda (,var) (reset ,body)) `(lambda (,var) ,body))))) (lam-ex (subst-expr (car *delcont-list*)))) (if f2 `(raise ',mark (let ((,name ,lam-ex)) (reset ,@expr))) `(raise ',mark (let ((,name ,lam-ex)) ,@expr))))) ; aliases -- in this macro-based approach, they should be just sugar (define reset0/m reset/m) (define prompt/m reset/m) (define prompt0/m reset/m) ; stupid-catchers (defmacro (shift/m sym expr) (raise "oops, naked delcont form" 'shift/m sym expr)) (defmacro (shift0/m sym expr) (raise "oops, naked delcont form" 'shift0/m sym expr)) (defmacro (control/m sym expr) (raise "oops, naked delcont form" 'control/m sym expr)) (defmacro (control0/m sym expr) (raise "oops, naked delcont form" 'control0/m sym expr))