Skip to content

Instantly share code, notes, and snippets.

@swannodette
Forked from dvanhorn/delta-0cfa.rkt
Created October 6, 2012 15:35
Show Gist options
  • Save swannodette/3845241 to your computer and use it in GitHub Desktop.
Save swannodette/3845241 to your computer and use it in GitHub Desktop.
Store delta 0CFA
#lang racket
;; 0CFA in the AAM style on some hairy Church numeral churning
;; + compilation phase
;; + lazy non-determinism
;; + specialized step & iterator 0m34.248s vs 0m16.339s
;; + compute store ∆s 0m16.339s vs 0m1.065s (!!!)
;; 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)
;; State = (cons Conf Store)
;; State^ = (cons (Set Conf) Store)
;; Conf
(struct co^ (k v) #:transparent)
(struct ap^ (f a k) #:transparent)
(struct ap-op^ (o vs k) #:transparent)
(struct ans^ (v) #:transparent)
;; Comp = Store Env Cont -> State^
;; A Cont is one of:
;; - 'mt
;; - (ar Comp Env Cont)
;; - (fn Val Cont)
;; - (ifk Comp Comp Env Cont)
;; - (1opk Opr Cont)
;; - (2opak Opr Comp 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)
(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 (join-one σ a x)
(hash-set σ a
(set-add (hash-ref σ a (set)) x)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; "Compiled" Machine
;; Compile away interpretive overhead of "ev" states
;; Expr -> Comp
(define (compile e)
(match e
[(var l x)
(λ (∆ ρ k)
(cons ∆ (set (co^ k (addr (lookup-env ρ x))))))]
[(num l n) (λ (∆ ρ k) (cons ∆ (set (co^ k n))))]
[(bln l b) (λ (∆ ρ k) (cons ∆ (set (co^ k b))))]
[(lam l x e)
(define c (compile e))
(λ (∆ ρ k) (cons ∆ (set (co^ k (clos l x c ρ)))))]
[(rec f (lam l x e))
(define c (compile e))
(λ (∆ ρ k) (cons ∆ (set (co^ k (rlos l f x c ρ)))))]
[(app l e0 e1)
(define c0 (compile e0))
(define c1 (compile e1))
(λ (∆ ρ k)
(define-values (∆* a) (push∆ ∆ l ρ 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∆ ∆ l ρ k))
(c0 ∆* ρ (ifk c1 c2 ρ a)))]
[(1op l o e)
(define c (compile e))
(λ (∆ ρ k)
(define-values (∆* a) (push∆ ∆ l ρ k))
(c ∆* ρ (1opk o a)))]
[(2op l o e0 e1)
(define c0 (compile e0))
(define c1 (compile e1))
(λ (∆ ρ k)
(define-values (∆* a) (push∆ ∆ l ρ k))
(c0 ∆* ρ (2opak o c1 ρ a)))]))
(struct addr (a) #:transparent)
;; Store (Addr + Val) -> Set Val
(define (get-val σ v)
(match v
[(addr loc) (hash-ref σ loc (λ () (error "~a ~a" loc σ)))]
[_ (set v)]))
;; "Bytecode" interpreter
;; State -> State^
;; State -> (cons [Listof (cons Addr (Setof Storable))] (Setof Conf))
(define (step-compiled^ s)
(match s
[(cons σ (co^ k v))
(match k
['mt (cons '()
(for*/set ((v (get-val σ v)))
(ans^ v)))]
[(ar c ρ l) (c '() ρ (fn v l))]
[(fn f l)
(cons '()
(for*/set ([k (get-cont σ l)]
[f (get-val σ f)])
(ap^ f v k)))]
[(ifk c a ρ l)
(define res^
(for*/set ([k (get-cont σ l)]
[v (get-val σ v)])
((if v c a) '() ρ k)))
(define-values (∆* cs*)
(for/fold ([∆ '()] [cs (set)])
([s res^])
(match s
[(cons ∆* cs*)
(values (append ∆* ∆)
(set-union cs* cs))])))
(cons ∆* cs*)]
[(1opk o l)
(cons '()
(for*/set ([k (get-cont σ l)]
[v (get-val σ v)])
(ap-op^ o (list v) k)))]
[(2opak o c ρ l)
(c '() ρ (2opfk o v l))]
[(2opfk o u l)
(cons '()
(for*/set ([k (get-cont σ l)]
[v (get-val σ v)]
[u (get-val σ u)])
(ap-op^ o (list v u) k)))])]
[(cons σ (ap^ fun a k))
(match fun
[(clos l x c ρ)
(define-values (ρ* ∆*) (bind s))
(c ∆* ρ* k)]
[(rlos l f x c ρ)
(define-values (ρ* ∆*) (bind s))
(c ∆* ρ* k)]
;; Anything else is stuck
[_ (cons '() (set))])]
[(cons σ (ap-op^ o vs k))
(match* (o vs)
[('zero? (list (? number? n))) (cons '() (set (co^ k (zero? n))))]
[('sub1 (list (? number? n))) (cons '() (set (co^ k (widen (sub1 n)))))]
[('add1 (list (? number? n))) (cons '() (set (co^ k (widen (add1 n)))))]
[('zero? (list 'number))
(cons '() (set (co^ k #t)
(co^ k #f)))]
[('sub1 (list 'number)) (cons '() (set (co^ k 'number)))]
[('* (list (? number? n) (? number? m)))
(cons '() (set (co^ k (widen (* m n)))))]
[('* (list (? number? n) 'number))
(cons '() (set (co^ k 'number)))]
[('* (list 'number 'number))
(cons '() (set (co^ k 'number)))]
;; Anything else is stuck
[(_ _) (cons '() (set))])]
[(cons σ c)
(cons '() (set))]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 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)
(join σ a (set 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 (set v)) b (set (rlos l f x e ρ))))]))
(define (push σ l ρ 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
[(cons σ (ap^ (clos l x e ρ) v k))
(values (extend ρ x x)
(list (cons x (get-val σ v))))]
[(cons σ (ap^ (rlos l f x e ρ) v k))
(values (extend (extend ρ x x) f f)
(list (cons f (set (rlos l f x e ρ)))
(cons x (get-val σ v))))]))
(define (push σ l ρ k)
(values (join-one σ l k)
l))
(define (push∆ ∆ l ρ k)
(values (cons (cons l (set k)) ∆)
l))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Exp -> Set Val
;; 0CFA with store widening and specialized iteration
(define (aval^ e)
(define fst (inj e))
(define snd (wide-step-specialized fst))
;; wide-step-specialized is monotonic so we only need to check the current
;; state against it's predecessor to see if we have reached a fixpoint.
(let loop ((next snd) (prev fst))
(if (equal? next prev)
(for/set ([c (cdr prev)]
#:when (ans^? c))
(ans^-v c))
(loop (wide-step-specialized next) next))))
;; Exp -> Set State
(define (inj e)
(match ((compile e) '() (hash) 'mt)
[(cons ∆ cs)
(cons (update ∆ (hash)) cs)]))
(define (update ∆ σ)
(match ∆
['() σ]
[(cons (cons a xs) ∆)
(update ∆ (join σ a xs))]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Widening State to State^
;; 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))))))
;; State^ -> State^
;; Specialized from wide-step : State^ -> { State^ } ≈ State^ -> State^
(define (wide-step-specialized state)
(match state
[(cons σ cs)
(define-values (cs* ∆)
(for/fold ([cs* (set)] [∆* '()])
([c cs])
(match (step-compiled^ (cons σ c))
[(cons ∆** cs**)
(values (set-union cs* cs**) (append ∆** ∆*))])))
(cons (update ∆ σ) (set-union cs cs*))]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 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)))
(c->n (lambda (cn) ((cn (lambda (u) (add1 u))) 0)))
(church=? (rec c=?
(lambda (e1)
(lambda (e2)
(if (church0? e1)
(church0? e2)
(if (church0? e2)
#f
((c=? ((sub e1) church1)) ((sub e2) church1)))))))))
;; multiplication distributes over addition
((church=? ((mult church2) ((plus church1) church3)))
((plus ((mult church2) church1)) ((mult church2) church3)))))
(aval^ (parse P))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment