syndicate-2017/racket/syndicate/little-actors/core.rkt

693 lines
23 KiB
Racket
Raw Normal View History

2017-01-27 21:29:54 +00:00
#lang racket
(require syndicate/monolithic)
(require syndicate/trie)
(require racket/set)
(module+ test
(require rackunit))
(define mt-scn (scn trie-empty))
;; an `exp` is either
2017-01-30 22:24:49 +00:00
;; ('lambda (var ...) exp) or
;; (exp exp ...) or
2017-01-27 21:29:54 +00:00
;; ('begin exp ...) or
;; ('let (var exp) exp) or
;; ('if exp exp exp) or
;; ('send! exp) or
;; ('react O ...) or
;; ('actor ('react O ...)) or
2017-01-30 19:40:27 +00:00
;; ('dataspace actor ...) or
;; ('outbound exp) or
;; ('inbound exp) or
2017-01-27 21:29:54 +00:00
;; ('set! var exp) or
;; ('read var) or
2017-01-30 19:40:27 +00:00
;; ('list exp ...) or
2017-01-27 21:29:54 +00:00
;; (primop exp ...) or
;; atom
2017-01-30 18:57:55 +00:00
;; a `val` is either
;; atom or
2017-01-30 19:40:27 +00:00
;; ('list val ...) or
;; (outbound val) or
2017-01-30 22:24:49 +00:00
;; (inbound val) or
;; (closure Γ ('lambda (var ...) exp))
(struct closure (env fun) #:transparent)
2017-01-30 18:57:55 +00:00
2017-01-27 21:29:54 +00:00
;; `primop` is one of
;; + * / - and or not equal? null? car cdr printf
2017-01-30 18:57:55 +00:00
;; an `O` (endpoint) is either
2017-01-27 21:29:54 +00:00
;; ('field var exp) or
;; ('assert exp) or
;; ('on E exp ...) or
;; ('stop-when E exp ...) or
;; ('on-start exp ...)
;; a `facet` is ('react O ...)
2017-01-30 19:40:27 +00:00
;; an `actor` is
;; ('actor facet) or
;; ('dataspace actor ...)
2017-01-27 21:29:54 +00:00
;; an E is either
;; ('asserted pat) or
;; ('retracted pat) or
;; (message pat)
;; a pat is either
;; $var or
;; _ or
;; exp or
2017-01-30 19:40:27 +00:00
;; ('observe pat) or
;; ('inbound pat) or
;; ('outbound pat) or
2017-01-27 21:29:54 +00:00
;; ('list pat ...)
;; a Γ is a (Listof Binding)
2017-01-30 18:57:55 +00:00
;; a Binding is (binding var val)
2017-01-27 21:29:54 +00:00
(struct binding (id v) #:transparent)
;; a σ is either
2017-01-30 18:57:55 +00:00
;; (Hashof var val) or
2017-01-27 21:29:54 +00:00
;; (store-concat σ σ)
(struct store-concat (σ1 σ2) #:transparent)
;; σ1 is the "parent" to σ2 (local)
;; a FacetTree is (facet-tree '(react O ...) Γ σ (Listof FacetTree))
(struct facet-tree (stx env sto children) #:transparent)
;; an ActorState is (actor-state π FacetTree)
(struct actor-state (π ft) #:transparent)
;; a π is a trie
(define π-union assertion-set-union)
2017-01-30 18:57:55 +00:00
;; a Program is a (Listof actor)
2017-01-27 21:29:54 +00:00
(define mt-Γ (list))
(define mt-σ (hash))
2017-01-30 18:57:55 +00:00
;; env-lookup : Γ var -> val
2017-01-27 21:29:54 +00:00
;; or throws an error for unbound variables
(define (env-lookup Γ id)
(match Γ
['() (error 'env-lookup "unbound variable: ~v" id)]
[(cons (binding x v) rest)
(if (equal? id x)
v
(env-lookup rest id))]))
2017-01-30 18:57:55 +00:00
;; extend-env : Γ var val -> Γ
2017-01-27 21:29:54 +00:00
(define (extend-env Γ id v)
(cons (binding id v) Γ))
2017-01-30 18:57:55 +00:00
;; update-sto : σ var val -> σ
2017-01-27 21:29:54 +00:00
;; update the value of var in the store, if present.
;; otherwise throw an error
(define (update-sto σ id v)
(let search ([σ σ]
[k-succ identity]
[k-fail (lambda () (error 'update-sto "unbound field: ~v" id))])
(match σ
[(store-concat σ1 σ2)
(search σ2
(lambda (new-σ2) (k-succ (store-concat σ1 new-σ2)))
(lambda () (search σ1
(lambda (new-σ1) (k-succ (store-concat new-σ1 σ2)))
k-fail)))]
[_
(if (hash-has-key? σ id)
(k-succ (hash-set σ id v))
(k-fail))])))
(module+ test
(let* ([s1 (make-store '(balance . 100))]
[s2 (store-concat mt-σ s1)]
[s3 (store-concat s1 mt-σ)])
(check-equal? (update-sto s2 'balance 50)
(store-concat mt-σ (make-store '(balance . 50))))
(check-equal? (update-sto s3 'balance 50)
(store-concat (make-store '(balance . 50)) mt-σ))))
(define (primop? x)
(member x '(+ - * / - and or not equal? null? car cdr printf)))
2017-01-30 18:57:55 +00:00
;; apply-primop : primop (Listof val) -> val
2017-01-27 21:29:54 +00:00
(define (apply-primop op args)
(match* (op args)
[('+ `(,v1 ,v2))
(+ v1 v2)]
[('- `(,v1 ,v2))
(- v1 v2)]
[('* `(,v1 ,v2))
(* v1 v2)]
[('/ `(,v1 ,v2))
(/ v1 v2)]
[('and `(,v1 ,v2))
(and v1 v2)]
[('or `(,v1 ,v2))
(and v1 v2)]
[('equal? `(,v1 ,v2))
(equal? v1 v2)]
[('not `(,v))
(not v)]
[('null? '(list))
#t]
[('null? _)
#f]
[('car `(list ,e ,es ...))
e]
[('cdr `(list ,e ,es ...))
es]
[('printf args)
(apply printf args)]
[(_ _)
(error 'apply-primop "invalid primitive application: ~v ~v" op args)]))
2017-01-30 18:57:55 +00:00
;; sto-fetch : σ var -> val
2017-01-27 21:29:54 +00:00
;; retrieve the value of field var.
;; if not present throw an error
(define (sto-fetch σ id)
(let search ([σ σ]
[k (lambda () (error 'update-sto "unbound field: ~v" id))])
(match σ
[(store-concat σ1 σ2)
(search σ2 (lambda () (search σ1 k)))]
[_
(if (hash-has-key? σ id)
(hash-ref σ id)
(k))])))
2017-01-30 18:57:55 +00:00
;; make-store : (Listof (cons var val)) -> σ
2017-01-27 21:29:54 +00:00
(define (make-store . vs)
(make-immutable-hash vs))
2017-02-03 20:16:16 +00:00
;; A (Continue A) is (continue A σ (Listof Action) (Listof FacetTree))
(struct continue (v sto as fs) #:transparent)
;; A Stop is (stop σ (Listof Action))
(struct stop (sto as) #:transparent)
;; A (Result A) is a Stop or (Continue A)
;; result-bind : Result (Any σ Any ... -> Result) Any ... -> Result
(define (result-bind r f . extra-args)
(match r
[(continue v σ as fs)
(match (apply f (cons v (cons σ extra-args)))
[(continue next-v next-σ more-as more-fs)
(continue next-v next-σ (append as more-as) (append fs more-fs))]
[s s])]
[s s]))
;; result-map : (Result A) (A -> B) -> (Result B)
(define (result-map f r)
(match r
[(continue v σ as fs)
(continue (f v) σ as fs)]
[s s]))
;; sequence-steps : Any σ (Listof (Any σ -> Result)) -> Result
(define (sequence-steps v σ steps)
(for/fold ([r (inj-result v σ)])
([s (in-list steps)])
(result-bind r s)))
;; inj-result : A σ -> (Continue A)
(define (inj-result v σ)
(continue v σ (list) (list)))
;; for-steps : A σ (Sequenceof B) (A σ B -> (Result A)) -> (Result A)
(define (for-steps v σ seq f)
(for/fold ([r (inj-result v σ)])
([x seq])
(result-bind r f x)))
;; boot-facet : facet Γ σ -> (Values σ (Listof Action) FacetTree)
2017-01-27 21:29:54 +00:00
(define (boot-facet f Γ σ)
(define initial-sto (initial-store f Γ σ))
2017-02-03 20:16:16 +00:00
(match-define (continue _ (store-concat parent-sto facet-sto) as fs)
(eval-start-actions f Γ (store-concat σ initial-sto)))
(values parent-sto as (facet-tree f Γ facet-sto fs)))
2017-01-27 21:29:54 +00:00
;; initial-store : facet Γ σ -> σ
;; only bad people would put effects here.
(define (initial-store f Γ σ)
(match-define `(react ,O ...) f)
(define locations
(for/fold ([locations (list)])
2017-02-03 20:16:16 +00:00
([o (in-list O)])
(match o
[`(field ,id ,exp)
(match-define (continue v _ _ _) (eval-exp exp Γ σ))
(cons (cons id v) locations)]
[_ locations])))
2017-01-27 21:29:54 +00:00
(apply make-store locations))
2017-02-03 20:16:16 +00:00
;; eval-start-actions : facet Γ σ -> (Continue #f)
2017-01-27 21:29:54 +00:00
(define (eval-start-actions f Γ σ)
(match-define `(react ,O ...) f)
2017-02-03 20:16:16 +00:00
(for-steps #f σ (in-list O)
(lambda (_ σ o)
(match o
[`(on-start ,exp ...)
(eval-exp* exp Γ σ)]
[_
(inj-result #f σ)]))))
2017-01-30 19:40:27 +00:00
;; boot-actor : actor Γ -> Action
(define (boot-actor a Γ)
(match a
[`(actor ,facet)
(define-values (_ as ft) (boot-facet facet Γ mt-σ))
(define assertions (ft-assertions ft mt-σ))
(spawn actor-behavior
(actor-state trie-empty ft)
(cons (scn assertions) as))]
[`(dataspace ,as ...)
(define boot-actions (for/list ([a (in-list as)])
(boot-actor a Γ)))
(spawn-dataspace boot-actions)]))
2017-02-03 20:16:16 +00:00
;; eval-exp : exp Γ σ -> (Continue val)
2017-01-27 21:29:54 +00:00
(define (eval-exp e Γ σ)
(match e
[`(react ,O ...)
(define-values (new-sto as ft) (boot-facet e Γ σ))
2017-02-03 20:16:16 +00:00
(continue (void) new-sto as (list ft))]
2017-01-30 22:24:49 +00:00
[`(actor ,_)
2017-01-27 21:29:54 +00:00
;; don't pass in parent store
2017-01-30 19:40:27 +00:00
(define spawn-action (boot-actor e Γ))
2017-02-03 20:16:16 +00:00
(continue (void) σ (list spawn-action) (list))]
2017-01-30 19:40:27 +00:00
[`(dataspace ,actors ...)
2017-02-03 20:16:16 +00:00
(continue (void) σ (list (boot-actor e Γ)) (list))]
2017-01-30 19:40:27 +00:00
[`(outbound ,exp)
2017-02-03 20:16:16 +00:00
(match-define (continue v new-sto as facets) (eval-exp exp Γ σ))
(continue (outbound v) σ as facets)]
2017-01-30 19:40:27 +00:00
[`(inbound ,exp)
2017-02-03 20:16:16 +00:00
(match-define (continue v new-sto as facets) (eval-exp exp Γ σ))
(continue (inbound v) σ as facets)]
2017-01-27 21:29:54 +00:00
[(? symbol? id)
(let ([v (env-lookup Γ id)])
2017-02-03 20:16:16 +00:00
(continue v σ (list) (list)))]
2017-01-30 22:24:49 +00:00
[`(lambda (,vars ...) ,exp)
(define c (closure Γ e))
2017-02-03 20:16:16 +00:00
(continue c σ (list) (list))]
2017-01-27 21:29:54 +00:00
[`(begin ,es ...)
2017-02-03 20:16:16 +00:00
(for-steps (void) σ (in-list es)
(lambda (v σ e) (eval-exp e Γ σ)))]
2017-01-27 21:29:54 +00:00
[`(list ,es ...)
2017-02-03 20:16:16 +00:00
(define res (for-steps (list) σ (in-list es)
(lambda (rev-vs σ e)
(result-map (lambda (v) (cons v rev-vs))
(eval-exp e Γ σ)))))
(result-map (lambda (rev-vs) (cons 'list (reverse rev-vs)))
res)]
2017-01-27 21:29:54 +00:00
[`(let (,x ,exp) ,body-exp)
2017-02-03 20:16:16 +00:00
(result-bind (eval-exp exp Γ σ)
(lambda (v new-sto)
(define new-Γ (extend-env Γ x v))
(eval-exp body-exp new-Γ new-sto)))]
2017-01-27 21:29:54 +00:00
[`(if ,pred-exp ,then-exp ,else-exp)
2017-02-03 20:16:16 +00:00
(result-bind (eval-exp pred-exp Γ σ)
(lambda (v new-sto)
(if v
(eval-exp then-exp Γ new-sto)
(eval-exp else-exp Γ new-sto))))]
2017-01-27 21:29:54 +00:00
[`(send! ,exp)
2017-02-03 20:16:16 +00:00
(match-define (continue v new-sto as facets) (eval-exp exp Γ σ))
(continue (void) new-sto (append as (list (message v))) facets)]
2017-01-27 21:29:54 +00:00
[`(set! ,id ,exp)
2017-02-03 20:16:16 +00:00
(match-define (continue v new-sto as facets) (eval-exp exp Γ σ))
2017-01-27 21:29:54 +00:00
(define result-sto (update-sto new-sto id v))
2017-02-03 20:16:16 +00:00
(continue (void) result-sto as facets)]
2017-01-27 21:29:54 +00:00
[`(read ,id)
(define v (sto-fetch σ id))
2017-02-03 20:16:16 +00:00
(continue v σ (list) (list))]
2017-01-27 21:29:54 +00:00
[`(,primop ,exp ..1)
#:when (primop? primop)
2017-02-03 20:16:16 +00:00
(result-bind (eval-exp* exp Γ σ)
(lambda (arg-vs new-sto)
(inj-result (apply-primop primop arg-vs) σ)))]
2017-01-30 22:24:49 +00:00
[`(,f-exp ,exps ...)
2017-02-03 20:16:16 +00:00
(result-bind (eval-exp f-exp Γ σ)
(lambda (f-v new-sto)
(unless (closure? f-v) (error 'eval-exp "tried to apply non-function ~v" f-v))
(result-bind (eval-exp* exps Γ σ)
(lambda (arg-vs final-sto)
(match-define (closure clo-env `(lambda (,vars ...) ,body-exp)) f-v)
(unless (= (length arg-vs) (length vars))
(error 'eval-exp "wrong number of arguments; expected ~v, got ~v" (length vars) (length arg-vs)))
(define new-env (append (map binding vars arg-vs) clo-env))
(eval-exp body-exp new-env final-sto)))))]
[x (continue x σ (list) (list))]))
2017-01-27 21:29:54 +00:00
(module+ test
;; sequencing result
2017-02-03 20:16:16 +00:00
(match-let ([(continue v s as f) (eval-exp `(begin 1 2 3) mt-Γ mt-σ)])
2017-01-27 21:29:54 +00:00
(check-equal? v 3))
;; variable lookup
2017-02-03 20:16:16 +00:00
(match-let ([(continue v s as f) (eval-exp 'x (list (binding 'x "hello")
(binding 'y "bye")
(binding 'x "world"))
mt-σ)])
2017-01-30 22:24:49 +00:00
(check-equal? v "hello"))
2017-01-27 21:29:54 +00:00
;; variable binding
2017-02-03 20:16:16 +00:00
(match-let ([(continue v s as f) (eval-exp '(let (y 12) "cake") mt-Γ mt-σ)])
2017-01-30 22:24:49 +00:00
(check-equal? v "cake"))
2017-02-03 20:16:16 +00:00
(match-let ([(continue v s as f) (eval-exp '(let (y 12) y) mt-Γ mt-σ)])
2017-01-27 21:29:54 +00:00
(check-equal? v 12))
;; if
2017-02-03 20:16:16 +00:00
(match-let ([(continue v s as f) (eval-exp '(if #f 5 6) mt-Γ mt-σ)])
2017-01-27 21:29:54 +00:00
(check-equal? v 6))
2017-02-03 20:16:16 +00:00
(match-let ([(continue v s as f) (eval-exp '(if #t 5 6) mt-Γ mt-σ)])
2017-01-27 21:29:54 +00:00
(check-equal? v 5))
;; send!
2017-02-03 20:16:16 +00:00
(match-let ([(continue v s as f) (eval-exp '(send! 5) mt-Γ mt-σ)])
2017-01-27 21:29:54 +00:00
(check-equal? as (list (message 5)))
(check-true (void? v)))
;; set!
2017-02-03 20:16:16 +00:00
(match-let ([(continue v s as f) (eval-exp '(set! x 12) mt-Γ (make-store '(x . "hello")))])
2017-01-27 21:29:54 +00:00
(check-true (void? v))
(check-equal? (hash-ref s 'x) 12))
2017-02-03 20:16:16 +00:00
(match-let ([(continue v s as f) (eval-exp '(begin (set! x (+ 1 (read x)))
(set! x (+ 1 (read x)))
(set! x (+ 1 (read x)))
(set! x (+ 1 (read x)))
(read x))
mt-Γ (make-store '(x . 0)))])
2017-01-27 21:29:54 +00:00
(check-equal? v 4))
;; store read
2017-02-03 20:16:16 +00:00
(match-let ([(continue v s as f) (eval-exp '(read x) mt-Γ (make-store '(y . 5) '(x . "hello")))])
2017-01-30 22:24:49 +00:00
(check-equal? v "hello"))
2017-02-03 20:16:16 +00:00
(match-let ([(continue v s as f) (eval-exp '(+ (- 5 1) (/ 4 (if (not #t) 1 2))) mt-Γ mt-σ)])
2017-01-30 22:24:49 +00:00
(check-equal? v 6))
;; lambda
2017-02-03 20:16:16 +00:00
(match-let ([(continue v s as f) (eval-exp '(let (f (lambda (x) (+ x 1))) (f 3)) mt-Γ mt-σ)])
2017-01-30 22:24:49 +00:00
(check-equal? v 4))
2017-02-03 20:16:16 +00:00
(match-let ([(continue v s as f) (eval-exp '(let (z 12)
(let (f (lambda (x) (+ x z)))
(let (z 4)
(f 3)))) mt-Γ mt-σ)])
2017-01-30 22:24:49 +00:00
(check-equal? v 15))
2017-02-03 20:16:16 +00:00
(match-let ([(continue v s as f) (eval-exp '(let (f (lambda () (actor (react (assert 5)))))
(f)) mt-Γ mt-σ)])
2017-01-30 22:24:49 +00:00
(check-false (empty? as))))
2017-01-27 21:29:54 +00:00
;; dollar-id? : any -> bool
;; test if the input is a symbol whose first character is $
(define (dollar-id? s)
(and (symbol? s)
(char=? (string-ref (symbol->string s) 0) #\$)))
;; undollar s : dollar-id? -> var
(define (undollar s)
(string->symbol (substring (symbol->string s) 1)))
;; eval-pat : pat Γ σ -> meta-pattern
2017-01-30 19:40:27 +00:00
;; technically this results in a Projection because it includes captures
2017-01-27 21:29:54 +00:00
;; if you put effects in your pattern then you deserve bad things
(define (eval-pat pat Γ σ)
(match pat
[`(list ,pats ...)
(cons 'list
(for/list ([p (in-list pats)])
(eval-pat p Γ σ)))]
[`(observe ,pat)
(observe (eval-pat pat Γ σ))]
2017-01-30 19:40:27 +00:00
[`(inbound ,pat)
(inbound (eval-pat pat Γ σ))]
[`(outbound ,pat)
(outbound (eval-pat pat Γ σ))]
2017-01-27 21:29:54 +00:00
[(? dollar-id? s)
(?!)]
['_ ?]
[exp
2017-02-03 20:16:16 +00:00
(match-define (continue v _ _ _) (eval-exp exp Γ σ))
2017-01-27 21:29:54 +00:00
v]))
;; pat-bindings : pat -> (Listof var)
(define (pat-bindings pat)
(match pat
[`(list ,pats ...)
(flatten (for/list ([p (in-list pats)])
(pat-bindings p)))]
[`(observe ,pat)
(pat-bindings pat)]
2017-01-30 19:40:27 +00:00
[`(inbound ,pat)
(pat-bindings pat)]
[`(outbound ,pat)
(pat-bindings pat)]
2017-01-27 21:29:54 +00:00
[(? dollar-id? s)
(list (undollar s))]
[_ (list)]))
(module+ test
(check-equal? (pat-bindings '(list hello $x world 5))
(list 'x)))
;; pat-matches : pat Γ σ π -> (Listof Γ)
;; evaluate the pattern and then project matching assertions
;; out of the given trie.
(define (pat-matches pat Γ σ π)
(define concrete-pat (eval-pat pat Γ σ))
(define bindings (pat-bindings pat))
(define s? (trie-project/set #:take (projection-arity concrete-pat) π concrete-pat))
(unless s? (error 'pat-matches "pattern resulted in an infinite set: ~v" pat))
(for/list ([captures (in-set s?)])
(map binding bindings captures)))
;; E-pat : E -> pat
(define E-pat second)
;; occurrences : E Event π Γ σ -> (Listof Γ)
(define (occurrences E e π-old Γ σ)
(define pat (E-pat E))
(match* (E e)
[(`(message ,_) (message v))
(pat-matches pat Γ σ (assertion v))]
[(`(asserted ,_) (scn π-new))
(define candidates (trie-subtract π-new π-old))
(pat-matches pat Γ σ candidates)]
[(`(retracted ,_) (scn π-new))
(define candidates (trie-subtract π-old π-new))
(pat-matches pat Γ σ candidates)]
[(_ _) (list)]))
(module+ test
(check-equal? (occurrences `(asserted 5) (scn (assertion 5)) trie-empty mt-Γ mt-σ)
(list (list)))
(check-equal? (occurrences `(retracted 5) (scn (assertion 5)) trie-empty mt-Γ mt-σ)
(list))
(check-equal? (occurrences `(retracted 5) (scn trie-empty) (assertion 5) mt-Γ mt-σ)
(list (list)))
(check-equal? (occurrences `(asserted 5) (message 5) trie-empty mt-Γ mt-σ)
(list))
2017-01-30 22:24:49 +00:00
(check-equal? (occurrences `(asserted (list "price" $x))
(scn (assertion '(list "price" 12)))
(assertion '(list "price" 5))
2017-01-27 21:29:54 +00:00
mt-Γ mt-σ)
(list (list (binding 'x 12))))
(check-equal? (list->set
2017-01-30 22:24:49 +00:00
(occurrences `(asserted (list "price" $x))
(scn (π-union (assertion '(list "price" 12)) (assertion '(list "price" 16))))
(assertion '(list "price" 5))
2017-01-27 21:29:54 +00:00
mt-Γ mt-σ))
(set (list (binding 'x 12)) (list (binding 'x 16)))))
;; subscription : E Γ σ -> π
(define (subscription E Γ σ)
;; projection->pattern to convert captures to wildcards
(assertion (projection->pattern (observe (eval-pat (E-pat E) Γ σ)))))
2017-02-03 20:16:16 +00:00
;; eval-exp* : (Listof exp) Γ σ -> (Result (Listof Values))
;; evaluate a sequence of expressions
2017-01-27 21:29:54 +00:00
(define (eval-exp* exps Γ σ)
2017-02-03 20:16:16 +00:00
(for-steps (list) σ (in-list exps)
(lambda (vs σ e)
(result-map (lambda (v) (append vs (list v)))
(eval-exp e Γ σ)))))
2017-01-27 21:29:54 +00:00
2017-02-03 20:16:16 +00:00
;; run-facet : facet π σ Γ Event -> Result
2017-01-27 21:29:54 +00:00
(define (run-facet f π-old σ Γ e)
(match-define `(react ,O ...) f)
2017-02-03 20:16:16 +00:00
(for-steps #f σ (in-list O)
(lambda (_ σ o)
(run-endpoint o π-old σ Γ e))))
2017-01-27 21:29:54 +00:00
2017-02-03 20:16:16 +00:00
;; run-endpoint : O π σ Γ Event -> Result
;; determine the effects of an endpoint in response to an event
2017-01-27 21:29:54 +00:00
(define (run-endpoint O π-old σ Γ e)
(match O
2017-02-03 20:16:16 +00:00
;; event-insensitive endpoints
2017-01-27 21:29:54 +00:00
[`(field ,_ ,_)
2017-02-03 20:16:16 +00:00
(inj-result #f σ)]
2017-01-27 21:29:54 +00:00
[`(on-start ,exp ...)
2017-02-03 20:16:16 +00:00
(inj-result #f σ)]
2017-01-27 21:29:54 +00:00
[`(assert ,exp)
2017-02-03 20:16:16 +00:00
(inj-result #f σ)]
;; event sensitive
2017-01-27 21:29:54 +00:00
[`(stop-when ,E ,exps ...)
(define bindings (occurrences E e π-old Γ σ))
(cond
[(empty? bindings)
2017-02-03 20:16:16 +00:00
(inj-result #f σ)]
2017-01-27 21:29:54 +00:00
[else
2017-02-03 20:16:16 +00:00
(match-define (continue _ sto as _)
(for-steps #f σ (in-list bindings)
(lambda (_ σ captures)
(define extended-env (append captures Γ))
(eval-exp* exps extended-env σ))))
2017-01-27 21:29:54 +00:00
(stop sto as)])]
[`(on ,E ,exps ...)
(define bindings (occurrences E e π-old Γ σ))
(cond
[(empty? bindings)
2017-02-03 20:16:16 +00:00
(inj-result #f σ)]
2017-01-27 21:29:54 +00:00
[else
2017-02-03 20:16:16 +00:00
(for-steps #f σ (in-list bindings)
(lambda (_ sto captures)
2017-01-27 21:29:54 +00:00
(define extended-env (append captures Γ))
2017-02-03 20:16:16 +00:00
(eval-exp* exps extended-env sto)))])]))
2017-01-27 21:29:54 +00:00
;; endpoint-assertions : O Γ σ -> π
;; IGNORE effects from such expressions (yadda yadda evil yadda yadda)
(define (endpoint-assertions O Γ σ)
(match O
[`(field ,_ ,_)
trie-empty]
[`(on-start ,exp ...)
trie-empty]
[`(assert ,exp)
2017-02-03 20:16:16 +00:00
(match-define (continue v _ _ _) (eval-exp exp Γ σ))
(assertion v)]
[`(stop-when ,E ,exps ...)
(subscription E Γ σ)]
[`(on ,E ,exps ...)
(subscription E Γ σ)]))
2017-01-27 21:29:54 +00:00
;; facet-assertions : facet Γ σ -> π
(define (facet-assertions f Γ σ)
(match-define `(react ,O ...) f)
(for/fold ([π trie-empty])
([o (in-list O)])
(π-union π (endpoint-assertions o Γ σ))))
;; an OK is (ok σ FacetTree (ListofAction))
(struct ok (sto ft as) #:transparent)
2017-01-27 21:29:54 +00:00
;; run-facets : FacetTree π σ Event -> (U OK (Listof Action))
(define (run-facets ft π parent-sto e)
(match-define (facet-tree stx env sto children) ft)
(define facet-sto (store-concat parent-sto sto))
;; I'm really not confident about the way the stores are being handled here
(match (run-facet stx π facet-sto env e)
2017-02-03 20:16:16 +00:00
[(continue _ new-sto as new-facets)
(define-values (final-sto final-as new-children)
2017-01-27 21:29:54 +00:00
(for/fold ([sto new-sto]
[as as]
[new-children (list)])
([ft (in-list (append children new-facets))])
(match (run-facets ft π sto e)
[(ok new-sto new-ft more-as)
2017-01-27 21:29:54 +00:00
(values new-sto
(append as more-as)
;; n^2 but let's keep the order the same
(append new-children (list new-ft)))]
[more-as
(values sto
(append as more-as)
new-children)])))
(match-define (store-concat new-parent-sto new-facet-sto) final-sto)
(ok new-parent-sto (facet-tree stx env new-facet-sto new-children) final-as)]
2017-01-27 21:29:54 +00:00
[(stop _ as)
as]))
;; ft-assertions : FacetTree σ -> π
(define (ft-assertions ft σ)
(match-define (facet-tree stx env sto children) ft)
(define extended-sto (store-concat σ sto))
(for/fold ([π (facet-assertions stx env extended-sto)])
([f (in-list children)])
(π-union π (ft-assertions f env extended-sto))))
2017-01-27 21:29:54 +00:00
;; actor-behavior : ActorState Event -> Transition
;; leaf behavior function
(define (actor-behavior e s)
(cond
[e
2017-02-03 20:16:16 +00:00
(with-handlers ([exn:fail? (lambda (e) (eprintf "exception: ~v\n" e) (quit #:exception e (list)))])
2017-01-27 22:35:35 +00:00
(match-define (actor-state π-old ft) s)
(match (run-facets ft π-old mt-σ e)
[(ok _ ft as)
(define assertions (ft-assertions ft mt-σ))
2017-01-27 22:35:35 +00:00
(define next-π (if (scn? e) (scn-trie e) π-old))
(transition (actor-state next-π ft)
(cons (scn assertions) as))]
2017-01-27 22:35:35 +00:00
[as
(quit as)]))]
2017-01-27 21:29:54 +00:00
[else #f]))
;; run : Program -> Syndicate
(define (run p)
(define boot-actions
(for/list ([boot (in-list p)])
2017-01-30 19:40:27 +00:00
(boot-actor boot mt-Γ)))
2017-01-27 21:29:54 +00:00
(run-ground boot-actions))
(define test-program
`((actor (react (on-start (printf "hello,world\n"))))))
(define test-program2
`(
(actor (react (on (asserted 5)
(printf "wat\n"))))
(actor (react (assert 5)))))
(define ping-pong
`(
2017-01-30 22:24:49 +00:00
(actor (react (on (message "ping")
2017-01-27 21:29:54 +00:00
(printf "ping\n")
2017-01-30 22:24:49 +00:00
(send! "pong"))))
(actor (react (on (message "pong")
2017-01-27 21:29:54 +00:00
(printf "pong\n")
2017-01-30 22:24:49 +00:00
(send! "ping"))
(on-start (send! "ping"))))))
2017-01-27 21:29:54 +00:00
(define bank-account
`(
(actor (react (field balance 0)
2017-01-30 22:24:49 +00:00
(assert (list "account" (read balance)))
(on (message (list "deposit" $amount))
2017-01-27 21:29:54 +00:00
(set! balance (+ (read balance) amount)))))
2017-01-30 22:24:49 +00:00
(actor (react (on (asserted (list "account" $balance))
2017-02-03 20:16:16 +00:00
(printf "Balance changed to ~a\n" balance))
(stop-when (asserted (list "account" 70))
(printf "bye\n"))))
2017-01-27 21:29:54 +00:00
2017-01-30 22:24:49 +00:00
(actor (react (stop-when (asserted (observe (list "deposit" _)))
(send! (list "deposit" +100))
(send! (list "deposit" -30)))))))
2017-01-27 21:29:54 +00:00
2017-01-30 19:40:27 +00:00
(define multi-level-ex
'(
(actor (react (on (asserted "hello")
(printf "goodbye"))))
(dataspace (actor (react (assert (outbound "hello")))))))
2017-01-30 22:24:49 +00:00
(define ff
'(
(actor (react (on (message 5)
(printf "5\n"))
(on (asserted (observe 12))
(printf "12\n"))
(on (asserted (observe 16))
(printf "16\n"))))
(actor (react (on (asserted 12))
(on-start (send! 5))
(on (asserted 16))
(on-start (send! 5))))))
2017-02-03 20:16:16 +00:00
(run bank-account)