Skip to content

Instantly share code, notes, and snippets.

@deeglaze
Forked from dvanhorn/compile-0cfa.rkt
Created October 4, 2012 17:24
Show Gist options
  • Save deeglaze/3835103 to your computer and use it in GitHub Desktop.
Save deeglaze/3835103 to your computer and use it in GitHub Desktop.
Compilation for 0CFA with lazy dereferencing
#lang racket
(require racket/trace
(for-syntax syntax/parse))
;; 0CFA in the AAM style on some hairy Church numeral churning
;; Soundness, but at what cost?
(define <- (case-lambda))
(begin-for-syntax
(define-syntax-class guards
#:attributes ((guard-forms 1) (gv 1) (gfromv 1)) #:literals (<-)
(pattern ((~and (~seq (~or [i:id e:expr]
[(is:id ...) e0:expr]) ...)
(~seq start:expr ...))
(~optional (~seq [v:id <- (σ:expr fromv:expr)] ...)
#:defaults ([(v 1) #'()]
[(σ 1) #'()]
[(fromv 1) #'()])))
;; XXX: Switch these for laziness
;;#:with (guard-forms ...) #'(start ... [v (get-val σ fromv)] ...)
;;#:with (gv ...) #'() #:with (gfromv ...) #'()
#:with (guard-forms ...) #'(start ...)
#:with (gv ...) #'(v ...)
#:with (gfromv ...) #'(fromv ...)
)))
(define-syntax (for/get-vals stx)
(syntax-parse stx
[(_ form:id targets:expr gs:guards body1:expr body:expr ...)
(syntax/loc stx
(form targets (gs.guard-forms ...)
(let* ([gs.gv gs.gfromv] ...)
body1 body ...)))]))
(define-syntax-rule (for/union guards body1 body ...)
(for/get-vals for/fold ([res (set)]) guards (set-union res (let () body1 body ...))))
(define-syntax-rule (for*/union guards body1 body ...)
(for/get-vals for*/fold ([res (set)]) guards (set-union res (let () body1 body ...))))
;; (X -> Set X) -> (Set X) -> (Set X)
(define ((appl f) s)
(for/union ([x (in-set s)])
(f x)))
;; (X -> Set X) (Set X) -> (Set X)
;; Calculate fixpoint of (appl f).
(define (fix f s)
(let loop ((accum (set)) (front s))
(if (set-empty? front)
accum
(let ((new-front ((appl f) front)))
(loop (set-union accum front)
(set-subtract new-front accum))))))
;; An Exp is one of:
;; (var Lab Exp)
;; (num Lab Number)
;; (bln Lab Boolean)
;; (lam Lab Sym Exp)
;; (app Lab Exp Exp)
;; (rec Sym Lam)
;; (if Lab Exp Exp Exp)
(struct exp (lab) #:transparent)
(struct var exp (name) #:transparent)
(struct num exp (val) #:transparent)
(struct bln exp (b) #:transparent)
(struct lam exp (var exp) #:transparent)
(struct app exp (rator rand) #:transparent)
(struct rec (name fun) #:transparent)
(struct ife exp (t c a) #:transparent)
(struct 1op exp (o a) #:transparent)
(struct 2op exp (o a b) #:transparent)
;; A Val is one of:
;; - Number
;; - Boolean
;; - (clos Lab Sym Exp Env)
;; - (rlos Lab Sym Sym Exp Env)
(struct clos (l x e ρ) #:transparent)
(struct rlos (l f x e ρ) #:transparent)
;; A Cont is one of:
;; - 'mt
;; - (ar Exp Env Cont)
;; - (fn Val Cont)
;; - (ifk Exp Exp Env Cont)
;; - (1opk Opr Cont)
;; - (2opak Opr Exp Env Cont)
;; - (2opfk Opr Val Cont)
(struct ar (e ρ k) #:transparent)
(struct fn (v k) #:transparent)
(struct ifk (c a ρ k) #:transparent)
(struct 1opk (o k) #:transparent)
(struct 2opak (o e ρ k) #:transparent)
(struct 2opfk (o v k) #:transparent)
;; State
(struct state (σ) #:transparent)
(struct ev state (e ρ k) #:transparent)
(struct co state (k v) #:transparent)
(struct ap state (f a k) #:transparent)
(struct ap-op state (o vs k) #:transparent)
(struct ans state (v) #:transparent)
(define (lookup ρ σ x)
(hash-ref σ (hash-ref ρ x)))
(define (lookup-env ρ x)
(hash-ref ρ x))
(define (get-cont σ l)
(hash-ref σ l))
(define (extend ρ x v)
(hash-set ρ x v))
(define (join σ a s)
(hash-set σ a
(set-union s (hash-ref σ a (set)))))
(define-syntax-rule (do guards e)
(for/get-vals for*/fold ([res (set)]) guards (set-add res e)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Machine
(struct addr (a) #:transparent)
;; Store (Addr + Val) -> Set Val
;; XXX: Switch these for laziness
#;
(define-syntax-rule (get-val σ v)
(match v
[(addr loc) (hash-ref σ loc (λ () (error "~a ~a" loc σ)))]
[_ (set v)]))
(define-syntax-rule (get-val σ v) (set v))
(define (compile e)
(match e
[(var l x)
(λ (σ ρ k) (deref ρ σ k x))]
[(num l n) (λ (σ ρ k) (set (co σ k n)))]
[(bln l b) (λ (σ ρ k) (set (co σ k b)))]
[(lam l x e)
(define c (compile e))
(λ (σ ρ k) (set (co σ k (clos l x c ρ))))]
[(rec f (lam l x e))
(define c (compile e))
(λ (σ ρ k) (set (co σ k (rlos l f x c ρ))))]
[(app l e0 e1)
(define c0 (compile e0))
(define c1 (compile e1))
(λ (σ ρ k)
;; "ev" simulated for push's sake.
(define-values (σ* a) (push (ev σ (app l e0 e1) ρ k)))
(c0 σ* ρ (ar c1 ρ a)))]
[(ife l e0 e1 e2)
(define c0 (compile e0))
(define c1 (compile e1))
(define c2 (compile e2))
(λ (σ ρ k)
(define-values (σ* a) (push (ev σ (ife l e0 e1 e2) ρ k)))
(c0 σ* ρ (ifk c1 c2 ρ a)))]
[(1op l o e)
(define c (compile e))
(λ (σ ρ k)
(define-values (σ* a) (push (ev σ (1op l o e) ρ k)))
(c σ* ρ (1opk o a)))]
[(2op l o e0 e1)
(define c0 (compile e0))
(define c1 (compile e1))
(λ (σ ρ k)
(define-values (σ* a) (push (ev σ (2op l o e0 e1) ρ k)))
(c0 σ* ρ (2opak o c1 ρ a)))]))
;; State -> Set State
(define (step-compiled state)
;(printf "~a~n" state)
(match state
[(co σ k v)
(match k
['mt (do ([v <- (σ v)])
(ans σ v))]
[(ar c ρ l) (c σ ρ (fn v l))]
[(fn f l)
(do ([k (get-cont σ l)]
[f <- (σ f)])
(ap σ f v k))]
[(ifk c a ρ l)
(for*/union ([k (get-cont σ l)]
[v <- (σ v)])
((if v c a) σ ρ k))]
[(1opk o l)
(do ([k (get-cont σ l)]
[v <- (σ v)])
(ap-op σ o (list v) k))]
[(2opak o c ρ l)
(c σ ρ (2opfk o v l))]
[(2opfk o u l)
(do ([k (get-cont σ l)]
[v <- (σ v)]
[u <- (σ u)])
(ap-op σ o (list v u) k))])]
[(ap σ fun a k)
(match fun
[(or (clos _ _ c _)
(rlos _ _ _ c _))
(define-values (ρ* σ*) (bind state))
(c σ* ρ* k)]
;; stuck
[_ (set state)])]
[(ap-op σ o vs k)
(match* (o vs)
[('zero? (list (? number? n))) (set (co σ k (zero? n)))]
[('sub1 (list (? number? n))) (set (co σ k (widen (sub1 n))))]
[('add1 (list (? number? n))) (set (co σ k (widen (add1 n))))]
[('zero? (list 'number))
(set (co σ k #t)
(co σ k #f))]
[('sub1 (list 'number)) (set (co σ k 'number))]
[('* (list (? number? n) (? number? m)))
(set (co σ k (widen (* m n))))]
[('* (list (? number? n) 'number))
(set (co σ k 'number))]
[('* (list 'number 'number))
(set (co σ k 'number))]
[(_ _) (set state)])]
;; stuck or an answer
[_ (set state)]))
(define (step state)
(match state
[(ev σ e ρ k)
(match e
[(var l x) (deref ρ σ k x)]
[(num l n) (set (co σ k n))]
[(bln l b) (set (co σ k b))]
[(lam l x e) (set (co σ k (clos l x e ρ)))]
[(rec f (lam l x e)) (set (co σ k (rlos l f x e ρ)))]
[(app l f e)
(define-values (σ* a) (push state))
(set (ev σ* f ρ (ar e ρ a)))]
[(ife l e0 e1 e2)
(define-values (σ* a) (push state))
(set (ev σ* e0 ρ (ifk e1 e2 ρ a)))]
[(1op l o e)
(define-values (σ* a) (push state))
(set (ev σ* e ρ (1opk o a)))]
[(2op l o e f)
(define-values (σ* a) (push state))
(set (ev σ* e ρ (2opak o f ρ a)))])]
[(co σ k v)
(match k
['mt (do ([v <- (σ v)])
(ans σ v))]
[(ar e ρ l) (set (ev σ e ρ (fn v l)))]
[(fn f l)
(do ([k (get-cont σ l)]
[f <- (σ f)])
(ap σ f v k))]
[(ifk c a ρ l)
(do ([k (get-cont σ l)]
[v <- (σ v)])
(ev σ (if v c a) ρ k))]
[(1opk o l)
(do ([k (get-cont σ l)]
[v <- (σ v)])
(ap-op σ o (list v) k))]
[(2opak o e ρ l)
(set (ev σ e ρ (2opfk o v l)))]
[(2opfk o u l)
(do ([k (get-cont σ l)]
[v <- (σ v)]
[u <- (σ u)])
(ap-op σ o (list v u) k))])]
[(ap σ fun a k)
(match fun
[(clos l x e ρ)
(define-values (ρ* σ*) (bind state))
(set (ev σ* e ρ* k))]
[(rlos l f x e ρ)
(define-values (ρ* σ*) (bind state))
(set (ev σ* e ρ* k))]
[_ (set state)])]
[(ap-op σ o vs k)
(match* (o vs)
[('zero? (list (? number? n))) (set (co σ k (zero? n)))]
[('sub1 (list (? number? n))) (set (co σ k (widen (sub1 n))))]
[('add1 (list (? number? n))) (set (co σ k (widen (add1 n))))]
[('zero? (list 'number))
(set (co σ k #t)
(co σ k #f))]
[('sub1 (list 'number)) (set (co σ k 'number))]
[('* (list (? number? n) (? number? m)))
(set (co σ k (widen (* m n))))]
[('* (list (? number? n) 'number))
(set (co σ k 'number))]
[('* (list 'number 'number))
(set (co σ k 'number))]
[(_ _) (set state)])]
[_ (set state)]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Concrete semantics
#;#;#;
(define (widen b)
(cond [(number? b) b]
[else (error "Unknown base value" b)]))
(define (bind s)
(match s
[(ap σ (clos l x e ρ) v k)
(define a
(add1 (for/fold ([i 0])
([k (in-hash-keys σ)])
(max i k))))
(values (extend ρ x a)
(extend σ a (get-val σ v)))]
[(ap σ (rlos l f x e ρ) v k)
(define a
(add1 (for/fold ([i 0])
([k (in-hash-keys σ)])
(max i k))))
(define b (add1 a))
(values (extend (extend ρ x a) f b)
(join (join σ a (get-val σ v)) b (set (rlos l f x e ρ))))]))
(define (push s)
(match s
[(ev σ e ρ k)
(define a
(add1 (for/fold ([i 0])
([k (in-hash-keys σ)])
(max i k))))
(values (join σ a (set k))
a)]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 0CFA-style Abstract semantics
(define (widen b)
(cond [(number? b) 'number]
[else (error "Unknown base value" b)]))
(define (bind s)
(match s
[(ap σ (clos l x e ρ) v k)
(values (extend ρ x x)
(join σ x (get-val σ v)))]
[(ap σ (rlos l f x e ρ) v k)
(values (extend (extend ρ x x) f f)
(join (join σ x (get-val σ v)) f (set (rlos l f x e ρ))))]))
(define (push s)
(match s
[(ev σ e ρ k)
(define a (exp-lab e))
(values (join σ a (set k))
a)]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Lazy/non-lazy
;; XXX: Switch these for laziness
#;
(define-syntax-rule (deref ρ σ k x)
(set (co σ k (addr (lookup-env ρ x)))))
(define-syntax-rule (deref ρ σ k x)
(do ([v (lookup ρ σ x)])
(co σ k v)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Exp -> Set Val
;; 0CFA without store widening
(define (aval e)
(for/set ([s (fix step (inj e))]
#:when (ans? s))
(ans-v s)))
(define (aval-compiled e)
(for/set ([s (fix step-compiled (inj-compiled e))]
#:when (ans? s))
(ans-v s)))
;; Exp -> Set Vlal
;; 0CFA with store widening
(define (aval^ e)
(for/union ([s (fix step (inj-wide e))])
(match s
[(cons cs σ)
(for/set ([c cs]
#:when (ans^? c))
(ans^-v c))])))
(define (aval^-compiled e)
(for/union ([s (fix wide-step-compiled (inj-wide-compiled e))])
(match s
[(cons cs σ)
(for/set ([c cs]
#:when (ans^? c))
(ans^-v c))])))
;; Exp -> Set State
(define (inj-compiled e)
((compile e) (hash) (hash) 'mt))
;; Exp -> Set State^
(define (inj-wide-compiled e)
(for/first ([s (inj-compiled e)])
(set (cons (set (s->c s))
(state-σ s)))))
;; Exp -> Set State
(define (inj e)
(set (ev (hash) e (hash) 'mt)))
;; Exp -> Set State^
(define (inj-wide e)
(set (cons (set (ev^ e (hash) 'mt)) (hash))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Widening State to State^
;; State^ = (cons (Set Conf) Store)
;; Conf
(struct ev^ (e ρ k) #:transparent)
(struct co^ (k v) #:transparent)
(struct ap^ (f a k) #:transparent)
(struct ap-op^ (o vs k) #:transparent)
(struct ans^ (v) #:transparent)
;; Conf Store -> State
(define (c->s c σ)
(match c
[(ev^ e ρ k) (ev σ e ρ k)]
[(co^ k v) (co σ k v)]
[(ap^ f a k) (ap σ f a k)]
[(ap-op^ o vs k) (ap-op σ o vs k)]
[(ans^ v) (ans σ v)]))
;; State -> Conf
(define (s->c s)
(match s
[(ev _ e ρ k) (ev^ e ρ k)]
[(co _ k v) (co^ k v)]
[(ap _ f a k) (ap^ f a k)]
[(ap-op _ o vs k) (ap-op^ o vs k)]
[(ans _ v) (ans^ v)]))
;; Store Store -> Store
(define (join-store σ1 σ2)
(for/fold ([σ σ1])
([k×v (in-hash-pairs σ2)])
(hash-set σ (car k×v)
(set-union (cdr k×v)
(hash-ref σ (car k×v) (set))))))
;; Set State -> Store
(define (join-stores ss)
(for/fold ([σ (hash)])
([s ss])
(join-store σ (state-σ s))))
;; State^ -> { State^ }
(define (wide-step state)
(match state
[(cons cs σ)
(define ss (for/set ([c cs]) (c->s c σ)))
(define ss* ((appl step) ss))
(set (cons (for/set ([s ss*]) (s->c s))
(join-stores ss*)))]))
(define (wide-step-compiled state)
(match state
[(cons cs σ)
(define ss (for/set ([c cs]) (c->s c σ)))
(define ss* ((appl step-compiled) ss))
(set (cons (for/set ([s ss*]) (s->c s))
(join-stores ss*)))]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Parser
(define (parse sexp)
(match sexp
[`(let* () ,e) (parse e)]
[`(let* ((,x ,e) . ,r) ,b)
(app (gensym)
(lam (gensym) x (parse `(let* ,r ,b)))
(parse e))]
[`(lambda (,x) ,e)
(lam (gensym) x (parse e))]
[`(if ,e0 ,e1 ,e2)
(ife (gensym) (parse e0) (parse e1) (parse e2))]
[`(rec ,f ,e)
(rec f (parse e))]
[`(sub1 ,e)
(1op (gensym) 'sub1 (parse e))]
[`(add1 ,e)
(1op (gensym) 'add1 (parse e))]
[`(zero? ,e)
(1op (gensym) 'zero? (parse e))]
[`(* ,e0 ,e1)
(2op (gensym) '* (parse e0) (parse e1))]
[`(,e0 ,e1)
(app (gensym)
(parse e0)
(parse e1))]
[(? boolean? b) (bln (gensym) b)]
[(? number? n) (num (gensym) n)]
[(? symbol? s) (var (gensym) s)]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Computing with Church numerals
(define P
;; Ian's example, curried, alpha renamed and
;; let* in place of define where possible.
'(let* ((plus (lambda (p1)
(lambda (p2)
(lambda (pf)
(lambda (x) ((p1 pf) ((p2 pf) x)))))))
(mult (lambda (m1)
(lambda (m2)
(lambda (mf) (m2 (m1 mf))))))
(pred (lambda (n)
(lambda (rf)
(lambda (rx)
(((n (lambda (g) (lambda (h) (h (g rf)))))
(lambda (ignored) rx))
(lambda (id) id))))))
(sub (lambda (s1)
(lambda (s2)
((s2 pred) s1))))
(church0 (lambda (f0) (lambda (x0) x0)))
(church1 (lambda (f1) (lambda (x1) (f1 x1))))
(church2 (lambda (f2) (lambda (x2) (f2 (f2 x2)))))
(church3 (lambda (f3) (lambda (x3) (f3 (f3 (f3 x3))))))
(church0? (lambda (z) ((z (lambda (zx) #f)) #t)))
(church=? (rec c=?
(lambda (e1)
(lambda (e2)
(if (church0? e1)
(church0? e2)
(if (church0? e2)
#f
((c=? ((sub e1) church1)) ((sub e2) church1)))))))))
((church=? ((mult church2) ((plus church1) church3)))
((plus ((mult church2) church1)) ((mult church2) church3)))))
#;(aval (parse P))
#;(aval^ (parse P))
(define FACT5
'((rec f (lambda (x)
(if (zero? x)
1
(* x (f (sub1 x))))))
5))
#;(aval (parse FACT5))
; soundness recovered...
(aval (parse '(((lambda (suc)
(lambda (zero) (suc zero)))
(lambda (n) (add1 n)))
0)))
; but this diverges
(aval^-compiled (parse P))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment