2019-03-15 21:48:52 +00:00
|
|
|
#lang racket
|
|
|
|
|
2020-05-29 19:18:18 +00:00
|
|
|
(provide (all-defined-out))
|
|
|
|
|
2019-03-15 21:48:52 +00:00
|
|
|
(require (only-in racket/hash hash-union))
|
2019-06-07 21:14:40 +00:00
|
|
|
(require racket/generator)
|
2019-03-15 21:48:52 +00:00
|
|
|
|
|
|
|
(module+ test
|
2020-06-08 20:18:57 +00:00
|
|
|
(require rackunit)
|
|
|
|
(require "test-utils.rkt"))
|
2019-03-15 21:48:52 +00:00
|
|
|
|
2019-03-22 19:14:49 +00:00
|
|
|
;; -------------------------------------------------------------------------
|
|
|
|
;; Role Type Data Definitions
|
|
|
|
|
|
|
|
;; a FacetName is a symbol
|
|
|
|
|
2019-03-15 21:48:52 +00:00
|
|
|
;; a T is one of
|
|
|
|
;; - (Role FacetName (Listof EP)), also abbreviated as just Role
|
2019-07-30 20:03:19 +00:00
|
|
|
;; - (Spawn Role)
|
2019-06-17 21:15:08 +00:00
|
|
|
;; - (Sends τ)
|
2019-06-18 14:26:04 +00:00
|
|
|
;; - (Realizes τ)
|
2019-03-22 19:14:49 +00:00
|
|
|
;; - (Stop FacetName Body)
|
2020-10-28 18:06:19 +00:00
|
|
|
(struct Role (nm eps) #:prefab)
|
|
|
|
(struct Spawn (ty) #:prefab)
|
|
|
|
(struct Sends (ty) #:prefab)
|
|
|
|
(struct Realizes (ty) #:prefab)
|
|
|
|
(struct Stop (nm body) #:prefab)
|
2019-03-15 21:48:52 +00:00
|
|
|
|
|
|
|
;; a EP is one of
|
2019-03-22 19:14:49 +00:00
|
|
|
;; - (Reacts D Body), describing an event handler
|
2019-03-15 21:48:52 +00:00
|
|
|
;; - (Shares τ), describing an assertion
|
2019-06-18 14:26:04 +00:00
|
|
|
;; - (Know τ), describing an internal assertion
|
2020-10-28 18:06:19 +00:00
|
|
|
(struct Reacts (evt body) #:prefab)
|
|
|
|
(struct Shares (ty) #:prefab)
|
|
|
|
(struct Know (ty) #:prefab)
|
2019-03-15 21:48:52 +00:00
|
|
|
|
2019-03-22 19:14:49 +00:00
|
|
|
;; a Body describes actions carried out in response to some event, and
|
|
|
|
;; is one of
|
|
|
|
;; - T
|
|
|
|
;; - (Listof Body)
|
|
|
|
;; - (Branch (Listof Body))
|
2020-10-28 18:06:19 +00:00
|
|
|
(struct Branch (arms) #:prefab)
|
2019-03-22 19:14:49 +00:00
|
|
|
|
2019-03-15 21:48:52 +00:00
|
|
|
;; a D is one of
|
2019-06-13 12:34:34 +00:00
|
|
|
;; - (Asserted τ), reaction to assertion
|
|
|
|
;; - (Retracted τ), reaction to retraction
|
2019-06-17 15:26:00 +00:00
|
|
|
;; - (Message τ), reaction to message
|
|
|
|
;; - (Know τ), reaction to internal assertion
|
|
|
|
;; - (Forget τ), reaction to internal retraction
|
|
|
|
;; - (Realize τ), reaction to internal message
|
2019-05-30 17:20:51 +00:00
|
|
|
;; - StartEvt, reaction to facet startup
|
|
|
|
;; - StopEvt, reaction to facet shutdown
|
2019-06-05 20:20:09 +00:00
|
|
|
;; - DataflowEvt, reaction to field updates
|
2020-10-28 18:06:19 +00:00
|
|
|
(struct Asserted (ty) #:prefab)
|
|
|
|
(struct Retracted (ty) #:prefab)
|
|
|
|
(struct Message (ty) #:prefab)
|
|
|
|
(struct Forget (ty) #:prefab)
|
|
|
|
(struct Realize (ty) #:prefab)
|
2019-05-30 17:20:51 +00:00
|
|
|
(define StartEvt 'Start)
|
|
|
|
(define StopEvt 'Stop)
|
2019-06-05 20:20:09 +00:00
|
|
|
(define DataflowEvt 'Dataflow)
|
2019-07-01 19:57:50 +00:00
|
|
|
;; Any -> Bool
|
|
|
|
;; recognize DataflowEvt
|
|
|
|
(define (DataflowEvt? x)
|
|
|
|
(equal? x DataflowEvt))
|
2019-03-15 21:48:52 +00:00
|
|
|
|
2019-06-26 14:09:00 +00:00
|
|
|
;; a D+ is a D with StartEvt and StopEvt replaced with variants that name the
|
|
|
|
;; specified facet,
|
|
|
|
;; - (StartOf FacetName)
|
|
|
|
;; - (StopOf FacetName)
|
2020-10-28 18:06:19 +00:00
|
|
|
(struct StartOf (fn) #:prefab)
|
|
|
|
(struct StopOf (fn) #:prefab)
|
2019-06-26 14:09:00 +00:00
|
|
|
|
|
|
|
;; NOTE: because I'm adding D+ after writing a bunch of code using only D,
|
|
|
|
;; expect inconsistencies in signatures and names
|
|
|
|
|
2019-03-15 21:48:52 +00:00
|
|
|
;; a τ is one of
|
2019-03-19 20:44:10 +00:00
|
|
|
;; - (U (Listof τ))
|
2019-03-15 21:48:52 +00:00
|
|
|
;; - (Struct StructName (Listof τ ...))
|
|
|
|
;; - (Observe τ)
|
2019-06-03 15:16:16 +00:00
|
|
|
;; - (List τ)
|
|
|
|
;; - (Set τ)
|
|
|
|
;; - (Hash τ τ)
|
2019-03-15 21:48:52 +00:00
|
|
|
;; - ⋆
|
2019-05-29 17:40:55 +00:00
|
|
|
;; - (Base Symbol)
|
2019-07-30 20:03:19 +00:00
|
|
|
;; - (internal-label Symbol τ)
|
2020-10-28 18:06:19 +00:00
|
|
|
(struct U (tys) #:prefab)
|
|
|
|
(struct Struct (nm tys) #:prefab)
|
|
|
|
(struct Observe (ty) #:prefab)
|
|
|
|
(struct List (ty) #:prefab)
|
|
|
|
(struct Set (ty) #:prefab)
|
|
|
|
(struct Hash (ty-k ty-v) #:prefab)
|
|
|
|
(struct Mk⋆ () #:prefab)
|
|
|
|
(struct internal-label (actor-id ty) #:prefab)
|
2019-03-15 21:48:52 +00:00
|
|
|
;; TODO this might be a problem when used as a match pattern
|
|
|
|
(define ⋆ (Mk⋆))
|
2020-10-28 18:06:19 +00:00
|
|
|
(struct Base (name) #:prefab)
|
2019-05-29 17:40:55 +00:00
|
|
|
(define Int (Base 'Int))
|
|
|
|
(define String (Base 'String))
|
|
|
|
(define Bool (Base 'Bool))
|
|
|
|
(define Symbol (Base 'Symbol))
|
2019-03-15 21:48:52 +00:00
|
|
|
|
|
|
|
;; a StructName is a Symbol
|
|
|
|
|
2019-03-29 20:12:46 +00:00
|
|
|
;; --------------------------------------------------------------------------
|
|
|
|
;; Derived Types
|
|
|
|
|
|
|
|
;; τ (Listof EP) -> EP
|
|
|
|
(define (During assertion eps)
|
|
|
|
(define facet-name (gensym 'during-inner))
|
2019-06-13 12:34:34 +00:00
|
|
|
(Reacts (Asserted assertion)
|
2019-03-29 20:12:46 +00:00
|
|
|
(Role facet-name
|
2019-06-13 12:34:34 +00:00
|
|
|
(cons (Reacts (Retracted assertion)
|
2019-03-29 20:12:46 +00:00
|
|
|
(Stop facet-name '()))
|
|
|
|
eps))))
|
|
|
|
|
2019-03-19 20:44:10 +00:00
|
|
|
;; -----------------------------------------------------------------------------
|
|
|
|
;; Compiling Roles to state machines
|
2019-03-15 21:48:52 +00:00
|
|
|
|
2020-05-29 15:15:07 +00:00
|
|
|
;; a RoleGraph is a
|
|
|
|
;; (role-graph StateName (Hashof StateName State))
|
|
|
|
;; describing the initial state and the behavior in each state.
|
|
|
|
(struct role-graph (st0 states) #:transparent)
|
|
|
|
|
2020-05-29 19:18:18 +00:00
|
|
|
;; a State is a (state StateName (Hashof D+ (Setof Transition)) (Setof τ))
|
|
|
|
(struct state (name transitions assertions) #:transparent)
|
2020-05-29 15:15:07 +00:00
|
|
|
|
2019-03-15 21:48:52 +00:00
|
|
|
;; a StateName is a (Setof FacetName)
|
|
|
|
;; let's assume that all FacetNames are unique
|
2020-05-29 15:15:07 +00:00
|
|
|
|
2019-06-17 21:15:08 +00:00
|
|
|
;; a Transition is a (transition (Listof TransitionEffect) StateName)
|
|
|
|
(struct transition (effs dest) #:transparent)
|
|
|
|
;; a TransitionEffect is one of
|
|
|
|
;; - (send τ)
|
2019-06-18 14:26:04 +00:00
|
|
|
;; - (realize τ)
|
2019-06-17 21:15:08 +00:00
|
|
|
(struct send (ty) #:transparent)
|
2019-06-18 14:26:04 +00:00
|
|
|
(struct realize (ty) #:transparent)
|
2019-03-15 21:48:52 +00:00
|
|
|
|
2020-05-29 19:18:18 +00:00
|
|
|
;; a TransitionDesc is a (Hashof D+ (Setof (Listof RoleEffect)), describing the
|
|
|
|
;; possible ways an event (+/- of an assertion) can alter the facet tree.
|
|
|
|
;; It always includes the keys (StartOf FacetName) and (StopOf FacetName).
|
|
|
|
(define (txn-desc0 fn) (hash (StartOf fn) (set) (StopOf fn) (set)))
|
|
|
|
|
|
|
|
;; a RoleEffect is one of
|
|
|
|
;; - (start RoleName)
|
|
|
|
;; - (stop RoleName)
|
|
|
|
;; - (send τ)
|
|
|
|
;; - (realize τ)
|
|
|
|
(struct start (nm) #:transparent)
|
|
|
|
(struct stop (nm) #:transparent)
|
|
|
|
|
2019-03-19 20:44:10 +00:00
|
|
|
;; a FacetTree is a
|
2019-07-29 22:22:48 +00:00
|
|
|
;; (facet-tree (Hashof FacetName (Listof FacetName))
|
2019-03-19 20:44:10 +00:00
|
|
|
;; (Hashof FacetName (U #f FacetName)))
|
|
|
|
;; describing the potential immediate children of each facet
|
2019-07-29 22:22:48 +00:00
|
|
|
;; and each facet's parent.
|
|
|
|
;; For roles that spawn multiple actors, a FacetTree is in fact a forest. The
|
|
|
|
;; parent of a root facet is #f
|
2019-03-19 20:44:10 +00:00
|
|
|
(struct facet-tree (down up) #:transparent)
|
2019-03-15 21:48:52 +00:00
|
|
|
|
2020-05-29 19:18:18 +00:00
|
|
|
|
2019-12-31 18:55:59 +00:00
|
|
|
;; RoleGraph -> Nat
|
|
|
|
(define (role-graph-size rg)
|
|
|
|
(for/sum ([st (in-hash-values (role-graph-states rg))])
|
|
|
|
(define edge-count (for/sum ([txns (in-hash-values (state-transitions st))])
|
|
|
|
(set-count txns)))
|
|
|
|
(add1 edge-count)))
|
|
|
|
|
2019-03-22 20:08:36 +00:00
|
|
|
;; Role -> RoleGraph
|
2019-03-15 21:48:52 +00:00
|
|
|
;; in each state, the transitions will include the reactions of the parent
|
|
|
|
;; facet(s)
|
|
|
|
(define (compile role)
|
2020-05-29 19:18:18 +00:00
|
|
|
(define labeled-role (label-internal-events role))
|
2020-05-29 15:15:07 +00:00
|
|
|
;; roles# : (Hashof FacetName TransitionDesc)
|
2020-05-29 19:18:18 +00:00
|
|
|
(define roles# (describe-roles labeled-role))
|
2020-05-29 15:15:07 +00:00
|
|
|
;; ft : FacetTree
|
2019-03-19 20:44:10 +00:00
|
|
|
(define ft (make-facet-tree role))
|
2020-05-29 19:18:18 +00:00
|
|
|
;; assertion# : (Hashof FacetName (Setof τ))
|
|
|
|
(define assertion# (all-roles-assertions (enumerate-roles labeled-role)))
|
2019-03-15 21:48:52 +00:00
|
|
|
(let loop ([work-list (list (set (Role-nm role)))]
|
|
|
|
[states (hash)])
|
|
|
|
(match work-list
|
|
|
|
[(cons current more)
|
|
|
|
(define agg-txn
|
|
|
|
(for/fold ([agg (hash)])
|
2019-06-17 17:18:43 +00:00
|
|
|
([nm (in-set current)])
|
|
|
|
(define txns (hash-ref roles# nm))
|
|
|
|
(hash-union agg txns #:combine combine-effect-sets)))
|
2019-06-21 20:48:49 +00:00
|
|
|
(define (build-transitions D effs)
|
|
|
|
(for*/set ([eff* (in-set effs)]
|
|
|
|
[txn (in-set (apply-effects eff* current ft roles#))]
|
2019-12-30 21:27:29 +00:00
|
|
|
;; TODO - why?
|
2019-06-21 20:48:49 +00:00
|
|
|
;; filter effect-free self-loops
|
|
|
|
#:unless (and (empty? (transition-effs txn))
|
|
|
|
(equal? (transition-dest txn) current)))
|
|
|
|
txn))
|
2019-03-15 21:48:52 +00:00
|
|
|
(define transitions
|
2019-06-26 14:09:00 +00:00
|
|
|
(for*/hash ([(D effs) (in-hash agg-txn)]
|
|
|
|
;; TODO - why was this here?
|
|
|
|
;; I feel like apply-affects was trying to handle start/stop things
|
|
|
|
;; #:unless (start/stop-evt? D)
|
|
|
|
[txns (in-value (build-transitions D effs))]
|
|
|
|
#:unless (set-empty? txns))
|
2019-06-17 21:15:08 +00:00
|
|
|
(values D txns)))
|
2020-05-29 19:18:18 +00:00
|
|
|
(define assertions (assertions-in-state current assertion#))
|
2019-03-15 21:48:52 +00:00
|
|
|
(define new-work
|
2019-06-17 21:15:08 +00:00
|
|
|
(for*/list ([txns (in-hash-values transitions)]
|
|
|
|
[txn (in-set txns)]
|
|
|
|
[st (in-value (transition-dest txn))]
|
2019-03-28 18:55:48 +00:00
|
|
|
#:unless (equal? st current)
|
|
|
|
#:unless (hash-has-key? states st))
|
2019-03-15 21:48:52 +00:00
|
|
|
st))
|
|
|
|
(loop (append more new-work)
|
2020-05-29 19:18:18 +00:00
|
|
|
(hash-set states current (state current transitions assertions)))]
|
2019-03-15 21:48:52 +00:00
|
|
|
['()
|
2019-03-22 20:08:36 +00:00
|
|
|
(role-graph (set (Role-nm role)) states)])))
|
2019-03-15 21:48:52 +00:00
|
|
|
|
2020-05-29 19:18:18 +00:00
|
|
|
;; StateName (Hashof FacetName (Setof τ)) -> (Setof τ)
|
|
|
|
(define (assertions-in-state sn assertion#)
|
|
|
|
(for/fold ([assertions (set)])
|
|
|
|
([facet-name (in-set sn)])
|
|
|
|
(set-union assertions (hash-ref assertion# facet-name (set)))))
|
|
|
|
|
2019-06-26 14:09:00 +00:00
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"facets created in OnStart handled properly"
|
|
|
|
(define strt
|
|
|
|
'(Role (x)
|
|
|
|
(Reacts OnStart
|
|
|
|
(Role (y)
|
|
|
|
(Shares (Hi))
|
|
|
|
(Reacts (Asserted (Bye))
|
|
|
|
(Stop y))))))
|
|
|
|
(define r (parse-T strt))
|
|
|
|
(define rg (run/timeout (thunk (compile r))))
|
|
|
|
(check-true (role-graph? rg))
|
|
|
|
(match-define (role-graph st0 st#) rg)
|
|
|
|
(check-true (hash-has-key? st# (set 'x 'y)))))
|
|
|
|
|
2021-01-28 16:26:11 +00:00
|
|
|
;; a DetectedCylce is a (detected-cycle StateName (Listof TraversalStep)), as in
|
|
|
|
;; (detected-cycle start steps)
|
|
|
|
;; where path represents the sequences of states containing a cycle,
|
|
|
|
(struct detected-cycle (start steps) #:transparent)
|
|
|
|
|
|
|
|
;; a TraversalStep is a (traversal-step D StateName)
|
|
|
|
;; representing a state transition along an edge matching D to a destination state
|
|
|
|
(struct traversal-step (evt dest) #:transparent)
|
2019-06-26 14:09:00 +00:00
|
|
|
|
|
|
|
;; RoleGraph Role -> (U RoleGraph DetectedCycle)
|
2019-06-19 21:17:05 +00:00
|
|
|
;; "Optimize" the given role graph with respect to internal events.
|
|
|
|
;; The resulting graph will have transitions of only external events.
|
2021-01-06 17:08:13 +00:00
|
|
|
(define (compile/internal-events rg)
|
2019-06-19 21:17:05 +00:00
|
|
|
(match-define (role-graph st0 orig-st#) rg)
|
|
|
|
;; doing funny business with state (set) here
|
2020-05-29 19:18:18 +00:00
|
|
|
(define orig-st#+ (hash-set orig-st# (set) (state (set) (hash) (set))))
|
2019-06-26 14:09:00 +00:00
|
|
|
|
2019-06-19 21:17:05 +00:00
|
|
|
;; a WorkItem is a
|
2021-01-28 16:26:11 +00:00
|
|
|
;; (work-item TraversalStep (Listof TraversalStep) D+ (Listof D+) (Listof TransitionEffect))
|
2019-06-21 17:07:27 +00:00
|
|
|
;; such as (work-item from path/r to by with effs), where
|
2019-06-19 21:17:05 +00:00
|
|
|
;; - from is the origin state for this chain of events
|
2019-06-21 20:48:49 +00:00
|
|
|
;; - path/r is the list of states in the path to this point, *after* from, in reverse
|
|
|
|
;; (meaning that all of these transitions are due to *internal* events)
|
2019-06-19 21:17:05 +00:00
|
|
|
;; - to is the current state that has been reached
|
|
|
|
;; - by is the external event that kicked off this sequence
|
|
|
|
;; - with is a list of pending events to be processed
|
2019-07-31 15:40:12 +00:00
|
|
|
;; - effs are the external effects emitted on this path
|
2019-06-21 17:07:27 +00:00
|
|
|
(struct work-item (from path/r to by with effs) #:transparent)
|
2021-01-28 16:26:11 +00:00
|
|
|
|
2019-06-21 17:07:27 +00:00
|
|
|
(let/ec fail
|
|
|
|
(define (walk work visited st#)
|
|
|
|
(match work
|
|
|
|
['()
|
2019-06-26 14:09:00 +00:00
|
|
|
(define mt-txns (hash-ref (hash-ref st# (set)) StartEvt))
|
|
|
|
(define new-st0
|
|
|
|
(cond
|
|
|
|
[(and (= (set-count mt-txns) 1)
|
|
|
|
(empty? (transition-effs (set-first mt-txns))))
|
|
|
|
(transition-dest (set-first mt-txns))]
|
|
|
|
[else
|
|
|
|
(set)]))
|
2019-06-21 17:07:27 +00:00
|
|
|
(define states
|
|
|
|
(for/hash ([(sn txns) (in-hash st#)]
|
2020-06-12 20:22:01 +00:00
|
|
|
;; handle empty state below
|
|
|
|
#:unless (set-empty? sn))
|
2020-05-29 19:18:18 +00:00
|
|
|
(define old-assertions (state-assertions (hash-ref orig-st#+ sn)))
|
|
|
|
(define new-assertions (external-assertions old-assertions))
|
|
|
|
(values sn (state sn txns new-assertions))))
|
2020-06-12 20:22:01 +00:00
|
|
|
(when (set-empty? new-st0)
|
|
|
|
(error 'compile-internal-events "not able to remove initial start event"))
|
|
|
|
(when (target-of-transition? (set) st#)
|
|
|
|
;; get rid of the empty state unless some other state transitions to it
|
|
|
|
(set! states (hash-set states (set) (state (set) (hash) (set)))))
|
2019-06-26 14:09:00 +00:00
|
|
|
(role-graph new-st0 states)]
|
2019-06-21 17:07:27 +00:00
|
|
|
[(cons (work-item from path/r to by with effs) more-work)
|
2021-01-28 16:26:11 +00:00
|
|
|
(match-define (traversal-step last-evt cur-st) to)
|
|
|
|
(define prev (if (empty? path/r) from (traversal-step-dest (first path/r))))
|
2020-05-29 19:18:18 +00:00
|
|
|
(define prev-assertions (state-assertions (hash-ref orig-st#+ prev)))
|
2021-01-28 16:26:11 +00:00
|
|
|
(match-define (state _ txn# cur-assertions) (hash-ref orig-st#+ cur-st))
|
2020-05-29 19:18:18 +00:00
|
|
|
(define new-state-changes (route-internal prev-assertions
|
|
|
|
cur-assertions))
|
2019-07-01 21:17:13 +00:00
|
|
|
(define state-changes* (for/list ([D (in-set new-state-changes)]
|
2020-05-29 15:15:07 +00:00
|
|
|
#:when (for/or ([D/actual (in-hash-keys txn#)])
|
|
|
|
(D<:? D D/actual)))
|
2019-07-01 21:17:13 +00:00
|
|
|
D))
|
2021-01-28 16:26:11 +00:00
|
|
|
(define started (for*/list ([fn (in-set (set-subtract cur-st prev))]
|
2019-07-01 21:17:13 +00:00
|
|
|
[D (in-value (StartOf fn))]
|
|
|
|
#:when (hash-has-key? txn# D))
|
|
|
|
D))
|
2021-01-28 16:26:11 +00:00
|
|
|
(define stopped (for*/list ([fn (in-set (set-subtract prev cur-st))]
|
2019-07-01 21:17:13 +00:00
|
|
|
[D (in-value (StopOf fn))]
|
|
|
|
#:when (hash-has-key? txn# D))
|
|
|
|
D))
|
|
|
|
(define new-events (append started stopped state-changes*))
|
2019-07-01 19:57:50 +00:00
|
|
|
|
|
|
|
;; (Listof D+) -> (Listof WorkItem)
|
|
|
|
;; Try to dispatch the first relevant pending event, which yields a
|
|
|
|
;; collection of work items based on its effects
|
|
|
|
(define (pending-evts->work-items pending-evts)
|
|
|
|
(define pending/first-relevant
|
|
|
|
(dropf pending-evts
|
|
|
|
(lambda (evt)
|
|
|
|
(not
|
|
|
|
(for/or ([D (in-hash-keys txn#)])
|
|
|
|
;; TODO - think I want non-empty intersection instead of subtyping
|
|
|
|
(and (D<:? evt D)
|
|
|
|
;; don't want dataflow edges to gobble up all events
|
|
|
|
(implies (DataflowEvt? D) (DataflowEvt? evt))))))))
|
|
|
|
(match pending/first-relevant
|
|
|
|
['()
|
|
|
|
'()]
|
|
|
|
[(cons evt more-pending)
|
|
|
|
(define path/r+ (cons to path/r))
|
2019-06-26 14:09:00 +00:00
|
|
|
(for*/list ([(D ts) (in-hash txn#)]
|
|
|
|
#:when (D<:? evt D)
|
2019-07-01 19:57:50 +00:00
|
|
|
#:when (implies (DataflowEvt? D) (DataflowEvt? evt))
|
2019-06-26 14:09:00 +00:00
|
|
|
[t (in-set ts)])
|
|
|
|
(match-define (transition more-effs dest) t)
|
2021-01-28 16:26:11 +00:00
|
|
|
(check-for-cycle! from path/r+ evt dest fail)
|
2019-07-31 15:40:12 +00:00
|
|
|
(define-values (internal-effs external-effs)
|
|
|
|
(partition-transition-effects more-effs))
|
2019-06-26 14:09:00 +00:00
|
|
|
(work-item from
|
|
|
|
path/r+
|
2021-01-28 16:26:11 +00:00
|
|
|
(traversal-step evt dest)
|
2019-06-26 14:09:00 +00:00
|
|
|
by
|
|
|
|
(append more-pending internal-effs)
|
2019-07-31 15:40:12 +00:00
|
|
|
(append effs external-effs)))]))
|
2019-07-01 19:57:50 +00:00
|
|
|
|
2019-07-01 21:17:13 +00:00
|
|
|
;; NOTE: knowledge of scheduling used here
|
2019-07-01 19:57:50 +00:00
|
|
|
(define pending*
|
2019-07-01 21:17:13 +00:00
|
|
|
(for*/list ([schedule (in-permutations new-events)]
|
|
|
|
[evts (in-value (append with schedule))]
|
|
|
|
[df? (in-list (if (hash-has-key? txn# DataflowEvt)
|
|
|
|
(list #t #f)
|
|
|
|
(list #f)))])
|
|
|
|
(if df? (cons DataflowEvt evts) evts)))
|
2019-07-01 19:57:50 +00:00
|
|
|
(define induced-work (map pending-evts->work-items pending*))
|
2019-07-01 21:17:13 +00:00
|
|
|
(define induced-work* (remove-duplicates (flatten induced-work)))
|
2019-07-01 19:57:50 +00:00
|
|
|
(cond
|
2019-07-01 21:17:13 +00:00
|
|
|
[(ormap empty? induced-work)
|
|
|
|
;; this is the end of some path
|
2021-01-28 16:26:11 +00:00
|
|
|
(define visited+ (set-add visited cur-st))
|
2019-07-01 19:57:50 +00:00
|
|
|
(define new-paths-work
|
2021-01-28 16:26:11 +00:00
|
|
|
(for*/list (#:unless (set-member? visited cur-st)
|
2019-07-01 19:57:50 +00:00
|
|
|
[(D txns) (in-hash txn#)]
|
|
|
|
#:when (external-evt? D)
|
|
|
|
#:unless (equal? D DataflowEvt)
|
|
|
|
[t (in-set txns)])
|
|
|
|
(match-define (transition es dst) t)
|
2019-07-31 15:40:12 +00:00
|
|
|
(define-values (internal-effs external-effs)
|
|
|
|
(partition-transition-effects es))
|
2021-01-28 16:26:11 +00:00
|
|
|
(work-item cur-st '() (traversal-step D dst) D internal-effs external-effs)))
|
|
|
|
(define new-st# (update-path st# from cur-st by effs))
|
2019-07-01 19:57:50 +00:00
|
|
|
(walk (append more-work induced-work* new-paths-work) visited+ new-st#)]
|
|
|
|
[else
|
2020-05-29 15:15:07 +00:00
|
|
|
(walk (append more-work induced-work*) visited st#)])]))
|
2021-01-28 16:26:11 +00:00
|
|
|
(walk (list (work-item (set) '() (traversal-step StartEvt st0) StartEvt '() '()))
|
2019-06-21 17:07:27 +00:00
|
|
|
(set)
|
|
|
|
(hash))))
|
2019-06-19 21:17:05 +00:00
|
|
|
|
2021-01-28 16:26:11 +00:00
|
|
|
;; (Listof TraceStep) D StateName (DetectedCycle -> X) -> (U X Void)
|
|
|
|
;; the path is in reverse, and the final step is the pair evt/dest;
|
|
|
|
;; so their is a cycle if the sequence from the first occurrence of
|
|
|
|
;; dest in the path matches the sequence from the second occurrence to
|
|
|
|
;; the first.
|
|
|
|
(define (check-for-cycle! from path/r evt dest fail)
|
|
|
|
;; TraceStep -> Bool
|
|
|
|
(define (same-state? step) (equal? dest (traversal-step-dest step)))
|
|
|
|
|
|
|
|
;; (Listof TraceStep) -> (Values (Listof TraceStep) (Listof TraceStep))
|
|
|
|
(define (split-at-same-state steps) (splitf-at steps (compose not same-state?)))
|
|
|
|
|
|
|
|
(define-values (loop1 trail) (split-at-same-state path/r))
|
|
|
|
(when (cons? trail)
|
|
|
|
(match-define (cons prior-last trail2) trail)
|
|
|
|
(define-values (loop2 trail3) (split-at-same-state trail2))
|
|
|
|
(define last-step (traversal-step evt dest))
|
|
|
|
(when (and (cons? trail3)
|
|
|
|
(equal? (cons last-step loop1)
|
|
|
|
(cons prior-last loop2)))
|
|
|
|
(fail (detected-cycle from (reverse (cons last-step path/r)))))))
|
|
|
|
|
2019-06-19 21:17:05 +00:00
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"most minimal functionality for removing internal events"
|
|
|
|
;; manager role has basically nothing to it
|
|
|
|
(define m (compile manager))
|
2021-01-06 17:08:13 +00:00
|
|
|
(define i (compile/internal-events m))
|
2019-06-19 21:17:05 +00:00
|
|
|
(check-true (role-graph? i))
|
2020-05-29 19:18:18 +00:00
|
|
|
(check-true (simulates?/rg i m))
|
|
|
|
(check-true (simulates?/rg m i))
|
2019-06-19 21:17:05 +00:00
|
|
|
;; this isn't necessarily *needed*, but nice to know
|
|
|
|
(check-equal? i m))
|
|
|
|
(test-case
|
|
|
|
"removing internal events on more involved role"
|
2019-12-31 18:55:59 +00:00
|
|
|
;; because it doesn't use any internal events, it should be unaffected
|
|
|
|
(define tmr (parse-T task-runner-ty))
|
2019-06-19 21:17:05 +00:00
|
|
|
(define tm (compile tmr))
|
2021-01-06 17:08:13 +00:00
|
|
|
(define tmi (compile/internal-events tm))
|
2019-06-19 21:17:05 +00:00
|
|
|
(check-true (role-graph? tmi))
|
2019-12-31 18:55:59 +00:00
|
|
|
;; I'm not exactly sure how the two should be related via simulation :S
|
2020-05-29 19:18:18 +00:00
|
|
|
(check-true (simulates?/rg tmi tm)))
|
2019-06-26 14:09:00 +00:00
|
|
|
(test-case
|
|
|
|
"detect a simple internal event cycle"
|
|
|
|
(define cyclic
|
|
|
|
'(Role (x)
|
|
|
|
(Reacts (Realize Int)
|
|
|
|
(Realizes Int))
|
|
|
|
(Reacts OnStart
|
|
|
|
(Realizes Int))))
|
2020-05-29 19:18:18 +00:00
|
|
|
(define r (parse-T cyclic))
|
2019-06-26 14:09:00 +00:00
|
|
|
(define rg (compile r))
|
2021-01-06 17:08:13 +00:00
|
|
|
(define i (run/timeout (thunk (compile/internal-events rg))))
|
2021-01-28 16:26:11 +00:00
|
|
|
(check-true (detected-cycle? i))
|
|
|
|
(check-match i
|
|
|
|
(detected-cycle
|
|
|
|
(== (set))
|
|
|
|
(list (traversal-step 'Start (== (set 'x)))
|
|
|
|
(traversal-step (StartOf 'x) (== (set 'x)))
|
|
|
|
(traversal-step (Realize (internal-label _ (== Int))) (== (set 'x)))
|
|
|
|
(traversal-step (Realize (internal-label _ (== Int))) (== (set 'x)))))))
|
2019-06-26 14:09:00 +00:00
|
|
|
(test-case
|
|
|
|
"interesting internal start event"
|
|
|
|
(test-case
|
|
|
|
"facets created in OnStart handled properly"
|
|
|
|
(define strt
|
|
|
|
'(Role (x)
|
|
|
|
(Reacts OnStart
|
|
|
|
(Role (y)
|
|
|
|
(Shares (Hi))
|
|
|
|
(Reacts (Asserted (Bye))
|
|
|
|
(Stop y))))))
|
|
|
|
(define r (parse-T strt))
|
|
|
|
(define rg (run/timeout (thunk (compile r))))
|
|
|
|
(check-true (role-graph? rg))
|
2021-01-06 17:08:13 +00:00
|
|
|
(define rgi (run/timeout (thunk (compile/internal-events rg))))
|
2019-06-26 14:09:00 +00:00
|
|
|
(check-true (role-graph? rgi))
|
|
|
|
(match-define (role-graph st0 st#) rgi)
|
|
|
|
(check-equal? st0 (set 'x 'y))
|
|
|
|
(check-true (hash-has-key? st# (set 'x 'y)))
|
|
|
|
(define xy-txns (state-transitions (hash-ref st# (set 'x 'y))))
|
|
|
|
(check-equal? xy-txns (hash (Asserted (Struct 'Bye '()))
|
|
|
|
(set (transition '() (set 'x)))))
|
|
|
|
(check-true (hash-has-key? st# (set 'x)))
|
|
|
|
(define x-txns (state-transitions (hash-ref st# (set 'x))))
|
2019-07-31 15:40:12 +00:00
|
|
|
(check-equal? x-txns (hash))))
|
|
|
|
(test-case
|
|
|
|
"remove internal effects from transitions"
|
|
|
|
(define role
|
|
|
|
(Role 'x
|
|
|
|
(list (Reacts (Asserted Int)
|
|
|
|
(list (Realizes String)
|
|
|
|
(Sends Int)
|
|
|
|
(Role 'y (list)))))))
|
|
|
|
(define rg (run/timeout (thunk (compile role))))
|
|
|
|
(check-true (role-graph? rg))
|
2021-01-06 17:08:13 +00:00
|
|
|
(define rgi (run/timeout (thunk (compile/internal-events rg))))
|
2019-07-31 15:40:12 +00:00
|
|
|
(check-true (role-graph? rgi))
|
|
|
|
(define state# (role-graph-states rgi))
|
|
|
|
(check-true (hash-has-key? state# (set 'x)))
|
|
|
|
(define txn# (state-transitions (hash-ref state# (set 'x))))
|
|
|
|
(check-equal? txn#
|
2020-05-29 15:15:07 +00:00
|
|
|
(hash (Asserted Int) (set (transition (list (send Int)) (set 'x 'y))))))
|
|
|
|
|
|
|
|
(test-case
|
|
|
|
"regression: type equality instead of subtyping on internal transitions"
|
|
|
|
(define desc '(Role (x)
|
|
|
|
(Know (Tuple Int))
|
|
|
|
(Reacts (Know (Tuple ★/t))
|
|
|
|
(Role (y)))))
|
2020-05-29 19:18:18 +00:00
|
|
|
(define role (run/timeout (thunk (parse-T desc))))
|
2020-05-29 15:15:07 +00:00
|
|
|
(define rg (run/timeout (thunk (compile role))))
|
|
|
|
(check-true (role-graph? rg))
|
2021-01-06 17:08:13 +00:00
|
|
|
(define rgi (run/timeout (thunk (compile/internal-events rg))))
|
2020-05-29 15:15:07 +00:00
|
|
|
(check-true (role-graph? rgi))
|
2020-05-29 19:18:18 +00:00
|
|
|
(check-match rgi
|
|
|
|
(role-graph (== (set 'x 'y))
|
|
|
|
(hash-table ((== (set 'x 'y)) (state (== (set 'x 'y))
|
|
|
|
(== (hash))
|
|
|
|
(== (set)))))))))
|
2019-06-19 21:17:05 +00:00
|
|
|
|
|
|
|
;; (Setof τ) (Setof τ) -> (Setof D)
|
|
|
|
;; Subtyping-based assertion routing (*not* intersection - TODO)
|
|
|
|
(define (route-internal prev current)
|
2019-06-26 14:09:00 +00:00
|
|
|
;; note that messages are handled separately, don't need to worry about them
|
|
|
|
;; here
|
2020-05-29 15:15:07 +00:00
|
|
|
(define old-interests (internal-interests prev))
|
2019-06-26 14:09:00 +00:00
|
|
|
(define old-matches (matching-interests old-interests prev))
|
2020-05-29 15:15:07 +00:00
|
|
|
(define new-interests (internal-interests current))
|
2019-06-26 14:09:00 +00:00
|
|
|
(define new-matches (matching-interests new-interests current))
|
|
|
|
(define appeared (label-assertions (assertion-delta new-matches old-matches) Know))
|
|
|
|
(define disappeared (label-assertions (assertion-delta old-matches new-matches) Forget))
|
|
|
|
(define appearing-interests (assertion-delta new-interests old-interests))
|
|
|
|
(define newly-relevant (label-assertions (matching-interests appearing-interests current) Know))
|
|
|
|
(set-union appeared disappeared newly-relevant))
|
|
|
|
|
2020-05-29 15:15:07 +00:00
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"test routing"
|
|
|
|
(define interest (internal-label 'x
|
|
|
|
(parse-τ '(Observe
|
|
|
|
(SlotAssignment (ReqID (Bind (Tuple Int Symbol))
|
|
|
|
(Bind Symbol))
|
|
|
|
Discard)))))
|
|
|
|
(define request (internal-label 'x
|
|
|
|
(parse-τ '(SlotAssignment (ReqID (Tuple Int Symbol) Symbol)
|
|
|
|
(Bind Symbol)))))
|
|
|
|
(define expected (set (Know request)))
|
|
|
|
(check-equal? (route-internal (set interest) (set interest request))
|
|
|
|
expected)
|
|
|
|
(check-equal? (route-internal (set request) (set interest request))
|
|
|
|
expected)
|
|
|
|
(check-equal? (route-internal (set) (set interest request))
|
|
|
|
expected))
|
|
|
|
(test-case
|
|
|
|
"test routing internally-labeled assertions"
|
|
|
|
(define interest (internal-label 'x
|
|
|
|
(parse-τ '(Observe
|
|
|
|
(SlotAssignment (ReqID (Bind (Tuple Int Symbol))
|
|
|
|
(Bind Symbol))
|
|
|
|
Discard)))))
|
|
|
|
(define request (internal-label 'x
|
|
|
|
(parse-τ '(SlotAssignment (ReqID (Tuple Int Symbol) Symbol)
|
|
|
|
(Bind Symbol)))))
|
|
|
|
(define expected (set (Know request)))
|
|
|
|
(check-equal? (route-internal (set interest) (set interest request))
|
|
|
|
expected)
|
|
|
|
(check-equal? (route-internal (set request) (set interest request))
|
|
|
|
expected)
|
|
|
|
(check-equal? (route-internal (set) (set interest request))
|
|
|
|
expected)))
|
|
|
|
|
2019-06-26 14:09:00 +00:00
|
|
|
;; (Setof τ) -> (Setof τ)
|
|
|
|
;; the type of interests in a set
|
2020-05-29 15:15:07 +00:00
|
|
|
(define (internal-interests as)
|
2019-06-26 14:09:00 +00:00
|
|
|
(for/set ([a (in-set as)]
|
2020-05-29 15:15:07 +00:00
|
|
|
#:when (and (internal-label? a)
|
|
|
|
(Observe? (internal-label-ty a))))
|
|
|
|
(internal-label (internal-label-actor-id a)
|
|
|
|
(Observe-ty (internal-label-ty a)))))
|
2019-06-26 14:09:00 +00:00
|
|
|
|
|
|
|
;; (Setof τ) (Setof τ) -> (Setof τ)
|
|
|
|
;; The assertions in as that have a matching (supertype) interest in is
|
|
|
|
(define (matching-interests is as)
|
|
|
|
(for/set ([a (in-set as)]
|
|
|
|
#:when (contains-supertype? is a))
|
|
|
|
a))
|
|
|
|
|
|
|
|
;; (Setof τ) τ -> Bool
|
|
|
|
;; does the set contain a type that is a supertype of a?
|
|
|
|
(define (contains-supertype? as a)
|
|
|
|
(for/or ([x (in-set as)])
|
|
|
|
(<:? a x)))
|
|
|
|
|
|
|
|
;; (Setof τ) (Setof τ) -> (Setof τ)
|
|
|
|
;; Computes as1 - as2, up to suptyping, applying xform to each element
|
|
|
|
(define (assertion-delta as1 as2)
|
|
|
|
(for/set ([a1 (in-set as1)]
|
|
|
|
#:unless (contains-supertype? as2 a1))
|
|
|
|
a1))
|
|
|
|
|
|
|
|
;; (Setof τ) (τ -> X) -> (Setof X)
|
|
|
|
;; apply a procedure to each assertion in a set
|
|
|
|
(define (label-assertions as f)
|
|
|
|
(for/set ([a (in-set as)])
|
|
|
|
(f a)))
|
2019-06-19 21:17:05 +00:00
|
|
|
|
2020-05-29 19:18:18 +00:00
|
|
|
;; (Setof τ) -> (Setof τ)
|
|
|
|
;; remove all internal-label assertions in a set
|
|
|
|
(define (external-assertions assertions)
|
|
|
|
(for/set ([a (in-set assertions)]
|
|
|
|
#:unless (internal-label? a))
|
|
|
|
a))
|
|
|
|
|
2019-06-19 21:17:05 +00:00
|
|
|
;; (Hashof StateName (Hashof D (Setof Transition)))
|
|
|
|
;; StateName
|
|
|
|
;; StateName
|
|
|
|
;; D
|
|
|
|
;; (Listof TransitionEffects)
|
|
|
|
;; -> (Hashof StateName (Hashof D (Setof Transition)))
|
|
|
|
;; record an edge between from and to based on the given event and emitting some effects
|
|
|
|
(define (update-path st# from to by effs)
|
2019-06-21 20:48:49 +00:00
|
|
|
(cond
|
|
|
|
[(and (equal? from to)
|
|
|
|
(empty? effs))
|
|
|
|
st#]
|
|
|
|
[else
|
|
|
|
(define txn (transition effs to))
|
2019-06-26 14:09:00 +00:00
|
|
|
;; make sure to is in the hash
|
|
|
|
(define st#+to (hash-update st# to values (hash)))
|
|
|
|
(hash-update st#+to
|
2019-06-21 20:48:49 +00:00
|
|
|
from
|
|
|
|
(lambda (txn#)
|
|
|
|
(hash-update txn#
|
|
|
|
by
|
|
|
|
(lambda (txns)
|
|
|
|
(set-add txns txn))
|
|
|
|
(set)))
|
|
|
|
(hash))]))
|
2019-06-19 21:17:05 +00:00
|
|
|
|
2019-07-31 15:40:12 +00:00
|
|
|
;; (Listof (TransitionEffect)) -> (Values (Listof D) (Listof TransitionEffect))
|
|
|
|
;; partition the internal and external effects, translating realize effects to
|
|
|
|
;; Realize events along the way
|
|
|
|
(define (partition-transition-effects effs)
|
|
|
|
(define-values (internals externals) (partition realize? effs))
|
|
|
|
(define (realize->Realize e) (Realize (realize-ty e)))
|
|
|
|
(values (map realize->Realize internals)
|
|
|
|
externals))
|
2019-06-19 21:17:05 +00:00
|
|
|
|
2019-05-30 17:20:51 +00:00
|
|
|
;; D -> Bool
|
|
|
|
;; test if D corresponds to an external event (assertion, message)
|
|
|
|
(define (external-evt? D)
|
2019-06-05 20:20:09 +00:00
|
|
|
(match D
|
2019-06-13 12:34:34 +00:00
|
|
|
[(or (Asserted _)
|
2019-06-17 15:26:00 +00:00
|
|
|
(Retracted _)
|
|
|
|
(Message _))
|
2019-06-05 20:20:09 +00:00
|
|
|
#t]
|
2019-06-17 15:26:00 +00:00
|
|
|
;; TODO - hacky
|
2019-06-05 20:20:09 +00:00
|
|
|
[(== DataflowEvt)
|
|
|
|
#t]
|
|
|
|
[_
|
|
|
|
#f]))
|
2019-05-30 17:20:51 +00:00
|
|
|
|
2019-06-26 14:09:00 +00:00
|
|
|
;; D+ -> Bool
|
2019-06-21 20:48:49 +00:00
|
|
|
;; test if D corresponds to Start or Stop event
|
|
|
|
(define (start/stop-evt? D)
|
|
|
|
(or (equal? D StartEvt)
|
2019-06-26 14:09:00 +00:00
|
|
|
(equal? D StopEvt)
|
|
|
|
(StartOf? D)
|
|
|
|
(StopOf? D)))
|
2019-06-21 20:48:49 +00:00
|
|
|
|
2019-12-31 18:55:59 +00:00
|
|
|
;; StateName (Hashof StateName (Hashof D+ (Setof Transition))) -> Bool
|
|
|
|
;; do any of the transitions go to `sn`?
|
|
|
|
(define (target-of-transition? sn st#)
|
|
|
|
(for*/or ([txn# (in-hash-values st#)]
|
|
|
|
[txns (in-hash-values txn#)]
|
|
|
|
[txn (in-set txns)])
|
|
|
|
(equal? sn (transition-dest txn))))
|
|
|
|
|
2019-03-15 21:48:52 +00:00
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"compile seller"
|
2019-03-22 20:08:36 +00:00
|
|
|
(define rg (compile seller))
|
|
|
|
(check-true (role-graph? rg))
|
|
|
|
(match-define (role-graph sn0 seller#) rg)
|
|
|
|
(check-equal? sn0 (set 'seller))
|
2019-03-15 21:48:52 +00:00
|
|
|
(check-true (hash-has-key? seller# (set 'seller)))
|
|
|
|
(check-true (hash-has-key? seller# (set 'seller 'fulfill)))
|
2020-11-30 22:49:03 +00:00
|
|
|
(check-equal? (list->set (hash-keys seller#))
|
|
|
|
(set (set 'seller 'fulfill)
|
|
|
|
(set 'seller)))
|
2019-03-15 21:48:52 +00:00
|
|
|
(define st0 (hash-ref seller# (set 'seller)))
|
|
|
|
(define transitions (state-transitions st0))
|
|
|
|
(define quote-request
|
2019-03-29 20:12:46 +00:00
|
|
|
(Observe (book-quote String ⋆)))
|
2019-06-13 12:34:34 +00:00
|
|
|
(check-true (hash-has-key? transitions (Asserted quote-request)))
|
|
|
|
(check-equal? (hash-ref transitions (Asserted quote-request))
|
2019-06-17 21:15:08 +00:00
|
|
|
(set (transition '() (set 'seller 'fulfill)))))
|
2019-03-22 19:34:38 +00:00
|
|
|
(test-case
|
|
|
|
"compile role that quits"
|
|
|
|
(define r
|
|
|
|
(Role 'x
|
2019-06-13 12:34:34 +00:00
|
|
|
(list (Reacts (Asserted Int)
|
2019-03-22 19:34:38 +00:00
|
|
|
(Stop 'x '())))))
|
2019-03-22 20:08:36 +00:00
|
|
|
(define rg (compile r))
|
|
|
|
(check-true (role-graph? rg))
|
|
|
|
(match-define (role-graph sn0 state#) rg)
|
|
|
|
(check-equal? sn0
|
|
|
|
(set 'x))
|
2019-03-22 19:34:38 +00:00
|
|
|
(check-true (hash-has-key? state# (set)))
|
|
|
|
(check-true (hash-has-key? state# (set 'x)))
|
|
|
|
(define state0 (hash-ref state# (set 'x)))
|
|
|
|
(define transitions (state-transitions state0))
|
2019-06-13 12:34:34 +00:00
|
|
|
(check-true (hash-has-key? transitions (Asserted Int)))
|
|
|
|
(check-equal? (hash-ref transitions (Asserted Int))
|
2019-06-17 21:15:08 +00:00
|
|
|
(set (transition '() (set)))))
|
2019-03-28 18:55:48 +00:00
|
|
|
|
|
|
|
(test-case
|
|
|
|
"leader-revised should have a quote/poll cycle"
|
|
|
|
(define rg (compile leader-revised))
|
|
|
|
(check-true (role-graph? rg))
|
|
|
|
(match-define (role-graph sn0 state#) rg)
|
|
|
|
(check-true (hash? state#))
|
|
|
|
(check-true (hash-has-key? state# (set 'get-quotes)))
|
|
|
|
(define gq-st (hash-ref state# (set 'get-quotes)))
|
|
|
|
(check-true (state? gq-st))
|
2020-05-29 19:18:18 +00:00
|
|
|
(match-define (state _ gq-transitions _) gq-st)
|
2019-06-13 12:34:34 +00:00
|
|
|
(define bq (Asserted (book-quote String Int)))
|
2019-03-28 18:55:48 +00:00
|
|
|
(check-true (hash? gq-transitions))
|
|
|
|
(check-true (hash-has-key? gq-transitions bq))
|
2019-06-17 21:15:08 +00:00
|
|
|
(define txns (hash-ref gq-transitions bq))
|
|
|
|
(check-true (set? txns))
|
|
|
|
(define dests (for/set ([t (in-set txns)])
|
|
|
|
(transition-dest t)))
|
|
|
|
(check-true (set? txns))
|
2019-03-28 18:55:48 +00:00
|
|
|
(check-true (set-member? dests (set 'get-quotes 'poll-members)))
|
|
|
|
(check-true (hash-has-key? state# (set 'get-quotes 'poll-members)))
|
|
|
|
(define gqpm-st (hash-ref state# (set 'get-quotes 'poll-members)))
|
|
|
|
(check-true (state? gqpm-st))
|
2020-05-29 19:18:18 +00:00
|
|
|
(match-define (state _ gqpm-transitions _) gqpm-st)
|
2019-06-13 12:34:34 +00:00
|
|
|
(define bi (Asserted (book-interest String String ⋆)))
|
2019-03-28 18:55:48 +00:00
|
|
|
(check-true (hash? gqpm-transitions))
|
|
|
|
(check-true (hash-has-key? gqpm-transitions bi))
|
2019-06-17 21:15:08 +00:00
|
|
|
(define txns2 (hash-ref gqpm-transitions bi))
|
|
|
|
(check-true (set? txns2))
|
|
|
|
(define dests2 (for/set ([t (in-set txns2)])
|
|
|
|
(transition-dest t)))
|
2019-03-28 18:55:48 +00:00
|
|
|
(check-true (set? dests2))
|
|
|
|
(check-true (set-member? dests2 (set 'get-quotes))))
|
|
|
|
|
|
|
|
(test-case
|
|
|
|
"simplified poll should quit"
|
|
|
|
(define poll/simpl
|
|
|
|
(Role
|
|
|
|
'poll-members
|
|
|
|
(list
|
|
|
|
(Reacts
|
2019-06-13 12:34:34 +00:00
|
|
|
(Asserted (book-interest String String ⋆))
|
2019-03-28 18:55:48 +00:00
|
|
|
(list
|
|
|
|
(Branch
|
|
|
|
(list
|
|
|
|
(Stop 'poll-members
|
|
|
|
(Branch (list
|
|
|
|
(Stop 'get-quotes (list))
|
|
|
|
(list))))
|
|
|
|
(list))))))))
|
|
|
|
(define transition# (describe-role poll/simpl))
|
|
|
|
(check-true (hash? transition#))
|
2019-06-13 12:34:34 +00:00
|
|
|
(define bi (Asserted (book-interest String String ⋆)))
|
2019-03-28 18:55:48 +00:00
|
|
|
(check-true (hash-has-key? transition# bi))
|
|
|
|
(define effs (hash-ref transition# bi))
|
|
|
|
(check-true (set? effs))
|
|
|
|
(check-true (set-member? effs (list (stop 'poll-members))))
|
|
|
|
)
|
|
|
|
(test-case
|
|
|
|
"Body->effects of simplified poll"
|
|
|
|
(define poll/simpl/body
|
|
|
|
(Stop 'poll-members
|
|
|
|
(Branch (list
|
|
|
|
(Stop 'get-quotes (list))
|
|
|
|
(list)))))
|
|
|
|
(define effs (Body->effects poll/simpl/body))
|
|
|
|
(check-true (set? effs))
|
|
|
|
(check-true (set-member? effs (list (stop 'poll-members) (stop 'get-quotes))))
|
|
|
|
(check-true (set-member? effs (list (stop 'poll-members)))))
|
|
|
|
(test-case
|
|
|
|
"Body->effects of even more simplified poll"
|
|
|
|
(define poll/simpl/body/simpl
|
|
|
|
(Branch (list
|
|
|
|
(Stop 'get-quotes (list))
|
|
|
|
(list))))
|
|
|
|
(define effs (Body->effects poll/simpl/body/simpl))
|
|
|
|
(check-true (set? effs))
|
|
|
|
(check-equal? effs
|
|
|
|
(set (list (stop 'get-quotes)) (list)))))
|
2019-03-15 21:48:52 +00:00
|
|
|
|
2019-03-19 20:44:10 +00:00
|
|
|
;; Role -> FacetTree
|
|
|
|
(define (make-facet-tree role)
|
|
|
|
(let loop (;; the work list contains pairs describing the immediate parent and a thing to analyze
|
|
|
|
[work (list (cons #f role))]
|
|
|
|
[downs (hash)]
|
|
|
|
[ups (hash)])
|
|
|
|
(match work
|
|
|
|
['()
|
|
|
|
(facet-tree downs ups)]
|
|
|
|
[(cons (cons parent T) rest)
|
|
|
|
(match T
|
2019-06-18 14:26:04 +00:00
|
|
|
[(or (Sends _)
|
|
|
|
(Realizes _))
|
2019-06-17 21:15:08 +00:00
|
|
|
(loop rest downs ups)]
|
2019-07-29 22:22:48 +00:00
|
|
|
[(Spawn role)
|
|
|
|
(loop (cons (cons #f role) rest) downs ups)]
|
2019-03-19 20:44:10 +00:00
|
|
|
[(Role nm eps)
|
|
|
|
;; record the parent/child relationship
|
|
|
|
(define downs2 (hash-update downs
|
|
|
|
parent
|
|
|
|
((curry cons) nm)
|
|
|
|
(list)))
|
|
|
|
(define downs3 (hash-set downs2 nm (list)))
|
|
|
|
(define ups2 (hash-set ups nm parent))
|
|
|
|
(define more-work*
|
|
|
|
(for/list ([ep (in-list eps)]
|
|
|
|
#:when (Reacts? ep))
|
2019-03-22 19:14:49 +00:00
|
|
|
(match-define (Reacts _ body) ep)
|
|
|
|
(map ((curry cons) nm) (Body->actions body))))
|
2019-03-19 20:44:10 +00:00
|
|
|
(loop (apply append rest more-work*)
|
|
|
|
downs3
|
|
|
|
ups2)]
|
2019-03-22 19:14:49 +00:00
|
|
|
[(Stop target body)
|
2019-07-29 22:22:48 +00:00
|
|
|
(define new-parent (hash-ref ups target #f))
|
2019-03-19 20:44:10 +00:00
|
|
|
(define more-work
|
2019-03-22 19:14:49 +00:00
|
|
|
(for/list ([k (in-list (Body->actions body))])
|
2019-03-19 20:44:10 +00:00
|
|
|
(cons new-parent k)))
|
|
|
|
(loop (append rest more-work)
|
|
|
|
downs
|
|
|
|
ups)])])))
|
|
|
|
|
2019-07-29 22:22:48 +00:00
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"basic spawn functionality"
|
|
|
|
(define seller (parse-T real-seller-ty))
|
|
|
|
(define seller+spawn (Role 'start (list (Reacts StartEvt (Spawn seller)))))
|
|
|
|
(define rg (run/timeout (thunk (compile seller+spawn))))
|
|
|
|
(check-true (role-graph? rg))
|
2021-01-06 17:08:13 +00:00
|
|
|
(define rgi (compile/internal-events rg))
|
2019-07-29 22:22:48 +00:00
|
|
|
(check-true (role-graph? rgi))
|
|
|
|
(define srg (compile seller))
|
2020-05-29 19:18:18 +00:00
|
|
|
(check-true (run/timeout (thunk (simulates?/rg rg rg))))
|
|
|
|
(check-false (run/timeout (thunk (simulates?/rg rg srg))))
|
2020-12-21 16:07:29 +00:00
|
|
|
(check-true (run/timeout (thunk (simulates?/rg srg rg))))
|
2020-05-29 19:18:18 +00:00
|
|
|
(check-true (run/timeout (thunk (simulates?/rg rgi srg))))
|
|
|
|
(check-true (run/timeout (thunk (simulates?/rg srg rgi)))))
|
2019-07-30 20:03:19 +00:00
|
|
|
(test-case
|
|
|
|
"internal events across spawns"
|
|
|
|
(define role
|
2020-05-29 19:18:18 +00:00
|
|
|
(Role 'start
|
|
|
|
(list
|
|
|
|
(Know Int)
|
|
|
|
(Reacts StartEvt
|
|
|
|
(Spawn (Role 'spawned
|
|
|
|
(list (Reacts (Know Int)
|
|
|
|
(Role 'know (list))))))))))
|
2019-07-30 20:03:19 +00:00
|
|
|
(define rg (run/timeout (thunk (compile role))))
|
|
|
|
(check-true (role-graph? rg))
|
2021-01-06 17:08:13 +00:00
|
|
|
(define rgi (run/timeout (thunk (compile/internal-events rg))))
|
2019-07-30 20:03:19 +00:00
|
|
|
(check-true (role-graph? rgi))
|
|
|
|
(define state-names (hash-keys (role-graph-states rgi)))
|
|
|
|
(for ([sn (in-list state-names)])
|
|
|
|
;; that facet shouldn't be reachable
|
|
|
|
(check-false (set-member? sn 'know)))))
|
2019-07-29 22:22:48 +00:00
|
|
|
|
2019-03-22 19:14:49 +00:00
|
|
|
;; Body -> (Listof T)
|
|
|
|
;; extract the list of all Role, Stop, and Spawn types from a Body
|
|
|
|
(define (Body->actions body)
|
|
|
|
(match body
|
|
|
|
[(? list?)
|
|
|
|
(apply append (map Body->actions body))]
|
|
|
|
[(Branch arms)
|
|
|
|
(apply append (map Body->actions arms))]
|
|
|
|
[T (list T)]))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"Body->actions Branch"
|
|
|
|
(define body (Branch
|
|
|
|
(list
|
|
|
|
(Stop 'leader
|
|
|
|
(Role 'announce
|
|
|
|
(list
|
|
|
|
(Shares (Struct 'book-of-the-month String)))))
|
|
|
|
(Stop 'poll (list)))))
|
|
|
|
(check-equal? (Body->actions body)
|
|
|
|
(list (Stop 'leader
|
|
|
|
(Role 'announce
|
|
|
|
(list
|
|
|
|
(Shares (Struct 'book-of-the-month String)))))
|
|
|
|
(Stop 'poll (list))))))
|
|
|
|
|
2019-03-19 20:44:10 +00:00
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"manager facet tree (one facet)"
|
|
|
|
(define ft (make-facet-tree manager))
|
|
|
|
(check-true (facet-tree? ft))
|
|
|
|
(match-define (facet-tree downs ups) ft)
|
|
|
|
(check-equal? (hash-ref downs #f)
|
|
|
|
(list 'account-manager))
|
|
|
|
(check-equal? (hash-ref downs 'account-manager)
|
|
|
|
(list))
|
|
|
|
(check-equal? (hash-ref ups 'account-manager)
|
|
|
|
#f))
|
|
|
|
(test-case
|
|
|
|
"seller facet tree (two facets)"
|
|
|
|
(define ft (make-facet-tree seller))
|
|
|
|
(check-true (facet-tree? ft))
|
|
|
|
(match-define (facet-tree downs ups) ft)
|
|
|
|
(check-equal? (hash-ref downs #f)
|
|
|
|
(list 'seller))
|
|
|
|
(check-equal? (hash-ref downs 'seller)
|
|
|
|
(list 'fulfill))
|
|
|
|
(check-equal? (hash-ref ups 'seller)
|
|
|
|
#f)
|
|
|
|
(check-equal? (hash-ref ups 'fulfill)
|
|
|
|
'seller)
|
|
|
|
|
|
|
|
(test-case
|
|
|
|
"leader-spec facet tree (stops facets)"
|
|
|
|
(define ft (make-facet-tree leader-spec))
|
|
|
|
(check-true (facet-tree? ft))
|
|
|
|
(match-define (facet-tree downs ups) ft)
|
|
|
|
(check-equal? (list->set (hash-ref downs #f))
|
|
|
|
(set 'leader 'announce))
|
|
|
|
(check-equal? (hash-ref downs 'leader)
|
|
|
|
(list 'poll))
|
|
|
|
(check-equal? (hash-ref downs 'poll)
|
|
|
|
(list))
|
|
|
|
(check-equal? (hash-ref downs 'announce)
|
|
|
|
(list))
|
|
|
|
(check-equal? (hash-ref ups 'leader)
|
|
|
|
#f)
|
|
|
|
(check-equal? (hash-ref ups 'announce)
|
|
|
|
#f)
|
|
|
|
(check-equal? (hash-ref ups 'poll)
|
|
|
|
'leader))
|
|
|
|
))
|
|
|
|
|
|
|
|
;; FacetName FacetName FacetTree -> Bool
|
|
|
|
;; determine if the first argument is a child*, or equal to, the second
|
|
|
|
(define (ancestor? desc ansc ft)
|
|
|
|
(cond
|
|
|
|
[(equal? desc ansc)
|
|
|
|
#t]
|
|
|
|
[else
|
|
|
|
(define parent (hash-ref (facet-tree-up ft) desc))
|
|
|
|
(and parent
|
|
|
|
(ancestor? parent ansc ft))]))
|
|
|
|
|
2019-05-30 17:20:51 +00:00
|
|
|
;; FacetName FacetName FacetTree -> (U #f Nat)
|
|
|
|
;; determine if the first argument is a child*, or equal to, the second; if so,
|
|
|
|
;; return their distance from one another in the tree
|
|
|
|
(define (ancestor?/dist desc ansc ft)
|
|
|
|
(cond
|
|
|
|
[(equal? desc ansc)
|
|
|
|
0]
|
|
|
|
[else
|
|
|
|
(define parent (hash-ref (facet-tree-up ft) desc))
|
|
|
|
(define ans? (and parent (ancestor?/dist parent ansc ft)))
|
|
|
|
(and ans?
|
|
|
|
(add1 ans?))]))
|
|
|
|
|
2019-03-19 20:44:10 +00:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"ancestors in leader-spec facet tree"
|
|
|
|
(define ft (make-facet-tree leader-spec))
|
|
|
|
(check-true (ancestor? 'leader 'leader ft))
|
|
|
|
(check-true (ancestor? 'poll 'leader ft))
|
|
|
|
(check-false (ancestor? 'leader 'poll ft))
|
2019-05-30 17:20:51 +00:00
|
|
|
(check-false (ancestor? 'announce 'leader ft)))
|
|
|
|
(test-case
|
|
|
|
"ancestor?/dist in leader-spec facet tree"
|
|
|
|
(define ft (make-facet-tree leader-spec))
|
|
|
|
(check-equal? (ancestor?/dist 'leader 'leader ft) 0)
|
|
|
|
(check-equal? (ancestor?/dist 'poll 'leader ft) 1)
|
|
|
|
(check-false (ancestor?/dist 'leader 'poll ft))
|
|
|
|
(check-false (ancestor?/dist 'announce 'leader ft))))
|
2019-03-19 20:44:10 +00:00
|
|
|
|
2019-06-17 21:15:08 +00:00
|
|
|
;; (Listof RoleEffect)
|
|
|
|
;; StateName
|
2019-05-30 17:20:51 +00:00
|
|
|
;; FacetTree
|
|
|
|
;; (Hashof FacetName TransitionDesc)
|
2019-06-17 21:15:08 +00:00
|
|
|
;; -> (Setof Transition)
|
2019-05-30 17:20:51 +00:00
|
|
|
;; determine the state resulting from some effects, given the currently active
|
|
|
|
;; facets and a description of possible facet locations and behavior.
|
|
|
|
(define (apply-effects effs st ft txn#)
|
|
|
|
(let loop ([st st]
|
|
|
|
[effs effs])
|
|
|
|
(match effs
|
|
|
|
['()
|
2019-06-17 21:15:08 +00:00
|
|
|
(set (transition '() st))]
|
2019-05-30 17:20:51 +00:00
|
|
|
[(cons eff rest)
|
|
|
|
(match eff
|
2019-06-18 14:26:04 +00:00
|
|
|
[(or (send _)
|
|
|
|
(realize _))
|
2019-07-31 15:40:12 +00:00
|
|
|
(for/set ([txn (in-set (loop st rest))])
|
|
|
|
(transition (cons eff (transition-effs txn)) (transition-dest txn)))]
|
2019-05-30 17:20:51 +00:00
|
|
|
[(start nm)
|
|
|
|
(define st+ (set-add st nm))
|
2019-06-26 14:09:00 +00:00
|
|
|
(define start-effs (hash-ref (hash-ref txn# nm) (StartOf nm)))
|
2019-05-30 17:20:51 +00:00
|
|
|
(cond
|
|
|
|
[(set-empty? start-effs)
|
|
|
|
(loop st+ rest)]
|
|
|
|
[else
|
2019-06-05 20:20:09 +00:00
|
|
|
(for*/set ([eff* (in-set start-effs)]
|
|
|
|
[result (in-set (loop st+ (append rest eff*)))])
|
|
|
|
result)])]
|
2019-05-30 17:20:51 +00:00
|
|
|
[(stop nm)
|
|
|
|
(define children (find-children ft nm st))
|
|
|
|
(define st-
|
|
|
|
(for/fold ([st st])
|
|
|
|
([c (in-list children)])
|
|
|
|
(set-remove st c)))
|
2021-02-22 16:30:43 +00:00
|
|
|
(define-values (final-txns _)
|
|
|
|
(for/fold ([txns (set (transition '() st-))]
|
|
|
|
[pending-effs rest])
|
|
|
|
([f-name (in-list children)])
|
|
|
|
(define stop-effs (hash-ref (hash-ref txn# f-name) (StopOf f-name)))
|
|
|
|
(define stop-effs+ (if (set-empty? stop-effs)
|
|
|
|
(set '())
|
|
|
|
stop-effs))
|
|
|
|
(define new-txns
|
|
|
|
(for*/set ([txn (in-set txns)]
|
|
|
|
[st (in-value (transition-dest txn))]
|
|
|
|
[effs* (in-set stop-effs+)]
|
|
|
|
[next-txn (in-set (loop st (append pending-effs effs*)))])
|
|
|
|
(transition (append (transition-effs txn)
|
|
|
|
(transition-effs next-txn))
|
|
|
|
(transition-dest next-txn))))
|
|
|
|
(values new-txns '())))
|
|
|
|
final-txns])])))
|
2019-05-30 17:20:51 +00:00
|
|
|
|
2019-07-31 15:40:12 +00:00
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"bug in apply-effects"
|
|
|
|
;; was dropping everything after the first send or realize effect
|
|
|
|
(define txns (apply-effects (list (realize Int) (realize String))
|
|
|
|
(set)
|
|
|
|
(facet-tree (hash) (hash))
|
|
|
|
(hash)))
|
2021-02-22 16:30:43 +00:00
|
|
|
(check-equal? txns (set (transition (list (realize Int) (realize String)) (set)))))
|
|
|
|
(test-case
|
|
|
|
"another bug in apply-effects"
|
|
|
|
;; was duplicating some effects
|
|
|
|
(define r #s(Role
|
|
|
|
run-a-round342
|
|
|
|
(#s(Shares
|
|
|
|
#s(Struct
|
|
|
|
RoundT
|
|
|
|
(#s(Base Symbol) #s(Base String) #s(List #s(Base String)))))
|
|
|
|
#s(Reacts
|
|
|
|
Start
|
|
|
|
#s(Role
|
|
|
|
wait364
|
|
|
|
(#s(Reacts
|
|
|
|
#s(Asserted #s(Struct LaterThanT (#s(Base Int))))
|
|
|
|
#s(Branch
|
|
|
|
((#s(Branch
|
|
|
|
((#s(Stop
|
|
|
|
run-a-round342
|
|
|
|
(#s(Role
|
|
|
|
over356
|
|
|
|
(#s(Shares
|
|
|
|
#s(Struct
|
|
|
|
ElectedT
|
|
|
|
(#s(Base String)
|
|
|
|
#s(Base String)))))))))
|
|
|
|
(#s(Stop
|
|
|
|
run-a-round342
|
|
|
|
(#s(Realizes
|
|
|
|
#s(Struct
|
|
|
|
StartRoundT
|
|
|
|
(#s(Set #s(Base String))
|
|
|
|
#s(Set #s(Base String)))))))))))
|
|
|
|
())))))))))
|
|
|
|
(define labeled-role (label-internal-events r))
|
|
|
|
(define roles# (describe-roles labeled-role))
|
|
|
|
(define ft (make-facet-tree r))
|
|
|
|
(define current (set 'wait364 'run-a-round342))
|
|
|
|
(define eff* (list
|
|
|
|
(stop 'run-a-round342)
|
|
|
|
(realize
|
|
|
|
'#s(internal-label
|
|
|
|
initial31336
|
|
|
|
#s(Struct
|
|
|
|
StartRoundT
|
|
|
|
(#s(Set #s(Base String)) #s(Set #s(Base String))))))))
|
|
|
|
(check-equal? (apply-effects eff* current ft roles#)
|
|
|
|
(set (transition
|
|
|
|
(list
|
|
|
|
(realize
|
|
|
|
'#s(internal-label
|
|
|
|
initial31336
|
|
|
|
#s(Struct StartRoundT (#s(Set #s(Base String)) #s(Set #s(Base String)))))))
|
|
|
|
(set))))))
|
2019-07-31 15:40:12 +00:00
|
|
|
|
2019-05-30 17:20:51 +00:00
|
|
|
;; FacetTree FacetName (Setof FacetName) -> (List FacetName)
|
|
|
|
;; return the facets in names that are children of the given facet nm, ordered
|
|
|
|
;; by their distance (farthest children first etc.)
|
|
|
|
(define (find-children ft nm names)
|
|
|
|
(define relations
|
|
|
|
(for*/list ([n (in-set names)]
|
|
|
|
[ans? (in-value (ancestor?/dist n nm ft))]
|
|
|
|
#:when ans?)
|
|
|
|
(list n ans?)))
|
|
|
|
(define farthest-to-nearest (sort relations > #:key second))
|
|
|
|
(map first farthest-to-nearest))
|
2019-03-15 21:48:52 +00:00
|
|
|
|
|
|
|
;; Role -> (Hashof FacetName TransitionDesc)
|
|
|
|
;; Extract a description of all roles mentioned in a Role
|
|
|
|
(define (describe-roles role)
|
2019-03-22 19:34:38 +00:00
|
|
|
(define all-roles (enumerate-roles role))
|
|
|
|
(for/hash ([r (in-list all-roles)])
|
|
|
|
(define txn (describe-role r))
|
|
|
|
(values (Role-nm r)
|
|
|
|
txn)))
|
|
|
|
|
|
|
|
;; T -> (Listof Role)
|
|
|
|
;; Find all nested role descriptions
|
|
|
|
(define (enumerate-roles t)
|
|
|
|
(match t
|
|
|
|
[(Role _ eps)
|
|
|
|
(define rs
|
|
|
|
(for*/list ([ep (in-list eps)]
|
|
|
|
#:when (Reacts? ep)
|
|
|
|
[body (in-value (Reacts-body ep))]
|
|
|
|
[act (in-list (Body->actions body))]
|
|
|
|
[role (in-list (enumerate-roles act))])
|
|
|
|
role))
|
|
|
|
(cons t rs)]
|
|
|
|
[(Stop _ body)
|
|
|
|
(for*/list ([act (in-list (Body->actions body))]
|
|
|
|
[role (in-list (enumerate-roles act))])
|
|
|
|
role)]
|
2019-06-18 14:26:04 +00:00
|
|
|
[(or (Sends _)
|
|
|
|
(Realizes _))
|
2019-06-17 21:15:08 +00:00
|
|
|
'()]
|
2019-07-29 22:22:48 +00:00
|
|
|
[(Spawn r)
|
|
|
|
(enumerate-roles r)]))
|
2019-03-15 21:48:52 +00:00
|
|
|
|
|
|
|
;; Role -> TransitionDesc
|
|
|
|
;; determine how the event handlers in a role alter the facet tree
|
|
|
|
(define (describe-role role)
|
|
|
|
(match role
|
|
|
|
[(Role nm eps)
|
2019-06-26 14:09:00 +00:00
|
|
|
(for/fold ([txns (txn-desc0 nm)])
|
2019-03-15 21:48:52 +00:00
|
|
|
([ep (in-list eps)]
|
|
|
|
#:when (Reacts? ep))
|
2019-03-22 19:14:49 +00:00
|
|
|
(match-define (Reacts evt body) ep)
|
|
|
|
(define effects (Body->effects body))
|
2019-05-30 17:20:51 +00:00
|
|
|
(when (equal? StopEvt evt)
|
|
|
|
;; facets that start inside a stop handler will get shutdown.
|
|
|
|
(define effects+
|
|
|
|
(for/set ([effs* (in-set effects)])
|
|
|
|
(define extra-stops
|
|
|
|
(for/list ([eff (in-list effs*)]
|
|
|
|
#:when (start? eff))
|
|
|
|
(stop (start-nm eff))))
|
|
|
|
(append effs* extra-stops)))
|
|
|
|
(set! effects effects+))
|
2019-03-15 21:48:52 +00:00
|
|
|
(cond
|
2019-03-22 19:14:49 +00:00
|
|
|
[(or (set-empty? effects)
|
|
|
|
(equal? effects (set '())))
|
2019-03-15 21:48:52 +00:00
|
|
|
txns]
|
|
|
|
[else
|
2019-06-26 14:09:00 +00:00
|
|
|
(define evt+
|
|
|
|
(match evt
|
|
|
|
[(== StartEvt)
|
|
|
|
(StartOf nm)]
|
|
|
|
[(== StopEvt)
|
|
|
|
(StopOf nm)]
|
|
|
|
[_
|
|
|
|
evt]))
|
2019-03-22 19:14:49 +00:00
|
|
|
(define (update-effect-set existing)
|
|
|
|
(combine-effect-sets effects existing))
|
2019-06-26 14:09:00 +00:00
|
|
|
(hash-update txns evt+ update-effect-set (set))]))]))
|
2019-03-22 19:14:49 +00:00
|
|
|
|
|
|
|
;; (Setof (Listof X)) (Setof (Listof X)) -> (Setof (Listof X))
|
|
|
|
;; two separately analyzed sets of effects may combine in any way
|
|
|
|
(define (combine-effect-sets s1 s2)
|
|
|
|
(cond
|
|
|
|
[(set-empty? s1)
|
|
|
|
s2]
|
|
|
|
[(set-empty? s2)
|
|
|
|
s1]
|
|
|
|
[else
|
|
|
|
(for*/set ([e1* (in-set s1)]
|
|
|
|
[e2* (in-set s2)])
|
|
|
|
(append e1* e2*))]))
|
2019-03-15 21:48:52 +00:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"describe simple role"
|
|
|
|
(define desc (describe-roles manager))
|
|
|
|
(check-true (hash-has-key? desc 'account-manager))
|
|
|
|
(check-equal? (hash-ref desc 'account-manager)
|
2019-06-26 14:09:00 +00:00
|
|
|
(txn-desc0 'account-manager)))
|
2019-03-15 21:48:52 +00:00
|
|
|
(test-case
|
|
|
|
"describe nested role"
|
|
|
|
(define desc (describe-roles seller))
|
|
|
|
(check-true (hash-has-key? desc 'seller))
|
|
|
|
(check-true (hash-has-key? desc 'fulfill))
|
|
|
|
(check-equal? (hash-ref desc 'fulfill)
|
2019-06-26 14:09:00 +00:00
|
|
|
(txn-desc0 'fulfill))
|
2019-03-15 21:48:52 +00:00
|
|
|
(define seller-txns (hash-ref desc 'seller))
|
|
|
|
(define quote-request
|
2019-03-29 20:12:46 +00:00
|
|
|
(Observe (book-quote String ⋆)))
|
2019-06-13 12:34:34 +00:00
|
|
|
(check-true (hash-has-key? seller-txns (Asserted quote-request)))
|
|
|
|
(check-equal? (hash-ref seller-txns (Asserted quote-request))
|
2019-03-22 19:14:49 +00:00
|
|
|
(set (list (start 'fulfill)))))
|
|
|
|
(test-case
|
|
|
|
"describe-roles bug"
|
|
|
|
(define role (Role 'poll
|
|
|
|
(list
|
2019-06-13 12:34:34 +00:00
|
|
|
(Reacts (Asserted Int)
|
2019-03-22 19:14:49 +00:00
|
|
|
(Branch
|
|
|
|
(list (Stop 'leader (Role 'announce (list (Shares Int))))
|
|
|
|
(Stop 'poll (list))))))))
|
|
|
|
(define desc (describe-roles role))
|
|
|
|
(check-true (hash? desc))
|
|
|
|
(check-true (hash-has-key? desc 'poll))
|
|
|
|
(define txns (hash-ref desc 'poll))
|
2019-06-13 12:34:34 +00:00
|
|
|
(check-true (hash-has-key? txns (Asserted Int)))
|
|
|
|
(check-equal? (hash-ref txns (Asserted Int))
|
2019-03-22 19:14:49 +00:00
|
|
|
(set (list (stop 'leader) (start 'announce))
|
|
|
|
(list (stop 'poll)))))
|
2019-03-19 20:44:10 +00:00
|
|
|
(test-case
|
|
|
|
"leader-spec announce"
|
|
|
|
(define desc (describe-roles leader-spec))
|
|
|
|
(check-true (hash-has-key? desc 'announce))
|
|
|
|
(check-equal? (hash-ref desc 'announce)
|
2019-06-26 14:09:00 +00:00
|
|
|
(txn-desc0 'announce)))
|
2019-03-19 20:44:10 +00:00
|
|
|
(test-case
|
|
|
|
"leader-spec transitions from {leader,poll} to {leader}"
|
|
|
|
(define desc (describe-roles leader-spec))
|
|
|
|
(check-true (hash-has-key? desc 'poll))
|
|
|
|
(define poll-txns (hash-ref desc 'poll))
|
2019-06-13 12:34:34 +00:00
|
|
|
(define evt (Asserted (book-interest String String Bool)))
|
2019-03-22 19:14:49 +00:00
|
|
|
(check-true (hash-has-key? poll-txns evt))
|
|
|
|
(define effs (hash-ref poll-txns evt))
|
|
|
|
(check-true (set-member? effs (list (stop 'poll))))))
|
|
|
|
|
|
|
|
;; Body -> (Setof (Listof RoleEffect))
|
|
|
|
(define (Body->effects body)
|
|
|
|
(match body
|
|
|
|
['()
|
|
|
|
(set)]
|
|
|
|
[(cons b more)
|
|
|
|
(define fst (Body->effects b))
|
|
|
|
(define later (Body->effects more))
|
|
|
|
(cond
|
|
|
|
[(set-empty? fst)
|
|
|
|
later]
|
|
|
|
[(set-empty? later)
|
|
|
|
fst]
|
|
|
|
[else
|
|
|
|
(for*/set ([f (in-set fst)]
|
|
|
|
[l (in-set later)])
|
|
|
|
(append f l))])]
|
|
|
|
[(Branch (list b ...))
|
|
|
|
(for/fold ([agg (set)])
|
|
|
|
([b (in-list b)])
|
2019-03-28 18:55:48 +00:00
|
|
|
(define effs (Body->effects b))
|
|
|
|
;; it's important to remember when "do nothing" is one of the alternatives of a branch
|
|
|
|
(define effs++
|
|
|
|
(if (set-empty? effs)
|
|
|
|
(set '())
|
|
|
|
effs))
|
|
|
|
(set-union agg effs++))]
|
2019-03-22 19:14:49 +00:00
|
|
|
[(Role nm _)
|
|
|
|
(set (list (start nm)))]
|
2019-06-17 21:15:08 +00:00
|
|
|
[(Sends τ)
|
|
|
|
(set (list (send τ)))]
|
2019-06-18 14:26:04 +00:00
|
|
|
[(Realizes τ)
|
|
|
|
(set (list (realize τ)))]
|
2019-03-22 19:14:49 +00:00
|
|
|
[(Stop nm more)
|
|
|
|
(define effects (Body->effects more))
|
|
|
|
(cond
|
|
|
|
[(set-empty? effects)
|
|
|
|
(set (list (stop nm)))]
|
|
|
|
[else
|
|
|
|
(for/set ([eff* (in-set effects)])
|
|
|
|
(cons (stop nm) eff*))])]
|
2019-07-29 22:22:48 +00:00
|
|
|
[(Spawn r)
|
|
|
|
(set (list (start (Role-nm r))))]))
|
2019-03-22 19:14:49 +00:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"Body->effects"
|
|
|
|
(check-equal? (Body->effects '())
|
|
|
|
(set))
|
|
|
|
(check-equal? (Body->effects (Branch '()))
|
|
|
|
(set))
|
|
|
|
(check-equal? (Body->effects manager)
|
|
|
|
(set (list (start 'account-manager))))
|
|
|
|
(check-equal? (Body->effects (list manager))
|
|
|
|
(set (list (start 'account-manager))))
|
|
|
|
(check-equal? (Body->effects (Branch (list manager)))
|
|
|
|
(set (list (start 'account-manager))))
|
|
|
|
(check-equal? (Body->effects (list manager client))
|
|
|
|
(set (list (start 'account-manager)
|
|
|
|
(start 'client))))
|
|
|
|
(check-equal? (Body->effects (Branch (list manager client)))
|
|
|
|
(set (list (start 'account-manager))
|
|
|
|
(list (start 'client))))
|
|
|
|
(check-equal? (Body->effects (list manager
|
|
|
|
(Branch (list client seller))))
|
|
|
|
(set (list (start 'account-manager) (start 'client))
|
|
|
|
(list (start 'account-manager) (start 'seller)))))
|
|
|
|
(test-case
|
|
|
|
"Body->effects bug?"
|
|
|
|
(define body (Branch
|
|
|
|
(list (Stop 'leader (Role 'announce (list (Shares Int))))
|
|
|
|
(Stop 'poll (list)))))
|
|
|
|
(check-equal? (Body->effects body)
|
|
|
|
(set (list (stop 'leader) (start 'announce))
|
|
|
|
(list (stop 'poll))))))
|
2019-03-19 20:44:10 +00:00
|
|
|
|
2019-07-30 20:03:19 +00:00
|
|
|
;; Role -> Role
|
|
|
|
;; label internal events & handlers with actor-unique identifiers
|
|
|
|
(define (label-internal-events T)
|
|
|
|
(let walk ([subj T] ;; T or EP or Body or D+
|
|
|
|
[current-actor (gensym 'initial)])
|
|
|
|
(define (map-walk s) (walk s current-actor))
|
|
|
|
(match subj
|
|
|
|
[(Role nm eps)
|
|
|
|
(Role nm (map map-walk eps))]
|
|
|
|
[(Spawn r)
|
|
|
|
(define new-actor-id (gensym (Role-nm r)))
|
|
|
|
(Spawn (walk r new-actor-id))]
|
|
|
|
[(Realizes ty)
|
|
|
|
(Realizes (internal-label current-actor ty))]
|
|
|
|
[(Stop nm body)
|
|
|
|
(Stop nm (walk body current-actor))]
|
|
|
|
[(Reacts D body)
|
|
|
|
(define D+ (walk D current-actor))
|
|
|
|
(Reacts D+ (walk body current-actor))]
|
|
|
|
[(Know ty)
|
|
|
|
(Know (internal-label current-actor ty))]
|
|
|
|
[(? cons?)
|
|
|
|
(map map-walk subj)]
|
|
|
|
[(Branch bodies)
|
|
|
|
(Branch (map map-walk bodies))]
|
|
|
|
[(Forget ty)
|
|
|
|
(Forget (internal-label current-actor ty))]
|
|
|
|
[(Realize ty)
|
|
|
|
(Realize (internal-label current-actor ty))]
|
|
|
|
[_
|
|
|
|
subj])))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "label internal events"
|
|
|
|
(define role
|
|
|
|
(Role 'start
|
|
|
|
(list
|
|
|
|
(Know Int)
|
|
|
|
(Reacts StartEvt
|
|
|
|
(Spawn (Role 'spawned
|
|
|
|
(list (Reacts (Know Int)
|
|
|
|
(Role 'know (list))))))))))
|
|
|
|
(define role+ (run/timeout (thunk (label-internal-events role))))
|
|
|
|
(check-true (Role? role+))
|
|
|
|
(match role+
|
|
|
|
[(Role _
|
|
|
|
(list
|
|
|
|
(Know (internal-label label1 Int))
|
|
|
|
(Reacts _
|
|
|
|
(Spawn (Role _
|
|
|
|
(list (Reacts (Know (internal-label label2 Int))
|
|
|
|
(Role _ (list)))))))))
|
|
|
|
(check-not-equal? label1 label2)]
|
|
|
|
[_
|
|
|
|
(fail "labelled role didn't match expected structure")])))
|
|
|
|
|
2019-03-22 20:15:54 +00:00
|
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
;; "Simulation"
|
|
|
|
|
|
|
|
;; τ τ -> Bool
|
|
|
|
;; subtyping on basic types
|
|
|
|
(define (<:? τ1 τ2)
|
|
|
|
(cond
|
|
|
|
[(eq? τ1 τ2)
|
|
|
|
#t]
|
|
|
|
[else
|
|
|
|
(match (list τ1 τ2)
|
|
|
|
[(list _ (== ⋆))
|
|
|
|
#t]
|
2019-05-30 14:09:34 +00:00
|
|
|
[(list (Base t1) (Base t2))
|
|
|
|
(equal? t1 t2)]
|
2019-07-30 20:03:19 +00:00
|
|
|
[(list (internal-label l1 t1) (internal-label l2 t2))
|
|
|
|
(and (equal? l1 l2)
|
|
|
|
(<:? t1 t2))]
|
2019-03-22 20:15:54 +00:00
|
|
|
[(list (U τs) _)
|
|
|
|
(for/and ([τ (in-list τs)])
|
|
|
|
(<:? τ τ2))]
|
|
|
|
[(list _ (U τs))
|
|
|
|
(for/or ([τ (in-list τs)])
|
|
|
|
(<:? τ1 τ))]
|
|
|
|
[(list (Observe τ11) (Observe τ22))
|
|
|
|
(<:? τ11 τ22)]
|
2019-06-03 15:16:16 +00:00
|
|
|
[(list (List τ11) (List τ22))
|
|
|
|
(<:? τ11 τ22)]
|
|
|
|
[(list (Set τ11) (Set τ22))
|
|
|
|
(<:? τ11 τ22)]
|
|
|
|
[(list (Hash τk1 τv1) (Hash τk2 τv2))
|
|
|
|
(and (<:? τk1 τk2)
|
|
|
|
(<:? τv1 τv2))]
|
2019-03-22 20:15:54 +00:00
|
|
|
[(list (Struct nm1 τs1) (Struct nm2 τs2))
|
|
|
|
(and (equal? nm1 nm2)
|
|
|
|
(= (length τs1) (length τs2))
|
|
|
|
(for/and ([τ11 (in-list τs1)]
|
|
|
|
[τ22 (in-list τs2)])
|
|
|
|
(<:? τ11 τ22)))]
|
|
|
|
[_
|
|
|
|
#f])]))
|
|
|
|
|
2019-03-26 01:01:22 +00:00
|
|
|
;; D D -> Bool
|
|
|
|
;; subtyping lifted over event descriptions
|
|
|
|
(define (D<:? D1 D2)
|
|
|
|
(match (list D1 D2)
|
2019-06-05 20:20:09 +00:00
|
|
|
[(list _ (== DataflowEvt))
|
|
|
|
;; TODO - sketchy, intuition "dataflow can happen at any time", though it
|
|
|
|
;; might actually take the place of multiple transitions
|
|
|
|
#t]
|
2019-06-13 12:34:34 +00:00
|
|
|
[(list (Asserted τ1) (Asserted τ2))
|
2019-03-26 01:01:22 +00:00
|
|
|
(<:? τ1 τ2)]
|
2019-06-13 12:34:34 +00:00
|
|
|
[(list (Retracted τ1) (Retracted τ2))
|
2019-03-26 01:01:22 +00:00
|
|
|
(<:? τ1 τ2)]
|
2019-06-17 15:26:00 +00:00
|
|
|
[(list (Message τ1) (Message τ2))
|
|
|
|
(<:? τ1 τ2)]
|
|
|
|
[(list (Know τ1) (Know τ2))
|
|
|
|
(<:? τ1 τ2)]
|
|
|
|
[(list (Forget τ1) (Forget τ2))
|
|
|
|
(<:? τ1 τ2)]
|
|
|
|
[(list (Realize τ1) (Realize τ2))
|
|
|
|
(<:? τ1 τ2)]
|
2019-06-26 14:09:00 +00:00
|
|
|
[(list (StartOf fn1) (StartOf fn2))
|
|
|
|
(equal? fn1 fn2)]
|
|
|
|
[(list (StopOf fn1) (StopOf fn2))
|
|
|
|
(equal? fn1 fn2)]
|
2019-06-03 15:16:16 +00:00
|
|
|
[(list (== StartEvt) (== StartEvt))
|
|
|
|
#t]
|
|
|
|
[(list (== StopEvt) (== StopEvt))
|
|
|
|
#t]
|
2019-03-26 01:01:22 +00:00
|
|
|
[_
|
|
|
|
#f]))
|
|
|
|
|
2019-06-17 21:15:08 +00:00
|
|
|
;; TransitionEffect TransitionEffect -> Bool
|
|
|
|
;; subtyping lifted over transition effects
|
|
|
|
(define (eff<:? e1 e2)
|
|
|
|
(match (list e1 e2)
|
|
|
|
[(list (send t1) (send t2))
|
|
|
|
(<:? t1 t2)]
|
2019-06-18 14:26:04 +00:00
|
|
|
[(list (realize t1) (realize t2))
|
|
|
|
(<:? t1 t2)]
|
2019-06-17 21:15:08 +00:00
|
|
|
[_
|
|
|
|
#f]))
|
|
|
|
|
2019-03-26 01:01:22 +00:00
|
|
|
;; Role -> (Setof τ)
|
|
|
|
;; Compute the set of assertions the role contributes (on its own, not
|
|
|
|
;; considering parent assertions)
|
|
|
|
(define (role-assertions r)
|
2019-06-05 20:20:09 +00:00
|
|
|
(for*/set ([ep (in-list (Role-eps r))]
|
|
|
|
[τ? (in-value (EP-assertion ep))]
|
|
|
|
#:when τ?)
|
|
|
|
τ?))
|
|
|
|
|
|
|
|
;; EP -> (U #f τ)
|
|
|
|
;; the type of assertion and endpoint contributes, otherwise #f for
|
|
|
|
;; dataflow/start/stop
|
|
|
|
(define (EP-assertion EP)
|
|
|
|
(match EP
|
|
|
|
[(Shares τ)
|
|
|
|
τ]
|
2020-05-29 19:18:18 +00:00
|
|
|
[(Know τ)
|
|
|
|
τ]
|
2019-06-05 20:20:09 +00:00
|
|
|
[(Reacts D _)
|
|
|
|
(match D
|
2019-06-13 12:34:34 +00:00
|
|
|
[(or (Asserted τ)
|
2019-06-17 15:26:00 +00:00
|
|
|
(Retracted τ)
|
|
|
|
(Message τ))
|
2019-06-05 20:20:09 +00:00
|
|
|
;; TODO - this doesn't put ⋆ in where an underlying pattern uses a capture
|
|
|
|
(Observe τ)]
|
2020-05-29 19:18:18 +00:00
|
|
|
[(or (Know (internal-label id τ))
|
|
|
|
(Forget (internal-label id τ))
|
|
|
|
(Realize (internal-label id τ)))
|
|
|
|
(internal-label id (Observe τ))]
|
2019-06-19 21:17:05 +00:00
|
|
|
[_
|
|
|
|
#f])]
|
|
|
|
[_ #f]))
|
2019-06-05 20:20:09 +00:00
|
|
|
|
|
|
|
(module+ test
|
2020-05-29 15:15:07 +00:00
|
|
|
(test-case
|
|
|
|
"EP-assertion sanity"
|
2019-06-05 20:20:09 +00:00
|
|
|
;; make sure the or pattern above works the way I think it does
|
2019-06-13 12:34:34 +00:00
|
|
|
(check-equal? (EP-assertion (Reacts (Asserted Int) #f))
|
2019-06-05 20:20:09 +00:00
|
|
|
(Observe Int))
|
2019-06-13 12:34:34 +00:00
|
|
|
(check-equal? (EP-assertion (Reacts (Retracted String) #f))
|
2019-06-05 20:20:09 +00:00
|
|
|
(Observe String)))
|
2020-05-29 15:15:07 +00:00
|
|
|
(test-case "EP-assertion/internal regression"
|
2020-05-29 19:18:18 +00:00
|
|
|
(check-equal? (EP-assertion (Reacts (Know (internal-label 'x Int)) '()))
|
2020-05-29 15:15:07 +00:00
|
|
|
(internal-label 'x (Observe Int)))))
|
2019-03-26 01:01:22 +00:00
|
|
|
|
|
|
|
;; an Equation is (equiv StateName StateName)
|
2019-06-17 21:15:08 +00:00
|
|
|
;; INVARIANT: lhs is "implementation", rhs is "specification"
|
2019-03-26 01:01:22 +00:00
|
|
|
;;
|
|
|
|
;; a Goal is one of
|
|
|
|
;; - Equation
|
|
|
|
;; - (one-of (Setof StateMatch))
|
|
|
|
;;
|
2019-06-17 21:15:08 +00:00
|
|
|
;; a StateMatch is a (Setof (equiv Transition Transition))
|
2019-03-26 01:01:22 +00:00
|
|
|
(struct equiv (a b) #:transparent)
|
|
|
|
(struct one-of (opts) #:transparent)
|
|
|
|
|
2019-06-17 21:15:08 +00:00
|
|
|
;; (Setof Transition) (Setof Transition) -> (Setof StateMatch)
|
|
|
|
;; Create potential edge matchings
|
2019-03-26 01:01:22 +00:00
|
|
|
;; In each state matching, each element a of the first set (as) is
|
|
|
|
;; matched with an element b of bs, where each b has at least one state
|
|
|
|
;; matched with it.
|
|
|
|
(define (make-combinations as bs)
|
2019-06-05 20:20:09 +00:00
|
|
|
(define (all-as? xs)
|
|
|
|
(for/and ([a (in-set as)])
|
|
|
|
(for/or ([x (in-list xs)])
|
|
|
|
(match-define (equiv xa _) x)
|
|
|
|
(equal? a xa))))
|
|
|
|
(define (all-bs? xs)
|
2019-03-26 01:01:22 +00:00
|
|
|
(for/and ([b (in-set bs)])
|
2019-06-05 20:20:09 +00:00
|
|
|
(for/or ([x (in-list xs)])
|
|
|
|
(match-define (equiv _ xb) x)
|
|
|
|
(equal? b xb))))
|
|
|
|
(define all-matches
|
|
|
|
(for*/list ([a (in-set as)]
|
2019-03-26 01:01:22 +00:00
|
|
|
[b (in-set bs)])
|
2019-06-05 20:20:09 +00:00
|
|
|
(equiv a b)))
|
|
|
|
(define combo-size (max (set-count as) (set-count bs)))
|
|
|
|
(for/set ([l-o-m (in-combinations all-matches combo-size)]
|
|
|
|
#:when (all-as? l-o-m)
|
|
|
|
#:when (all-bs? l-o-m))
|
|
|
|
(list->set l-o-m)))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"potential combinations bug"
|
|
|
|
;; confirmed bug
|
|
|
|
(define dests1 (set (set 'A)))
|
|
|
|
(define dests2 (set (set 'B) (set 'C)))
|
|
|
|
(check-equal? (make-combinations dests1 dests2)
|
|
|
|
(set (set (equiv (set 'A) (set 'B))
|
|
|
|
(equiv (set 'A) (set 'C))))))
|
|
|
|
(test-case
|
|
|
|
"potential combinations bug"
|
|
|
|
(define dests1 (set (set 'B) (set 'C)))
|
|
|
|
(define dests2 (set (set 'A)))
|
|
|
|
(check-equal? (make-combinations dests1 dests2)
|
|
|
|
(set (set (equiv (set 'B) (set 'A))
|
|
|
|
(equiv (set 'C) (set 'A))))))
|
|
|
|
(test-case
|
|
|
|
"another combinations bug"
|
|
|
|
;; returning matches with 3 elements
|
|
|
|
(define dests1 (set (set 'A) (set 'L)))
|
|
|
|
(define dests2 (set (set 'A) (set 'L)))
|
|
|
|
(check-equal? (make-combinations dests1 dests2)
|
|
|
|
(set
|
|
|
|
(set (equiv (set 'L) (set 'A)) (equiv (set 'A) (set 'L)))
|
|
|
|
(set (equiv (set 'L) (set 'L)) (equiv (set 'A) (set 'A)))))))
|
2019-03-26 01:01:22 +00:00
|
|
|
|
|
|
|
;; Role Role -> Bool
|
|
|
|
;; determine if the first role acts suitably like the second role.
|
|
|
|
;; at all times, it is asserting a superset of the second's assertions
|
|
|
|
;; role1 ~ actual
|
|
|
|
;; role2 ~ spec
|
|
|
|
(define (simulates? role1 role2)
|
2019-06-07 21:14:40 +00:00
|
|
|
(define rg1 (compile role1))
|
|
|
|
(define rg2 (compile role2))
|
2020-05-29 19:18:18 +00:00
|
|
|
(simulates?/rg rg1 rg2))
|
2019-06-07 21:14:40 +00:00
|
|
|
|
|
|
|
;; RoleGraph Role RoleGraph Role -> Bool
|
|
|
|
;; determine if the first role acts suitably like the second role.
|
|
|
|
;; at all times, it is asserting a superset of the second's assertions
|
|
|
|
;; rg1 ~ actual
|
|
|
|
;; rg2 ~ spec
|
|
|
|
;; like simulates?, but take in and use the compiled role graph; the role1 and
|
|
|
|
;; role2 arguments are just for determining the assertions in each state
|
|
|
|
;; useful when checking subgraphs
|
2020-05-29 19:18:18 +00:00
|
|
|
(define (simulates?/rg rg1 rg2)
|
2019-06-07 21:14:40 +00:00
|
|
|
(match-define (role-graph st0-1 st#1) rg1)
|
|
|
|
(match-define (role-graph st0-2 st#2) rg2)
|
2020-05-29 19:18:18 +00:00
|
|
|
|
2019-03-26 01:01:22 +00:00
|
|
|
;; Goal (Setof Equation) -> Bool
|
2019-06-05 20:20:09 +00:00
|
|
|
(define not-equiv (mutable-set))
|
2019-03-26 01:01:22 +00:00
|
|
|
(define (verify goal assumptions)
|
2019-06-05 20:20:09 +00:00
|
|
|
(let/ec esc
|
|
|
|
(define (return ans)
|
|
|
|
(when (and (equiv? goal)
|
|
|
|
(not ans))
|
|
|
|
(set-add! not-equiv goal))
|
|
|
|
(esc ans))
|
2019-03-26 01:01:22 +00:00
|
|
|
(match goal
|
|
|
|
[(equiv sn1 sn2)
|
|
|
|
(when (set-member? assumptions goal)
|
|
|
|
(return #t))
|
2019-06-05 20:20:09 +00:00
|
|
|
(when (set-member? not-equiv goal)
|
|
|
|
(esc #f))
|
2020-05-29 19:18:18 +00:00
|
|
|
(match-define (state _ transitions1 assertions1) (hash-ref st#1 sn1))
|
|
|
|
(match-define (state _ transitions2 assertions2) (hash-ref st#2 sn2))
|
2019-06-06 17:49:59 +00:00
|
|
|
(unless (assertion-superset? assertions1 assertions2)
|
2019-03-26 01:01:22 +00:00
|
|
|
(return #f))
|
2019-06-06 18:13:13 +00:00
|
|
|
(define (verify/with-current-assumed g)
|
|
|
|
(verify g (set-add assumptions goal)))
|
|
|
|
(unless (same-on-specified-events? transitions1
|
|
|
|
transitions2
|
2020-12-21 16:07:29 +00:00
|
|
|
sn1
|
2019-06-06 18:13:13 +00:00
|
|
|
verify/with-current-assumed)
|
2019-03-26 01:01:22 +00:00
|
|
|
(return #f))
|
2019-06-06 18:13:13 +00:00
|
|
|
(return (same-on-extra-events? transitions1
|
|
|
|
transitions2
|
|
|
|
sn2
|
|
|
|
verify/with-current-assumed))]
|
2019-03-26 01:01:22 +00:00
|
|
|
[(one-of matchings)
|
|
|
|
(for/or ([matching (in-set matchings)])
|
2019-06-17 21:15:08 +00:00
|
|
|
(define matching-hypos
|
|
|
|
(for/set ([eq (in-set matching)])
|
|
|
|
(match-define (equiv t1 t2) eq)
|
|
|
|
(equiv (transition-dest t1) (transition-dest t2))))
|
2019-03-26 01:01:22 +00:00
|
|
|
(for/and ([goal (in-set matching)])
|
2019-06-17 21:15:08 +00:00
|
|
|
(match-define (equiv (transition effs1 dest1)
|
|
|
|
(transition effs2 dest2)) goal)
|
|
|
|
(cond
|
|
|
|
[(effects-subsequence? effs2 effs1)
|
|
|
|
(define local-goal (equiv dest1 dest2))
|
|
|
|
(define hypotheses
|
|
|
|
(set-remove matching-hypos local-goal))
|
|
|
|
(verify local-goal (set-union hypotheses assumptions))]
|
|
|
|
[else
|
|
|
|
#f])
|
|
|
|
))])))
|
2019-06-06 17:49:59 +00:00
|
|
|
(verify (equiv st0-1 st0-2) (set)))
|
2019-06-05 20:20:09 +00:00
|
|
|
|
2020-12-01 22:34:32 +00:00
|
|
|
;; Role Role -> Bool
|
|
|
|
(define (simulates?/report-error impl spec)
|
2021-01-28 16:26:11 +00:00
|
|
|
(define impl-rg (compile/internal-events (compile impl)))
|
|
|
|
(define spec-rg (compile/internal-events (compile spec)))
|
2020-12-01 22:34:32 +00:00
|
|
|
(cond
|
2021-01-28 16:26:11 +00:00
|
|
|
[(detected-cycle? impl-rg)
|
|
|
|
(printf "Detected Cycle in Implementation!\n")
|
|
|
|
(describe-detected-cycle impl-rg)
|
|
|
|
#f]
|
|
|
|
[(detected-cycle? spec-rg)
|
|
|
|
(printf "Detected Cycle in Specification!\n")
|
|
|
|
(describe-detected-cycle spec-rg)
|
|
|
|
#f]
|
2020-12-01 22:34:32 +00:00
|
|
|
[(simulates?/rg impl-rg spec-rg)
|
|
|
|
#t]
|
|
|
|
[else
|
|
|
|
(define trace (find-simulation-counterexample impl-rg spec-rg))
|
|
|
|
(print-failing-trace trace impl-rg spec-rg)
|
|
|
|
#f]))
|
|
|
|
|
2021-01-28 16:26:11 +00:00
|
|
|
;; DetectedCycle -> Void
|
|
|
|
(define (describe-detected-cycle dc)
|
|
|
|
(printf "Initial State: ~a\n" (detected-cycle-start dc))
|
|
|
|
(for ([step (in-list (detected-cycle-steps dc))])
|
|
|
|
(printf " :: ~a ==> ~a\n" (D->label (traversal-step-evt step)) (traversal-step-dest step))))
|
|
|
|
|
2020-11-30 22:44:02 +00:00
|
|
|
;; a FailingTrace is a (failing-trace (Listof Transition) (Listof Transition) (Listof TraceStep))
|
|
|
|
(struct failing-trace (impl-path spec-path steps) #:transparent)
|
|
|
|
|
|
|
|
;; a TraceStep is one of
|
|
|
|
;; - (both-step D)
|
|
|
|
;; - (impl-step D)
|
2020-12-21 16:07:29 +00:00
|
|
|
;; - (spec-step D)
|
2020-11-30 22:44:02 +00:00
|
|
|
;; describing either both the spec and the implementation responding to an
|
2020-12-21 16:07:29 +00:00
|
|
|
;; event, only the implementation, or only the spec
|
2020-11-30 22:44:02 +00:00
|
|
|
(struct both-step (evt) #:transparent)
|
|
|
|
(struct impl-step (evt) #:transparent)
|
2020-12-21 16:07:29 +00:00
|
|
|
(struct spec-step (evt) #:transparent)
|
2020-11-30 22:44:02 +00:00
|
|
|
|
2020-12-01 22:34:32 +00:00
|
|
|
;; FailingTrace RoleGraph RoleGraph -> Void
|
2020-11-30 22:44:02 +00:00
|
|
|
(define (print-failing-trace trace impl-rg spec-rg)
|
|
|
|
(match-define (role-graph _ impl-st#) impl-rg)
|
|
|
|
(match-define (role-graph _ spec-st#) spec-rg)
|
|
|
|
(match-define (failing-trace impl-path spec-path steps) trace)
|
|
|
|
(define SEP (make-string 40 #\;))
|
|
|
|
(define (print-sep)
|
|
|
|
(newline)
|
2020-12-01 22:34:32 +00:00
|
|
|
(displayln SEP)
|
2020-11-30 22:44:02 +00:00
|
|
|
(newline))
|
|
|
|
(let loop ([steps steps]
|
|
|
|
[impl-path impl-path]
|
|
|
|
[spec-path spec-path]
|
2020-12-21 16:07:29 +00:00
|
|
|
;; because the path might end with an impl-step or spec-step, remember the last
|
|
|
|
;; states we've seen so we can print its assertions at the right time
|
|
|
|
[last-spec-state (transition-dest (car spec-path))]
|
|
|
|
[last-impl-state (transition-dest (car impl-path))])
|
|
|
|
(define (get-spec-dest)
|
|
|
|
(transition-dest (car spec-path)))
|
|
|
|
(define (get-impl-dest)
|
|
|
|
(transition-dest (car impl-path)))
|
2020-11-30 22:44:02 +00:00
|
|
|
(match steps
|
|
|
|
[(cons step more-steps)
|
|
|
|
(print-sep)
|
|
|
|
(printf "In response to event:\n")
|
|
|
|
(match step
|
|
|
|
[(or (both-step D)
|
2020-12-21 16:07:29 +00:00
|
|
|
(impl-step D)
|
|
|
|
(spec-step D))
|
2020-11-30 22:44:02 +00:00
|
|
|
(pretty-print D)])
|
2020-12-21 16:07:29 +00:00
|
|
|
(when (or (both-step? step) (impl-step? step))
|
|
|
|
(define impl-effs (transition-effs (car impl-path)))
|
|
|
|
(printf "Implementation steps to state:\n")
|
|
|
|
(pretty-print (get-impl-dest))
|
|
|
|
(unless (empty? impl-effs)
|
|
|
|
(printf "With Effects:\n")
|
|
|
|
(pretty-print impl-effs)))
|
2020-11-30 22:44:02 +00:00
|
|
|
(when (empty? more-steps)
|
2020-12-21 16:07:29 +00:00
|
|
|
(define impl-final (if (spec-step? step) last-impl-state (get-impl-dest)))
|
2020-11-30 22:44:02 +00:00
|
|
|
(printf "Implementation Assertions:\n")
|
2020-12-21 16:07:29 +00:00
|
|
|
(pretty-print (state-assertions (hash-ref impl-st# impl-final))))
|
|
|
|
(when (or (both-step? step) (spec-step? step))
|
|
|
|
(define spec-effs (transition-effs (car spec-path)))
|
2020-11-30 22:44:02 +00:00
|
|
|
(printf "Specification steps to state:\n")
|
2020-12-21 16:07:29 +00:00
|
|
|
(pretty-print (get-spec-dest))
|
2020-12-01 22:34:32 +00:00
|
|
|
(unless (empty? spec-effs)
|
|
|
|
(printf "With Effects:\n")
|
|
|
|
(pretty-print spec-effs)))
|
2020-11-30 22:44:02 +00:00
|
|
|
(when (empty? more-steps)
|
2020-12-21 16:07:29 +00:00
|
|
|
(define spec-final (if (impl-step? step) last-spec-state (get-spec-dest)))
|
2020-11-30 22:44:02 +00:00
|
|
|
(printf "Specification Assertions:\n")
|
2020-12-21 16:07:29 +00:00
|
|
|
(pretty-print (state-assertions (hash-ref spec-st# spec-final))))
|
2020-11-30 22:44:02 +00:00
|
|
|
(loop more-steps
|
2020-12-21 16:07:29 +00:00
|
|
|
(if (spec-step? step) impl-path (cdr impl-path))
|
|
|
|
(if (impl-step? step) spec-path (cdr spec-path))
|
|
|
|
(if (impl-step? step) last-spec-state (get-spec-dest))
|
|
|
|
(if (spec-step? step) last-impl-state (get-impl-dest)))]
|
2020-11-30 22:44:02 +00:00
|
|
|
[_
|
2020-12-01 22:34:32 +00:00
|
|
|
(newline)
|
2020-11-30 22:44:02 +00:00
|
|
|
(void)])))
|
|
|
|
|
|
|
|
;; RoleGraph RoleGraph -> Trace
|
|
|
|
;; assuming impl-rg does not simulate spec-rg, find a trace of transitions
|
|
|
|
;; (event + effects + destination assertions) demonstrating different behaviors
|
|
|
|
(define (find-simulation-counterexample impl-rg spec-rg)
|
|
|
|
(match-define (role-graph impl-st0 impl-st#) impl-rg)
|
|
|
|
(match-define (role-graph spec-st0 spec-st#) spec-rg)
|
|
|
|
;; inside loop, the each trace field is in reverse
|
|
|
|
(let loop ([work (list (failing-trace (list (transition '() impl-st0))
|
|
|
|
(list (transition '() spec-st0))
|
|
|
|
(list (both-step StartEvt))))]
|
|
|
|
#;[visited (set)])
|
|
|
|
(match work
|
|
|
|
[(cons (failing-trace impl-path/rev spec-path/rev steps/rev) more-work)
|
|
|
|
(match-define (transition impl-effs impl-dest) (car impl-path/rev))
|
|
|
|
(match-define (transition spec-effs spec-dest) (car spec-path/rev))
|
|
|
|
(define last-step (car steps/rev))
|
|
|
|
(cond
|
|
|
|
[(or (impl-step? last-step)
|
|
|
|
;; when only the implementation steps, no need to compare effects on transitions
|
2020-12-21 16:07:29 +00:00
|
|
|
(and (spec-step? last-step) (empty? spec-effs))
|
2020-11-30 22:44:02 +00:00
|
|
|
(effects-subsequence? spec-effs impl-effs))
|
|
|
|
;; cascading conds will help with development and isolating where things go wrong
|
|
|
|
(match-define (state _ impl-transition# impl-assertions) (hash-ref impl-st# impl-dest))
|
|
|
|
(match-define (state _ spec-transition# spec-assertions) (hash-ref spec-st# spec-dest))
|
|
|
|
(cond
|
|
|
|
;; n.b. internal events should be compiled away by now or this wouldn't work
|
|
|
|
[(assertion-superset? impl-assertions spec-assertions)
|
|
|
|
;; same effects and same assertions, compare transitions
|
|
|
|
;; TODO: similarity to `same-on-specified-events?`
|
|
|
|
(define spec-matching-txns
|
|
|
|
(for*/list ([(spec-D spec-txns) (in-hash spec-transition#)]
|
|
|
|
[(impl-D impl-txns) (in-hash impl-transition#)]
|
|
|
|
#:when (D<:? spec-D impl-D)
|
|
|
|
[spec-txn (in-set spec-txns)]
|
|
|
|
[impl-txn (in-set impl-txns)])
|
|
|
|
(failing-trace (cons impl-txn impl-path/rev)
|
|
|
|
(cons spec-txn spec-path/rev)
|
|
|
|
(cons (both-step spec-D) steps/rev))))
|
2020-12-21 16:07:29 +00:00
|
|
|
;; transitions that the spec has but the implementation doesn't respond to
|
|
|
|
;; TODO: similarity to `same-on-extra-events?`
|
|
|
|
(define impl-evts (hash-keys impl-transition#))
|
|
|
|
(define spec-extra-txns
|
|
|
|
(for*/list ([(spec-D spec-txns) (in-hash spec-transition#)]
|
|
|
|
;; TODO - this more or less assumes that *any* event matching impl-D also matches spec-evt, which I'm not sure is quite right
|
|
|
|
#:unless (for/or ([impl-evt (in-list impl-evts)])
|
|
|
|
(D<:? impl-evt spec-D))
|
|
|
|
[spec-txn (in-set spec-txns)])
|
|
|
|
(failing-trace impl-path/rev
|
|
|
|
(cons spec-txn spec-path/rev)
|
|
|
|
(cons (spec-step spec-D) steps/rev))))
|
|
|
|
;; TODO: similarity to above code
|
2020-11-30 22:44:02 +00:00
|
|
|
;; transitions that the implementation has that the spec doesn't respond to
|
|
|
|
(define spec-evts (hash-keys spec-transition#))
|
|
|
|
(define impl-extra-txns
|
|
|
|
(for*/list ([(impl-D impl-txns) (in-hash impl-transition#)]
|
|
|
|
;; TODO - this more or less assumes that *any* event matching impl-D also matches spec-evt, which I'm not sure is quite right
|
|
|
|
#:unless (for/or ([spec-evt (in-list spec-evts)])
|
|
|
|
(D<:? spec-evt impl-D))
|
|
|
|
[impl-txn (in-set impl-txns)])
|
|
|
|
(failing-trace (cons impl-txn impl-path/rev)
|
|
|
|
spec-path/rev
|
|
|
|
(cons (impl-step impl-D) steps/rev))))
|
2020-12-21 16:07:29 +00:00
|
|
|
(loop (append more-work spec-matching-txns spec-extra-txns impl-extra-txns))]
|
2020-11-30 22:44:02 +00:00
|
|
|
[else
|
|
|
|
;; states have different assertions
|
|
|
|
(failing-trace (reverse impl-path/rev) (reverse spec-path/rev) (reverse steps/rev))])]
|
|
|
|
[else
|
|
|
|
;; transitions have different effects
|
|
|
|
(failing-trace (reverse impl-path/rev) (reverse spec-path/rev) (reverse steps/rev))])]
|
|
|
|
[_
|
|
|
|
(error "ran out of work")])))
|
|
|
|
|
2019-06-05 20:20:09 +00:00
|
|
|
;; (List Role) -> (Hashof RoleName (Setof τ))
|
|
|
|
;; map each role's name to the assertions it contributes
|
|
|
|
(define (all-roles-assertions roles)
|
|
|
|
(for/hash ([role (in-list roles)])
|
|
|
|
(values (Role-nm role)
|
|
|
|
(role-assertions role))))
|
2019-03-26 01:01:22 +00:00
|
|
|
|
2019-06-06 17:49:59 +00:00
|
|
|
;; (Setof τ) (Setof τ) -> Bool
|
|
|
|
;; is as1 a superset of as2?
|
|
|
|
(define (assertion-superset? as1 as2)
|
|
|
|
(for/and ([assertion2 (in-set as2)])
|
|
|
|
(for/or ([assertion1 (in-set as1)])
|
|
|
|
(<:? assertion2 assertion1))))
|
|
|
|
|
2019-06-17 21:15:08 +00:00
|
|
|
;; (Hashof D (Setof Transition))
|
|
|
|
;; (Hashof D (Setof Transition))
|
2019-06-06 18:13:13 +00:00
|
|
|
;; (Goal -> Bool) -> Bool
|
|
|
|
;; Determine if:
|
|
|
|
;; for each event D going from sn2,
|
|
|
|
;; for each event E, D <: E, going from sn1,
|
|
|
|
;; (with the exception of the Dataflow HACK below)
|
|
|
|
;; for the set of states X connected to sn2 by D,
|
|
|
|
;; for the set of states Y connected to sn1 by E,
|
|
|
|
;; it is possible to pair the states of X and Y such that they are in simulation,
|
|
|
|
;; as determined by the verify procedure
|
2019-06-17 21:15:08 +00:00
|
|
|
;; and the effects on the edge going to Y are a supersequence of the effects
|
|
|
|
;; on the edge to Y
|
2020-12-21 16:07:29 +00:00
|
|
|
;; and:
|
|
|
|
;; Determine if the events in transitions2 that don't have any match in transitions1, are:
|
|
|
|
;; - all effect free
|
|
|
|
;; - verify with sn1 matched to each destination
|
|
|
|
(define (same-on-specified-events? transitions1 transitions2 sn1 verify)
|
2019-06-17 21:15:08 +00:00
|
|
|
(for/and ([(D2 edges2) (in-hash transitions2)])
|
|
|
|
(define edges1
|
2019-06-06 18:13:13 +00:00
|
|
|
(for/fold ([agg (set)])
|
2019-06-17 21:15:08 +00:00
|
|
|
([(D1 txns1) (in-hash transitions1)]
|
2019-06-06 18:13:13 +00:00
|
|
|
#:when (D<:? D2 D1)
|
|
|
|
;; only consider dataflow events vs. non-dataflow when
|
|
|
|
;; there is not a dataflow edge in the spec (HACK)
|
|
|
|
#:unless (and (equal? D1 DataflowEvt)
|
|
|
|
(not (equal? D2 DataflowEvt))
|
|
|
|
(hash-has-key? transitions2 D1)))
|
2019-06-17 21:15:08 +00:00
|
|
|
(set-union agg txns1)))
|
2019-06-06 18:13:13 +00:00
|
|
|
(cond
|
2019-06-17 21:15:08 +00:00
|
|
|
[(set-empty? edges1)
|
2020-12-21 16:07:29 +00:00
|
|
|
;; - I think this is right, as long as the current state of the implementation
|
|
|
|
;; matches all states the spec steps to --- unless there are effects on the transition
|
|
|
|
(for/and ([txn (in-set edges2)])
|
|
|
|
(and (empty? (transition-effs txn))
|
|
|
|
(verify (equiv sn1 (transition-dest txn)))))]
|
2019-06-06 18:13:13 +00:00
|
|
|
[else
|
2019-06-17 21:15:08 +00:00
|
|
|
(define combos (make-combinations edges1 edges2))
|
2019-06-06 18:13:13 +00:00
|
|
|
(verify (one-of combos))])))
|
|
|
|
|
2019-06-17 21:15:08 +00:00
|
|
|
;; (Listof TransitionEffect) (Listof TransitionEffect) -> Bool
|
|
|
|
;; determine if actual includes (supertypes of) the effects of spec in the same
|
|
|
|
;; order
|
|
|
|
(define (effects-subsequence? spec actual)
|
|
|
|
(match spec
|
|
|
|
['()
|
|
|
|
#t]
|
|
|
|
[(cons eff1 more-spec)
|
|
|
|
(match actual
|
|
|
|
['()
|
|
|
|
#f]
|
|
|
|
[(cons eff2 more-actual)
|
|
|
|
(if (eff<:? eff1 eff2)
|
|
|
|
(effects-subsequence? more-spec more-actual)
|
|
|
|
(effects-subsequence? spec more-actual))])]))
|
|
|
|
|
|
|
|
;; (Hashof D (Setof Transition))
|
|
|
|
;; (Hashof D (Setof Transition))
|
2019-06-06 18:13:13 +00:00
|
|
|
;; StateName
|
|
|
|
;; (Goal -> Bool) -> Bool
|
|
|
|
;; Determine if:
|
|
|
|
;; for each event E, going from sn1,
|
|
|
|
;; such that for each event D going from sn2, ¬ D <: E,
|
|
|
|
;; for the set of states X connected to sn1 by E,
|
|
|
|
;; each state in X is equivalent to sn2,
|
|
|
|
;; as determined by the verify procedure
|
|
|
|
(define (same-on-extra-events? transitions1 transitions2 sn2 verify)
|
|
|
|
(define evts1 (hash-keys transitions1))
|
|
|
|
(define evts2 (hash-keys transitions2))
|
|
|
|
(define extra-evts
|
|
|
|
(for/set ([evt1 (in-list evts1)]
|
|
|
|
#:unless (for/or ([evt2 (in-list evts2)])
|
|
|
|
(D<:? evt2 evt1)))
|
|
|
|
evt1))
|
|
|
|
(for*/and ([evt (in-set extra-evts)]
|
2019-06-17 21:15:08 +00:00
|
|
|
[txn (in-set (hash-ref transitions1 evt))])
|
|
|
|
(verify (equiv (transition-dest txn) sn2))))
|
2019-06-06 18:13:13 +00:00
|
|
|
|
2019-03-26 01:01:22 +00:00
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"simplest simul"
|
|
|
|
(define r (Role 'x (list)))
|
2019-03-26 01:05:55 +00:00
|
|
|
(check-true (simulates? r r)))
|
2019-03-26 14:05:09 +00:00
|
|
|
(test-case
|
|
|
|
"identity simulation"
|
|
|
|
(check-true (simulates? manager manager))
|
|
|
|
(check-true (simulates? client client))
|
|
|
|
(check-true (simulates? seller seller)))
|
|
|
|
(test-case
|
|
|
|
"simulation isn't vacuous"
|
|
|
|
(check-false (simulates? manager client))
|
|
|
|
(check-false (simulates? client manager))
|
|
|
|
(check-false (simulates? manager seller))
|
|
|
|
(check-false (simulates? seller manager))
|
|
|
|
(check-false (simulates? client seller))
|
|
|
|
(check-false (simulates? seller client)))
|
2019-03-26 01:05:55 +00:00
|
|
|
(test-case
|
|
|
|
"leader-spec identity simulation"
|
2019-03-27 18:02:25 +00:00
|
|
|
(check-true (simulates? leader-spec leader-spec)))
|
|
|
|
(test-case
|
|
|
|
"things aren't quite right with leader-actual"
|
|
|
|
(check-false (simulates? leader-actual leader-spec))
|
2019-03-28 18:55:48 +00:00
|
|
|
(check-true (simulates? leader-fixed? leader-spec)))
|
|
|
|
(test-case
|
|
|
|
"things aren't quite right with leader-revised"
|
2019-03-29 20:12:46 +00:00
|
|
|
(check-false (simulates? leader-revised leader-spec)))
|
|
|
|
(test-case
|
|
|
|
"things aren't quite right with member role"
|
|
|
|
(check-false (simulates? member-actual member-spec))
|
|
|
|
(define member-actual/revised
|
|
|
|
(Role
|
|
|
|
'member41
|
|
|
|
(list
|
|
|
|
(Shares (club-member String))
|
|
|
|
(Reacts
|
2019-06-13 12:34:34 +00:00
|
|
|
(Asserted (Observe (book-interest String ⋆ ⋆)))
|
2019-03-29 20:12:46 +00:00
|
|
|
(Role
|
|
|
|
'during-inner42
|
|
|
|
(list
|
|
|
|
(Shares (book-interest String String Bool))
|
|
|
|
(Reacts
|
2019-06-13 12:34:34 +00:00
|
|
|
(Retracted (Observe (book-interest String ⋆ ⋆)))
|
2019-03-29 20:12:46 +00:00
|
|
|
;; removed (Stop 'during-inner42 '()) here
|
|
|
|
'())))))))
|
|
|
|
(check-true (simulates? member-actual/revised member-spec)))
|
|
|
|
(test-case
|
|
|
|
"things aren't quite right with seller role"
|
|
|
|
(check-false (simulates? seller-actual seller))
|
|
|
|
(define seller-spec/revised
|
|
|
|
(Role 'seller
|
|
|
|
;; change body to a During
|
|
|
|
(list
|
|
|
|
(During (Observe (book-quote String ⋆))
|
|
|
|
(list (Shares (book-quote String Int)))))))
|
|
|
|
(check-true (simulates? seller-actual seller-spec/revised))))
|
2019-03-26 01:01:22 +00:00
|
|
|
|
2019-06-07 21:14:40 +00:00
|
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
;; SubGraphs
|
|
|
|
|
2019-06-10 17:59:19 +00:00
|
|
|
;; Role Role -> (Listof RoleGraph)
|
|
|
|
;; Find all subgraphs of the implementation role that simulate the spec role
|
|
|
|
(define (simulating-subgraphs impl spec)
|
2020-01-07 16:52:02 +00:00
|
|
|
;; assume spec doesn't have any internal events
|
2019-06-10 17:59:19 +00:00
|
|
|
(define spec-rg (compile spec))
|
2021-01-06 17:08:13 +00:00
|
|
|
(define impl-rg (compile/internal-events (compile impl)))
|
2019-06-10 17:59:19 +00:00
|
|
|
(define evts (relevant-events spec-rg))
|
|
|
|
(for/list ([srg (subgraphs impl-rg evts)]
|
2020-05-29 19:18:18 +00:00
|
|
|
#:when (simulates?/rg srg spec-rg))
|
2019-06-10 17:59:19 +00:00
|
|
|
srg))
|
|
|
|
|
2020-11-30 22:47:53 +00:00
|
|
|
;; Role Role -> (Maybe RoleGraph)
|
|
|
|
;; try to find any subgraph of the implementation simulating the spec
|
|
|
|
;; TODO: would be nice to find the largest
|
|
|
|
(define (find-simulating-subgraph impl spec)
|
|
|
|
(define spec-rg (compile spec))
|
2021-01-06 17:08:13 +00:00
|
|
|
(define impl-rg (compile/internal-events (compile impl)))
|
2020-12-21 16:07:29 +00:00
|
|
|
(find-simulating-subgraph/rg impl-rg spec-rg))
|
2020-12-01 22:34:32 +00:00
|
|
|
|
|
|
|
;; RoleGraph RoleGraph -> (Maybe RoleGraph)
|
|
|
|
(define (find-simulating-subgraph/rg impl-rg spec-rg)
|
2020-11-30 22:47:53 +00:00
|
|
|
(define evts (relevant-events spec-rg))
|
|
|
|
(for/first ([srg (subgraphs impl-rg evts)]
|
2020-12-01 22:34:32 +00:00
|
|
|
#:when (simulates?/rg srg spec-rg))
|
2020-11-30 22:47:53 +00:00
|
|
|
srg))
|
|
|
|
|
2020-12-01 22:34:32 +00:00
|
|
|
;; Role Role -> Bool
|
|
|
|
(define (find-simulating-subgraph/report-error impl spec)
|
|
|
|
(define spec-rg (compile spec))
|
2021-01-06 17:08:13 +00:00
|
|
|
(define impl-rg (compile/internal-events (compile impl)))
|
2020-12-01 22:34:32 +00:00
|
|
|
(define ans (find-simulating-subgraph/rg impl-rg spec-rg))
|
|
|
|
(cond
|
|
|
|
[ans
|
|
|
|
#t]
|
|
|
|
[else
|
2020-12-08 15:46:53 +00:00
|
|
|
(define-values (ft sg) (find-largest-simulating-subgraph-counterexample impl-rg spec-rg))
|
2020-12-01 22:34:32 +00:00
|
|
|
(print-failing-trace ft impl-rg spec-rg)
|
|
|
|
#f]))
|
|
|
|
|
|
|
|
;; RoleGraph RoleGraph -> (Values FailingTrace RoleGraph)
|
|
|
|
;; assuming impl does not have any simulating subgraphs of spec
|
|
|
|
;; largest *trace*, not largest subgraph
|
|
|
|
(define (find-largest-simulating-subgraph-counterexample impl-rg spec-rg)
|
|
|
|
(define evts (relevant-events spec-rg))
|
|
|
|
(define-values (trace len rg)
|
|
|
|
(for/fold ([best-trace #f]
|
|
|
|
[best-length 0]
|
|
|
|
[best-subgraph #f])
|
|
|
|
([srg (subgraphs impl-rg evts)])
|
|
|
|
(define ft (find-simulation-counterexample srg spec-rg))
|
|
|
|
(define len (failing-trace-length ft))
|
|
|
|
;; thing >= will prefer larger graphs
|
|
|
|
(if (>= len best-length)
|
|
|
|
(values ft len srg)
|
|
|
|
(values best-trace best-length best-subgraph))))
|
|
|
|
(values trace rg))
|
|
|
|
|
|
|
|
;; FailingTrace -> Int
|
|
|
|
(define (failing-trace-length ft)
|
|
|
|
(length (failing-trace-steps ft)))
|
|
|
|
|
2019-06-10 17:59:19 +00:00
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"task manager has task performer subgraphs"
|
|
|
|
(define tpr (parse-T task-performer-spec))
|
|
|
|
(define tmr (parse-T task-manager-ty))
|
|
|
|
(define ans (simulating-subgraphs tmr tpr))
|
2020-12-21 16:07:29 +00:00
|
|
|
(check-equal? (length ans) 340)
|
2019-06-10 17:59:19 +00:00
|
|
|
(define tprg (compile tpr))
|
2020-05-29 19:18:18 +00:00
|
|
|
(check-true (simulates?/rg (first ans) tprg))
|
|
|
|
(check-true (simulates?/rg (second ans) tprg))))
|
2019-06-10 17:59:19 +00:00
|
|
|
|
2019-06-07 21:14:40 +00:00
|
|
|
;; RoleGraph (Setof τ) -> (Sequenceof RoleGraph)
|
|
|
|
;; generate non-empty subgraphs, where at least the given assertions are enabled
|
|
|
|
(define (subgraphs rg as)
|
|
|
|
(match-define (role-graph _ state#) rg)
|
|
|
|
;; (Setof (U τ DataflowEvt))
|
|
|
|
(define all-events
|
|
|
|
(for*/set ([st (in-hash-values state#)]
|
|
|
|
[txn# (in-value (state-transitions st))]
|
|
|
|
[D (in-hash-keys txn#)])
|
|
|
|
(match D
|
2019-06-17 15:26:00 +00:00
|
|
|
;; TODO - might not make as much sense w/ internal events
|
|
|
|
[(or (Asserted τ)
|
|
|
|
(Retracted τ)
|
|
|
|
(Message τ)
|
|
|
|
(Know τ)
|
|
|
|
(Forget τ)
|
|
|
|
(Realize τ))
|
2019-06-07 21:14:40 +00:00
|
|
|
τ]
|
|
|
|
[_ D])))
|
|
|
|
(in-generator
|
2019-06-10 17:59:19 +00:00
|
|
|
(define cache (mutable-set))
|
|
|
|
(for* ([states* (in-combinations (hash-keys state#))]
|
|
|
|
[events* (in-combinations (set->list all-events))]
|
|
|
|
[event-set (in-value (list->set events*))]
|
|
|
|
#:when (assertion-superset? (set-remove event-set DataflowEvt) as))
|
|
|
|
(define states (list->set states*))
|
|
|
|
(define (event-enabled? D)
|
2019-06-17 15:26:00 +00:00
|
|
|
;; TODO - include internal events
|
2019-06-10 17:59:19 +00:00
|
|
|
(for/or ([e (in-set event-set)])
|
|
|
|
(or (equal? DataflowEvt e)
|
2019-06-13 12:34:34 +00:00
|
|
|
(D<:? D (Asserted e))
|
|
|
|
(D<:? D (Retracted e)))))
|
2019-06-10 17:59:19 +00:00
|
|
|
(define st#
|
|
|
|
(for/hash ([st (in-list states*)])
|
2020-05-29 19:18:18 +00:00
|
|
|
(match-define (state _ orig-txn# assertions) (hash-ref state# st))
|
2020-12-21 16:07:29 +00:00
|
|
|
(define (enabled-txns D)
|
|
|
|
(define orig-txns (hash-ref orig-txn# D))
|
|
|
|
(for/set ([txn (in-set orig-txns)]
|
|
|
|
#:when (set-member? states (transition-dest txn)))
|
|
|
|
txn))
|
2019-06-10 17:59:19 +00:00
|
|
|
(define txn#
|
2020-12-21 16:07:29 +00:00
|
|
|
(for*/hash ([D (in-hash-keys orig-txn#)]
|
|
|
|
#:when (event-enabled? D)
|
|
|
|
[new-txns (in-value (enabled-txns D))]
|
|
|
|
#:unless (set-empty? new-txns))
|
2019-06-17 21:15:08 +00:00
|
|
|
(values D new-txns)))
|
2020-05-29 19:18:18 +00:00
|
|
|
(values st (state st txn# assertions))))
|
2019-06-10 17:59:19 +00:00
|
|
|
(for ([st0 (in-list states*)])
|
|
|
|
(define rg (role-graph st0 st#))
|
|
|
|
(unless (set-member? cache rg)
|
|
|
|
(define reachable (reachable-states rg))
|
|
|
|
(define all-inc?
|
|
|
|
(for/and ([st (in-set states)])
|
|
|
|
(set-member? reachable st)))
|
|
|
|
(when all-inc?
|
|
|
|
(yield rg))
|
|
|
|
(set-add! cache rg))))))
|
|
|
|
|
|
|
|
;; RoleGraph -> (Setof StateName)
|
|
|
|
;; Determine the set of states reachable from the starting state
|
|
|
|
(define (reachable-states rg)
|
|
|
|
(match-define (role-graph st0 state#) rg)
|
|
|
|
(let search ([work (list st0)]
|
|
|
|
[seen (set)])
|
|
|
|
(match work
|
|
|
|
['() seen]
|
|
|
|
[(cons current more)
|
2020-05-29 19:18:18 +00:00
|
|
|
(match-define (state name txn# _) (hash-ref state# current))
|
2019-06-10 17:59:19 +00:00
|
|
|
(cond
|
|
|
|
[(set-member? seen name)
|
|
|
|
(search more seen)]
|
|
|
|
[else
|
|
|
|
(define connections
|
2019-06-17 21:15:08 +00:00
|
|
|
(for*/list ([txn* (in-hash-values txn#)]
|
|
|
|
[txn (in-set txn*)])
|
|
|
|
(transition-dest txn)))
|
2019-06-10 17:59:19 +00:00
|
|
|
(search (append more connections)
|
|
|
|
(set-add seen name))])])))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"reachable states"
|
|
|
|
(define rg
|
|
|
|
(role-graph (set 'X 'Y 'Z)
|
2020-05-29 19:18:18 +00:00
|
|
|
(hash (set 'X 'Y 'Z) (state (set 'X 'Y 'Z)
|
|
|
|
(hash (Asserted Int) (set (transition '() (set 'X 'Y 'Z)))
|
|
|
|
(Retracted Int) (set (transition '() (set 'X 'Y))))
|
|
|
|
(set))
|
|
|
|
(set 'X) (state (set 'X) '#hash() (set))
|
|
|
|
(set 'X 'Y) (state (set 'X 'Y)
|
|
|
|
(hash (Asserted Int) (set (transition '() (set 'X 'Y 'Z))))
|
|
|
|
(set)))))
|
2019-06-10 17:59:19 +00:00
|
|
|
(define reachable (reachable-states rg))
|
|
|
|
(check-true (set-member? reachable (set 'X 'Y 'Z)))
|
|
|
|
(check-true (set-member? reachable (set 'X 'Y)))
|
|
|
|
(check-false (set-member? reachable (set 'X))))
|
|
|
|
(test-case
|
|
|
|
"struct seems to make a difference?"
|
|
|
|
(define rg
|
|
|
|
(role-graph
|
|
|
|
(set 'during-inner2 'during-inner1 'tm)
|
|
|
|
(hash
|
|
|
|
(set 'during-inner2 'during-inner1 'tm)
|
|
|
|
(state
|
|
|
|
(set 'during-inner2 'during-inner1 'tm)
|
|
|
|
(hash
|
2019-06-13 12:34:34 +00:00
|
|
|
(Asserted (Struct 'TaskAssignment (list)))
|
2019-06-17 21:15:08 +00:00
|
|
|
(set (transition '() (set 'during-inner2 'during-inner1 'tm)))
|
2019-06-13 12:34:34 +00:00
|
|
|
(Retracted (Struct 'TaskAssignment (list)))
|
2020-05-29 19:18:18 +00:00
|
|
|
(set (transition '() (set 'during-inner1 'tm))))
|
|
|
|
(set))
|
2019-06-10 17:59:19 +00:00
|
|
|
(set 'tm)
|
2020-05-29 19:18:18 +00:00
|
|
|
(state (set 'tm) '#hash() (set))
|
2019-06-10 17:59:19 +00:00
|
|
|
(set 'during-inner1 'tm)
|
|
|
|
(state
|
|
|
|
(set 'during-inner1 'tm)
|
|
|
|
(hash
|
2019-06-13 12:34:34 +00:00
|
|
|
(Asserted (Struct 'TaskAssignment (list)))
|
2020-05-29 19:18:18 +00:00
|
|
|
(set (transition '() (set 'during-inner2 'during-inner1 'tm))))
|
|
|
|
(set)))))
|
2019-06-10 17:59:19 +00:00
|
|
|
(define reachable (reachable-states rg))
|
|
|
|
(check-true (set-member? reachable (set 'during-inner2 'during-inner1 'tm)))
|
|
|
|
(check-true (set-member? reachable (set 'during-inner1 'tm)))
|
|
|
|
(check-false (set-member? reachable (set 'tm)))))
|
|
|
|
|
|
|
|
;; RoleGraph -> (Setof (U τ DataflowEvt))
|
|
|
|
;; extract the assertions that cause transitions, and dataflow events if they
|
|
|
|
;; occur
|
|
|
|
(define (relevant-events rg)
|
|
|
|
(match-define (role-graph _ state#) rg)
|
|
|
|
(for*/set ([st (in-hash-values state#)]
|
|
|
|
[txn# (in-value (state-transitions st))]
|
2019-06-17 15:26:00 +00:00
|
|
|
[D (in-hash-keys txn#)]
|
|
|
|
#:when (external-evt? D))
|
2019-06-10 17:59:19 +00:00
|
|
|
(match D
|
2019-06-13 12:34:34 +00:00
|
|
|
[(or (Asserted τ) (Retracted τ))
|
2019-06-10 17:59:19 +00:00
|
|
|
τ]
|
|
|
|
[_ D])))
|
2019-06-07 21:14:40 +00:00
|
|
|
|
2019-03-19 20:44:10 +00:00
|
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
;; Visualization
|
|
|
|
|
2019-06-17 21:15:08 +00:00
|
|
|
;; TODO - for now, assume there are no names that need escaping
|
|
|
|
|
|
|
|
;; RoleGraph -> DotString
|
|
|
|
;; name is an optional string
|
|
|
|
;; translate the states to DOT that can be passed to graphviz
|
|
|
|
(define (render rg
|
|
|
|
#:name [name #f])
|
|
|
|
(match-define (role-graph st0 st#) rg)
|
|
|
|
(define graph-name (or name "Roles"))
|
|
|
|
(define entry-node (format "~a [style=bold];" (state-name->dot-name st0)))
|
|
|
|
(define edges
|
|
|
|
(for/list ([(sn st) (in-hash st#)])
|
|
|
|
(define dot-name (state-name->dot-name sn))
|
|
|
|
(define txns (state-transitions st))
|
|
|
|
(define dot-edges
|
|
|
|
(for*/list ([(D targets) (in-hash txns)]
|
|
|
|
[target (in-set targets)])
|
|
|
|
(render-edge dot-name D target)))
|
|
|
|
(string-join dot-edges "\n")))
|
|
|
|
(string-join (cons entry-node edges)
|
|
|
|
"\n"
|
|
|
|
#:before-first (format "digraph ~a {\n" graph-name)
|
|
|
|
#:after-last "\n}"))
|
|
|
|
|
|
|
|
;; RoleGraph PathString -> DotString
|
|
|
|
;; Like render but write the output to a file
|
|
|
|
(define (render-to-file rg dest
|
|
|
|
#:name [name #f])
|
|
|
|
(with-output-to-file dest
|
|
|
|
(lambda () (write-string (render rg #:name name)))
|
|
|
|
#:exists 'replace))
|
|
|
|
|
|
|
|
;; StateName -> String
|
|
|
|
(define (state-name->dot-name sn)
|
|
|
|
(define nms
|
|
|
|
(for/list ([nm (in-set sn)])
|
|
|
|
(~a nm)))
|
|
|
|
(string-join nms ","
|
|
|
|
#:before-first "\"{"
|
|
|
|
#:after-last "}\""))
|
|
|
|
|
|
|
|
;; String D Transition -> DotString
|
|
|
|
;; describe an edge between the states with the corresponding label
|
|
|
|
(define (render-edge from evt txn)
|
|
|
|
(match-define (transition effs to) txn)
|
|
|
|
(define target-dot (state-name->dot-name to))
|
|
|
|
(define evt-label (D->label evt))
|
|
|
|
(define edge-label
|
|
|
|
;; TODO - better presentation of effects
|
|
|
|
(if (empty? effs)
|
|
|
|
evt-label
|
|
|
|
(string-append evt-label "[...]")))
|
|
|
|
(format "~a -> ~a [label=\"~a\"];" from target-dot edge-label))
|
|
|
|
|
|
|
|
;; D -> DotString
|
|
|
|
;; give a description of an event suitable for rendering
|
|
|
|
(define (D->label evt)
|
|
|
|
(match evt
|
|
|
|
[(Asserted τ)
|
|
|
|
(string-append "+" (τ->string τ))]
|
|
|
|
[(Retracted τ)
|
|
|
|
(string-append "-" (τ->string τ))]
|
|
|
|
[(Message τ)
|
|
|
|
(string-append "!" (τ->string τ))]
|
|
|
|
[(Know τ)
|
|
|
|
(string-append "~+" (τ->string τ))]
|
|
|
|
[(Forget τ)
|
|
|
|
(string-append "~-" (τ->string τ))]
|
|
|
|
[(Realize τ)
|
|
|
|
(string-append "~!" (τ->string τ))]
|
2019-06-26 14:09:00 +00:00
|
|
|
[(StartOf fn)
|
|
|
|
(format "(Started ~a)" fn)]
|
|
|
|
[(StopOf fn)
|
|
|
|
(format "(Stopped ~a)" fn)]
|
2019-06-17 21:15:08 +00:00
|
|
|
[(== StartEvt)
|
|
|
|
"Start"]
|
|
|
|
[(== StopEvt)
|
|
|
|
"Stop"]
|
|
|
|
[(== DataflowEvt)
|
|
|
|
"Dataflow"]))
|
|
|
|
|
|
|
|
;; τ -> String
|
|
|
|
(define (τ->string τ)
|
|
|
|
;; (Listof String) -> String
|
|
|
|
(define (paren-join xs)
|
|
|
|
(string-join xs
|
|
|
|
#:before-first "("
|
|
|
|
#:after-last ")"))
|
|
|
|
(match τ
|
|
|
|
[(Base name)
|
|
|
|
(symbol->string name)]
|
|
|
|
[(== ⋆) "⋆"]
|
|
|
|
[(Observe τ2)
|
|
|
|
(string-append "?" (τ->string τ2))]
|
|
|
|
[(List τ2)
|
|
|
|
(τ->string (Struct 'List (list τ2)))]
|
|
|
|
[(Set τ2)
|
|
|
|
(τ->string (Struct 'Set (list τ2)))]
|
|
|
|
[(Hash τk τv)
|
|
|
|
(τ->string (Struct 'Hash (list τk τv)))]
|
|
|
|
[(Struct nm τs)
|
|
|
|
(define slots (map τ->string τs))
|
|
|
|
(paren-join (cons (~a nm) slots))]
|
|
|
|
[(U τs)
|
|
|
|
(define slots (map τ->string τs))
|
2019-07-30 20:03:19 +00:00
|
|
|
(paren-join (cons "U" slots))]
|
|
|
|
[(internal-label _ ty)
|
|
|
|
(τ->string ty)]))
|
2019-05-29 17:40:55 +00:00
|
|
|
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
;; Converting types from the turnstile implementation
|
|
|
|
|
|
|
|
;; QuotedType -> T
|
|
|
|
(define (parse-T ty)
|
|
|
|
(match ty
|
|
|
|
[(list 'Role (list name) eps ...)
|
|
|
|
(define parsed-eps (map parse-EP eps))
|
|
|
|
(Role name parsed-eps)]
|
|
|
|
[(list 'Spawn t)
|
|
|
|
(Spawn (parse-T t))]
|
2019-06-17 21:15:08 +00:00
|
|
|
[(list 'Sends t)
|
|
|
|
(Sends (parse-τ t))]
|
2019-06-18 14:26:04 +00:00
|
|
|
[(list 'Realizes t)
|
|
|
|
(Realizes (parse-τ t))]
|
2019-05-29 17:40:55 +00:00
|
|
|
[(list 'Stop name body ...)
|
2020-05-29 15:15:07 +00:00
|
|
|
(define bdy (cond [(empty? body) body]
|
|
|
|
[(= (length body) 1) (first body)]
|
|
|
|
[else (cons 'Effs body)]))
|
2019-05-29 17:40:55 +00:00
|
|
|
(Stop name (parse-Body bdy))]
|
|
|
|
))
|
|
|
|
|
|
|
|
;; Sexp -> EP
|
|
|
|
(define (parse-EP ep)
|
|
|
|
(match ep
|
|
|
|
[(list 'Shares ty)
|
|
|
|
(define parsed-ty (parse-τ ty))
|
|
|
|
(Shares parsed-ty)]
|
2019-06-18 14:26:04 +00:00
|
|
|
[(list 'Know ty)
|
|
|
|
(define parsed-ty (parse-τ ty))
|
|
|
|
(Know parsed-ty)]
|
2019-05-29 17:40:55 +00:00
|
|
|
[(list 'Reacts D b ...)
|
|
|
|
(define bdy (if (= (length b) 1)
|
|
|
|
(first b)
|
|
|
|
(cons 'Effs b)))
|
|
|
|
(Reacts (parse-D D) (parse-Body bdy))]))
|
|
|
|
|
|
|
|
(define (parse-Body b)
|
|
|
|
(match b
|
|
|
|
[(list 'Branch bs ...)
|
|
|
|
(Branch (map parse-Body bs))]
|
|
|
|
[(list 'Effs bs ...)
|
|
|
|
(list (map parse-Body bs))]
|
|
|
|
[(list)
|
|
|
|
(list)]
|
|
|
|
[_
|
|
|
|
(parse-T b)]))
|
|
|
|
|
|
|
|
(define (parse-D d)
|
|
|
|
(match d
|
2019-06-13 12:34:34 +00:00
|
|
|
[(list 'Asserted t)
|
|
|
|
(Asserted (parse-τ t))]
|
|
|
|
[(list 'Retracted t)
|
|
|
|
(Retracted (parse-τ t))]
|
2019-06-17 15:26:00 +00:00
|
|
|
[(list 'Message t)
|
|
|
|
(Message (parse-τ t))]
|
|
|
|
[(list 'Know t)
|
|
|
|
(Know (parse-τ t))]
|
|
|
|
[(list 'Forget t)
|
|
|
|
(Forget (parse-τ t))]
|
|
|
|
[(list 'Realize t)
|
|
|
|
(Realize (parse-τ t))]
|
2019-05-30 17:20:51 +00:00
|
|
|
['OnStart
|
|
|
|
StartEvt]
|
|
|
|
['OnStop
|
2019-06-05 20:20:09 +00:00
|
|
|
StopEvt]
|
|
|
|
['OnDataflow
|
|
|
|
DataflowEvt]))
|
2019-05-29 17:40:55 +00:00
|
|
|
|
|
|
|
;; Sexp -> τ
|
|
|
|
(define (parse-τ ty)
|
|
|
|
(match ty
|
|
|
|
[(list 'Observe t)
|
|
|
|
(Observe (parse-τ t))]
|
2019-06-03 15:16:16 +00:00
|
|
|
[(list 'List t)
|
|
|
|
(List (parse-τ t))]
|
|
|
|
[(list 'Set t)
|
|
|
|
(Set (parse-τ t))]
|
|
|
|
[(list 'Hash t-k t-v)
|
|
|
|
(Hash (parse-τ t-k) (parse-τ t-v))]
|
2019-05-29 17:40:55 +00:00
|
|
|
['★/t
|
|
|
|
⋆]
|
|
|
|
[(list (or 'U* 'U) t ...)
|
|
|
|
(U (map parse-τ t))]
|
|
|
|
[(list 'Bind t)
|
|
|
|
;; TODO : questionable
|
2019-06-05 20:20:09 +00:00
|
|
|
⋆
|
|
|
|
#;(parse-τ t)]
|
2019-05-29 17:40:55 +00:00
|
|
|
['Discard
|
|
|
|
⋆]
|
|
|
|
[(list struct-name tys ...)
|
|
|
|
(Struct struct-name (map parse-τ tys))]
|
|
|
|
[(? symbol?)
|
|
|
|
(Base ty)])
|
|
|
|
)
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(check-equal? (parse-T '(Stop during-inner))
|
|
|
|
(Stop 'during-inner (list)))
|
|
|
|
(test-case
|
|
|
|
"real seller type"
|
|
|
|
(check-true (Role? (parse-T real-seller-ty))))
|
|
|
|
(test-case
|
|
|
|
"Stop with a single continuation effect"
|
|
|
|
(check-true (Stop? (parse-T '(Stop poll-members
|
|
|
|
(Branch (Effs (Stop get-quotes)) (Effs)))))))
|
|
|
|
(test-case
|
2019-06-05 20:20:09 +00:00
|
|
|
"parsed types are (not) the same as my manual conversions"
|
2019-06-06 17:49:59 +00:00
|
|
|
;; because I parse (Bind τ) as ⋆, whereas my manual conversions use τ thus
|
|
|
|
;; the "real" types are more specialized and implement the manual
|
|
|
|
;; conversions, but not vice versa
|
|
|
|
(check-true (simulates? (parse-T real-seller-ty) seller-actual))
|
2019-06-05 20:20:09 +00:00
|
|
|
(check-false (simulates? seller-actual (parse-T real-seller-ty)))
|
2019-05-29 17:40:55 +00:00
|
|
|
|
2019-06-06 17:49:59 +00:00
|
|
|
(check-true (simulates? (parse-T real-member-ty) member-actual))
|
2019-06-05 20:20:09 +00:00
|
|
|
(check-false (simulates? member-actual (parse-T real-member-ty)))
|
2019-05-29 17:40:55 +00:00
|
|
|
|
2019-06-06 17:49:59 +00:00
|
|
|
(check-true (simulates? (parse-T real-leader-ty) leader-actual))
|
2019-06-05 20:20:09 +00:00
|
|
|
(check-false (simulates? leader-actual (parse-T real-leader-ty)))
|
2019-06-06 17:49:59 +00:00
|
|
|
(check-true (simulates? (parse-T real-leader-ty) leader-revised))
|
2020-05-29 15:15:07 +00:00
|
|
|
(check-false (simulates? leader-revised (parse-T real-leader-ty))))
|
|
|
|
|
|
|
|
(test-case
|
|
|
|
"parse a Stop with two actions"
|
|
|
|
(define r '(Stop
|
|
|
|
assign-manager
|
|
|
|
(Role
|
|
|
|
(waiting-for-answer)
|
|
|
|
(Reacts OnStop)
|
|
|
|
)
|
|
|
|
(Role
|
|
|
|
(_)
|
|
|
|
(Know
|
|
|
|
(SlotAssignment (ReqID (Tuple Int Symbol) Symbol) Symbol)))))
|
|
|
|
(check-true (Stop? (parse-T r)))))
|
2019-05-29 17:40:55 +00:00
|
|
|
|
2019-06-17 15:29:55 +00:00
|
|
|
;; --------------------------------------------------------------------------
|
|
|
|
;; Examples, Book Club
|
|
|
|
|
|
|
|
(define manager
|
|
|
|
(Role 'account-manager
|
|
|
|
(list (Shares (Struct 'account (list Int)))
|
|
|
|
(Reacts (Asserted (Struct 'deposit '())) '()))))
|
|
|
|
(define client
|
|
|
|
(Role 'client
|
|
|
|
(list (Reacts (Asserted (Struct 'account (list Int))) '()))))
|
|
|
|
|
|
|
|
;; τ τ -> τ
|
|
|
|
;; short hand for creating a book quote struct type
|
|
|
|
(define (book-quote ty1 ty2)
|
|
|
|
(Struct 'BookQuoteT (list ty1 ty2)))
|
|
|
|
|
|
|
|
;; τ τ τ -> τ
|
|
|
|
;; short hand for creating a book quote interest type
|
|
|
|
(define (book-interest ty1 ty2 ty3)
|
|
|
|
(Struct 'BookInterestT (list ty1 ty2 ty3)))
|
|
|
|
|
|
|
|
;; τ -> τ
|
|
|
|
;; short hand for creating a book of the month type
|
|
|
|
(define (book-of-the-month ty)
|
|
|
|
(Struct 'BookOfTheMonthT (list ty)))
|
|
|
|
|
|
|
|
;; τ -> τ
|
|
|
|
;; short hand for creating a club member type
|
|
|
|
(define (club-member ty)
|
|
|
|
(Struct 'ClubMemberT (list ty)))
|
|
|
|
|
|
|
|
(define seller
|
|
|
|
(Role 'seller
|
|
|
|
(list
|
|
|
|
(Reacts (Asserted (Observe (book-quote String ⋆)))
|
|
|
|
(Role 'fulfill
|
|
|
|
(list (Shares (book-quote String Int))))))))
|
|
|
|
|
|
|
|
(define seller-actual
|
|
|
|
(Role
|
|
|
|
'seller27
|
|
|
|
(list
|
|
|
|
(Reacts
|
|
|
|
(Asserted (Observe (book-quote String ⋆)))
|
|
|
|
(Role
|
|
|
|
'during-inner29
|
|
|
|
(list
|
|
|
|
(Shares (book-quote String (U (list Int Int))))
|
|
|
|
(Reacts
|
|
|
|
(Retracted (Observe (book-quote String ⋆)))
|
|
|
|
(Stop 'during-inner29 '()))))))))
|
|
|
|
|
|
|
|
(define leader-spec
|
|
|
|
(Role 'leader
|
|
|
|
(list
|
|
|
|
(Reacts
|
|
|
|
(Asserted (book-quote String Int))
|
|
|
|
(Role 'poll
|
|
|
|
(list
|
|
|
|
(Reacts
|
|
|
|
(Asserted (book-interest String String Bool))
|
|
|
|
(Branch
|
|
|
|
(list
|
|
|
|
(Stop 'leader
|
|
|
|
(Role 'announce
|
|
|
|
(list
|
|
|
|
(Shares (book-of-the-month String)))))
|
|
|
|
(Stop 'poll (list)))))))))))
|
|
|
|
|
|
|
|
(define leader-actual
|
|
|
|
(Role
|
|
|
|
'get-quotes
|
|
|
|
(list
|
|
|
|
(Reacts
|
|
|
|
(Asserted (book-quote String Int))
|
|
|
|
(Branch
|
|
|
|
(list
|
|
|
|
;; problem 1: spec doesn't say actor can give up when running out of books
|
|
|
|
(Stop 'get-quotes '())
|
|
|
|
(Role
|
|
|
|
'poll-members
|
|
|
|
(list
|
|
|
|
(Reacts
|
|
|
|
(Asserted (book-interest String String ⋆))
|
|
|
|
(Branch (list
|
|
|
|
;; problem 2: combining poll-members and get-quotes here (should be another branch)
|
|
|
|
(Stop 'poll-members
|
|
|
|
(Stop 'get-quotes '()))
|
|
|
|
(Stop 'get-quotes
|
|
|
|
(Role 'announce
|
|
|
|
(list
|
|
|
|
(Shares (book-of-the-month String))))))))
|
|
|
|
(Reacts (Retracted (book-interest String String Bool)) (list))
|
|
|
|
(Reacts (Asserted (book-interest String String Bool)) (list))
|
|
|
|
(Reacts (Retracted (book-interest String String Bool)) (list))
|
|
|
|
(Reacts (Asserted (book-interest String String Bool)) (list)))))))
|
|
|
|
(Reacts (Retracted (club-member String)) (list))
|
|
|
|
(Reacts (Asserted (club-member String)) (list)))))
|
|
|
|
|
|
|
|
(define leader-fixed?
|
|
|
|
(Role 'get-quotes
|
|
|
|
(list
|
|
|
|
(Reacts (Asserted (book-quote String Int))
|
|
|
|
(Branch (list
|
|
|
|
(Role 'poll-members
|
|
|
|
(list
|
|
|
|
(Reacts (Asserted (book-interest String String ⋆))
|
|
|
|
(Branch (list
|
|
|
|
(Stop 'poll-members
|
|
|
|
'())
|
|
|
|
(Stop 'get-quotes
|
|
|
|
(Role 'announce
|
|
|
|
(list
|
|
|
|
(Shares (book-of-the-month String))))))))
|
|
|
|
(Reacts (Retracted (book-interest String String Bool)) (list))
|
|
|
|
(Reacts (Asserted (book-interest String String Bool)) (list))
|
|
|
|
(Reacts (Retracted (book-interest String String Bool)) (list))
|
|
|
|
(Reacts (Asserted (book-interest String String Bool)) (list)))))))
|
|
|
|
(Reacts (Retracted (club-member String)) (list))
|
|
|
|
(Reacts (Asserted (club-member String)) (list)))))
|
|
|
|
|
|
|
|
(define leader-revised
|
|
|
|
(Role
|
|
|
|
'get-quotes
|
|
|
|
(list
|
|
|
|
(Reacts
|
|
|
|
(Asserted (book-quote String Int))
|
|
|
|
(Branch
|
|
|
|
(list
|
|
|
|
(Branch (list (Stop 'get-quotes (list)) (list)))
|
|
|
|
(Role
|
|
|
|
'poll-members
|
|
|
|
(list
|
|
|
|
(Reacts
|
|
|
|
(Asserted (book-interest String String ⋆))
|
|
|
|
(list
|
|
|
|
(Branch
|
|
|
|
(list
|
|
|
|
(Stop 'poll-members
|
|
|
|
(Branch (list
|
|
|
|
(Stop 'get-quotes (list))
|
|
|
|
(list))))
|
|
|
|
(list)))
|
|
|
|
(Branch
|
|
|
|
(list
|
|
|
|
(Stop
|
|
|
|
'get-quotes
|
|
|
|
(Role 'announce (list (Shares (book-of-the-month String)))))
|
|
|
|
(list)))))
|
|
|
|
(Reacts (Retracted (book-interest String String Bool)) (list))
|
|
|
|
(Reacts (Asserted (book-interest String String Bool)) (list))
|
|
|
|
(Reacts (Retracted (book-interest String String Bool)) (list))
|
|
|
|
(Reacts (Asserted (book-interest String String Bool)) (list)))))))
|
|
|
|
(Reacts (Retracted (club-member String)) (list))
|
|
|
|
(Reacts (Asserted (club-member String)) (list)))))
|
|
|
|
|
|
|
|
(define member-spec
|
|
|
|
(Role
|
|
|
|
'member
|
|
|
|
(list
|
|
|
|
(Shares (club-member String))
|
|
|
|
(Reacts (Asserted (Observe (book-interest String ⋆ ⋆)))
|
|
|
|
(Role 'respond
|
|
|
|
(list
|
|
|
|
(Shares (book-interest String String Bool))))))))
|
|
|
|
|
|
|
|
(define member-actual
|
|
|
|
(Role
|
|
|
|
'member41
|
|
|
|
(list
|
|
|
|
(Shares (club-member String))
|
|
|
|
(Reacts
|
|
|
|
(Asserted (Observe (book-interest String ⋆ ⋆)))
|
|
|
|
(Role
|
|
|
|
'during-inner42
|
|
|
|
(list
|
|
|
|
(Shares (book-interest String String Bool))
|
|
|
|
(Reacts
|
|
|
|
(Retracted (Observe (book-interest String ⋆ ⋆)))
|
|
|
|
;; this bit is a noticeable deviation from the spec
|
|
|
|
(Stop 'during-inner42 '()))))))))
|
|
|
|
|
2019-05-29 17:40:55 +00:00
|
|
|
(define real-seller-ty
|
|
|
|
'(Role
|
|
|
|
(seller)
|
|
|
|
(Reacts
|
2019-06-13 12:34:34 +00:00
|
|
|
(Asserted (Observe (BookQuoteT (Bind String) Discard)))
|
2019-05-29 17:40:55 +00:00
|
|
|
(Role
|
|
|
|
(during-inner)
|
|
|
|
(Shares (BookQuoteT String Int))
|
|
|
|
(Reacts
|
2019-06-13 12:34:34 +00:00
|
|
|
(Retracted (Observe (BookQuoteT String Discard)))
|
2019-05-29 17:40:55 +00:00
|
|
|
(Stop during-inner))))))
|
|
|
|
|
|
|
|
(define real-member-ty
|
|
|
|
'(Role
|
|
|
|
(member)
|
|
|
|
(Shares (ClubMemberT String))
|
|
|
|
(Reacts
|
2019-06-13 12:34:34 +00:00
|
|
|
(Asserted (Observe (BookInterestT (Bind String) Discard Discard)))
|
2019-05-29 17:40:55 +00:00
|
|
|
(Role
|
|
|
|
(during-inner)
|
|
|
|
(Shares (BookInterestT String String Bool))
|
|
|
|
(Reacts
|
2019-06-13 12:34:34 +00:00
|
|
|
(Retracted (Observe (BookInterestT String Discard Discard)))
|
2019-05-29 17:40:55 +00:00
|
|
|
(Stop during-inner))))))
|
|
|
|
|
|
|
|
(define real-leader-ty
|
|
|
|
'(Role
|
|
|
|
(get-quotes)
|
|
|
|
(Reacts
|
2019-06-13 12:34:34 +00:00
|
|
|
(Asserted (BookQuoteT String (Bind Int)))
|
2019-05-29 17:40:55 +00:00
|
|
|
(Branch
|
|
|
|
(Effs (Branch (Effs (Stop get-quotes)) (Effs)))
|
|
|
|
(Effs
|
|
|
|
(Role
|
|
|
|
(poll-members)
|
|
|
|
(Reacts
|
2019-06-13 12:34:34 +00:00
|
|
|
(Asserted (BookInterestT String (Bind String) Discard))
|
2019-05-29 17:40:55 +00:00
|
|
|
(Branch
|
|
|
|
(Effs (Stop poll-members (Branch (Effs (Stop get-quotes)) (Effs))))
|
|
|
|
(Effs))
|
|
|
|
(Branch
|
|
|
|
(Effs
|
|
|
|
(Stop get-quotes (Role (announce) (Shares (BookOfTheMonthT String)))))
|
|
|
|
(Effs)))
|
2019-06-13 12:34:34 +00:00
|
|
|
(Reacts (Retracted (BookInterestT String (Bind String) Bool)))
|
|
|
|
(Reacts (Asserted (BookInterestT String (Bind String) Bool)))
|
|
|
|
(Reacts (Retracted (BookInterestT String (Bind String) Bool)))
|
|
|
|
(Reacts (Asserted (BookInterestT String (Bind String) Bool)))))))
|
|
|
|
(Reacts (Retracted (ClubMemberT (Bind String))))
|
|
|
|
(Reacts (Asserted (ClubMemberT (Bind String))))))
|
2019-06-05 20:20:09 +00:00
|
|
|
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
;; Flink Examples
|
|
|
|
|
2019-12-30 21:27:29 +00:00
|
|
|
(define task-assigner-spec
|
2019-06-05 20:20:09 +00:00
|
|
|
'(Role
|
2019-12-30 21:27:29 +00:00
|
|
|
(assign)
|
|
|
|
(Shares
|
|
|
|
(Observe
|
|
|
|
(TaskPerformance
|
|
|
|
Symbol
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U (MapWork String) (ReduceWork (Hash String Int) (Hash String Int))))
|
|
|
|
★/t)))
|
|
|
|
(Reacts
|
|
|
|
(Asserted
|
|
|
|
(TaskPerformance
|
|
|
|
Symbol
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U (MapWork String) (ReduceWork (Hash String Int) (Hash String Int))))
|
|
|
|
★/t))
|
|
|
|
(Branch (Stop assign) (Effs)))))
|
2019-06-05 20:20:09 +00:00
|
|
|
|
|
|
|
(module+ test
|
2019-12-30 21:27:29 +00:00
|
|
|
(test-case "parse and compile task-assigner-spec"
|
|
|
|
(check-true (Role? (parse-T task-assigner-spec)))
|
|
|
|
(check-true (role-graph? (compile (parse-T task-assigner-spec))))))
|
2019-06-06 17:49:59 +00:00
|
|
|
|
|
|
|
(define task-performer-spec
|
|
|
|
'(Role
|
|
|
|
(listen)
|
|
|
|
(Reacts
|
2019-06-13 12:34:34 +00:00
|
|
|
(Asserted
|
2019-12-30 22:13:29 +00:00
|
|
|
(Observe
|
|
|
|
(TaskPerformance
|
2019-12-30 21:27:29 +00:00
|
|
|
Symbol
|
2019-12-30 22:13:29 +00:00
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
2019-12-30 21:27:29 +00:00
|
|
|
(U
|
2019-12-30 22:13:29 +00:00
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int))))
|
2019-12-30 21:27:29 +00:00
|
|
|
★/t)))
|
2019-06-06 17:49:59 +00:00
|
|
|
(Role
|
|
|
|
(during-inner)
|
|
|
|
(Reacts
|
2019-06-13 12:34:34 +00:00
|
|
|
(Retracted
|
2019-12-30 22:13:29 +00:00
|
|
|
(Observe
|
|
|
|
(TaskPerformance
|
2019-12-30 21:27:29 +00:00
|
|
|
Symbol
|
2019-12-30 22:13:29 +00:00
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
2019-12-30 21:27:29 +00:00
|
|
|
(U
|
2019-12-30 22:13:29 +00:00
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int))))
|
2019-12-30 21:27:29 +00:00
|
|
|
★/t)))
|
2019-06-06 17:49:59 +00:00
|
|
|
(Stop during-inner))
|
|
|
|
(Shares
|
2019-12-30 22:13:29 +00:00
|
|
|
(TaskPerformance
|
2019-06-06 17:49:59 +00:00
|
|
|
Symbol
|
2019-12-30 22:13:29 +00:00
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
2019-12-30 21:27:29 +00:00
|
|
|
(U
|
2019-12-30 22:13:29 +00:00
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int))))
|
|
|
|
(U (Finished (Hash String Int)) Symbol)))))))
|
2019-06-06 17:49:59 +00:00
|
|
|
|
|
|
|
(module+ test
|
2019-12-30 21:27:29 +00:00
|
|
|
(test-case "parse and compile task-performer-spec"
|
|
|
|
(check-true (Role? (parse-T task-performer-spec)))
|
|
|
|
(check-true (role-graph? (compile (parse-T task-performer-spec))))))
|
|
|
|
|
|
|
|
|
|
|
|
(define job-manager-actual
|
2020-01-07 16:52:02 +00:00
|
|
|
'(Role
|
|
|
|
(jm)
|
|
|
|
(Shares (JobManagerAlive))
|
|
|
|
(Reacts
|
|
|
|
(Asserted
|
|
|
|
(Observe
|
|
|
|
(JobCompletion
|
|
|
|
(Bind Symbol)
|
|
|
|
(Bind
|
|
|
|
(List
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U (MapWork String) (ReduceWork Int Int)))))
|
|
|
|
Discard)))
|
|
|
|
(Role
|
|
|
|
(during-inner)
|
|
|
|
(Reacts
|
|
|
|
OnStart
|
2020-05-29 15:15:07 +00:00
|
|
|
(Realizes
|
|
|
|
(TaskIsReady
|
|
|
|
Symbol
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U
|
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int))))))
|
2020-01-07 16:52:02 +00:00
|
|
|
(Role
|
|
|
|
(delegate-tasks)
|
|
|
|
(Reacts
|
2020-05-29 15:15:07 +00:00
|
|
|
(Realize (TaskIsReady Symbol (Bind (U))))
|
2020-01-07 16:52:02 +00:00
|
|
|
(Role
|
|
|
|
(perform)
|
|
|
|
(Reacts
|
|
|
|
OnStart
|
|
|
|
(Role
|
|
|
|
(select)
|
|
|
|
(Reacts
|
2020-05-29 15:15:07 +00:00
|
|
|
(Know
|
|
|
|
(SlotAssignment (ReqID (Tuple Int Symbol) Symbol) (Bind Symbol)))
|
|
|
|
(Role
|
|
|
|
(assign)
|
|
|
|
(Reacts
|
|
|
|
(Asserted
|
|
|
|
(TaskPerformance
|
|
|
|
Symbol
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U
|
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int))))
|
|
|
|
(Bind (U (Finished (Hash String Int)) Symbol))))
|
2020-01-07 16:52:02 +00:00
|
|
|
(Branch
|
2020-05-29 15:15:07 +00:00
|
|
|
(Effs)
|
|
|
|
(Effs)
|
|
|
|
(Effs (Stop assign))
|
2020-01-07 16:52:02 +00:00
|
|
|
(Effs
|
2020-05-29 15:15:07 +00:00
|
|
|
(Stop
|
|
|
|
perform
|
|
|
|
(Branch
|
|
|
|
(Effs (Realizes (TasksFinished Symbol (Hash String Int))))
|
|
|
|
(Effs
|
|
|
|
(Branch
|
|
|
|
(Effs
|
|
|
|
(Realizes
|
|
|
|
(TaskIsReady
|
|
|
|
Symbol
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U
|
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int)))))))
|
|
|
|
(Effs))))))))
|
|
|
|
(Reacts (Retracted (TaskManager Symbol Discard)) (Stop assign))))))
|
2020-01-07 16:52:02 +00:00
|
|
|
(Reacts OnStop)
|
|
|
|
(Reacts OnStart)))
|
|
|
|
(Reacts
|
|
|
|
(Realize (TasksFinished Symbol (Bind (Hash String Int))))
|
|
|
|
(Stop
|
|
|
|
delegate-tasks
|
|
|
|
(Role
|
|
|
|
(done)
|
|
|
|
(Shares
|
|
|
|
(JobCompletion
|
|
|
|
Symbol
|
|
|
|
(List
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U (MapWork String) (ReduceWork Int Int))))
|
|
|
|
(Hash String Int))))))))
|
|
|
|
(Reacts
|
|
|
|
(Retracted
|
|
|
|
(Observe
|
|
|
|
(JobCompletion
|
|
|
|
Symbol
|
|
|
|
(List
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U (MapWork String) (ReduceWork Int Int))))
|
|
|
|
Discard)))
|
|
|
|
(Stop during-inner))))
|
2020-05-29 15:15:07 +00:00
|
|
|
(Reacts
|
|
|
|
OnStart
|
|
|
|
(Role
|
|
|
|
(slot-manager)
|
|
|
|
(Know (Slots Int))
|
|
|
|
(Reacts
|
|
|
|
(Know
|
|
|
|
(Observe
|
|
|
|
(SlotAssignment
|
|
|
|
(ReqID (Bind (Tuple Int Symbol)) (Bind Symbol))
|
|
|
|
Discard)))
|
|
|
|
(Role
|
|
|
|
(during-inner2)
|
|
|
|
(Reacts OnStop)
|
|
|
|
(Reacts
|
|
|
|
OnStart
|
|
|
|
(Role
|
|
|
|
(assign-manager)
|
|
|
|
(Reacts
|
|
|
|
(Know (Slots (Bind Int)))
|
|
|
|
(Branch
|
|
|
|
(Effs)
|
|
|
|
(Effs
|
|
|
|
(Branch
|
|
|
|
(Effs
|
|
|
|
(Stop
|
|
|
|
assign-manager
|
|
|
|
(Role
|
|
|
|
(waiting-for-answer)
|
|
|
|
(Reacts OnStop)
|
|
|
|
(Reacts
|
|
|
|
(Asserted
|
|
|
|
(Observe
|
|
|
|
(TaskPerformance
|
|
|
|
Symbol
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(Bind
|
|
|
|
(U
|
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int)))))
|
|
|
|
Discard)))
|
|
|
|
(Role
|
|
|
|
(_)
|
|
|
|
(Reacts
|
|
|
|
(Asserted
|
|
|
|
(TaskPerformance
|
|
|
|
Symbol
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U
|
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int))))
|
|
|
|
Discard))
|
|
|
|
(Stop waiting-for-answer)))))
|
|
|
|
(Role
|
|
|
|
(_)
|
|
|
|
(Know
|
|
|
|
(SlotAssignment (ReqID (Tuple Int Symbol) Symbol) Symbol)))))
|
|
|
|
(Effs)))))))
|
|
|
|
(Reacts
|
|
|
|
(Forget
|
|
|
|
(Observe
|
|
|
|
(SlotAssignment (ReqID (Tuple Int Symbol) Symbol) Discard)))
|
|
|
|
(Stop during-inner2))))
|
|
|
|
(Reacts (Retracted (TaskManager (Bind Symbol) (Bind Int))))
|
|
|
|
(Reacts (Asserted (TaskManager (Bind Symbol) (Bind Int))))))))
|
2019-12-30 21:27:29 +00:00
|
|
|
|
2020-01-07 16:52:02 +00:00
|
|
|
(module+ test
|
2019-12-30 21:27:29 +00:00
|
|
|
(test-case
|
|
|
|
"job manager reads and compiles"
|
2020-05-29 19:18:18 +00:00
|
|
|
(define jmr (run/timeout (thunk (parse-T job-manager-actual))))
|
2019-12-30 21:27:29 +00:00
|
|
|
(check-true (Role? jmr))
|
2020-11-30 22:49:03 +00:00
|
|
|
(define jm (run/timeout (thunk (compile jmr)) 5000))
|
2019-12-30 21:27:29 +00:00
|
|
|
(check-true (role-graph? jm))
|
2021-01-06 17:08:13 +00:00
|
|
|
(define jmi (run/timeout (thunk (compile/internal-events jm)) 5000))
|
2020-11-30 22:49:03 +00:00
|
|
|
(check-true (run/timeout (thunk (simulates?/rg jmi jmi)) 5000))))
|
2019-06-06 17:49:59 +00:00
|
|
|
|
|
|
|
(define task-runner-ty
|
|
|
|
'(Role
|
|
|
|
(runner)
|
2019-12-30 21:27:29 +00:00
|
|
|
(Shares (TaskRunner Symbol))
|
2019-06-06 17:49:59 +00:00
|
|
|
(Reacts
|
2019-06-13 12:34:34 +00:00
|
|
|
(Asserted
|
2019-12-30 21:27:29 +00:00
|
|
|
(Observe
|
|
|
|
(TaskPerformance
|
|
|
|
Symbol
|
2019-06-06 17:49:59 +00:00
|
|
|
(Bind
|
2019-12-30 21:27:29 +00:00
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U
|
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int)))))
|
|
|
|
Discard)))
|
2019-06-06 17:49:59 +00:00
|
|
|
(Role
|
|
|
|
(during-inner)
|
|
|
|
(Shares
|
2019-12-30 21:27:29 +00:00
|
|
|
(TaskPerformance
|
|
|
|
Symbol
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U
|
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int))))
|
|
|
|
(U (Finished (Hash String Int)) Symbol)))
|
2019-06-06 17:49:59 +00:00
|
|
|
(Reacts
|
2019-06-13 12:34:34 +00:00
|
|
|
(Retracted
|
2019-12-30 21:27:29 +00:00
|
|
|
(Observe
|
|
|
|
(TaskPerformance
|
|
|
|
Symbol
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U
|
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int))))
|
|
|
|
Discard)))
|
2019-06-06 17:49:59 +00:00
|
|
|
(Stop during-inner))))
|
2019-12-30 21:27:29 +00:00
|
|
|
(Reacts (Retracted (TaskManager Symbol Discard)) (Stop runner))))
|
2019-06-06 17:49:59 +00:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case "parse and compile task-runner-ty"
|
|
|
|
(check-true (Role? (parse-T task-runner-ty)))
|
2019-12-30 21:27:29 +00:00
|
|
|
(check-true (role-graph? (compile (parse-T task-runner-ty)))))
|
|
|
|
(test-case "task-runner subgraph(s) simulate task-performer"
|
|
|
|
(define tr (parse-T task-runner-ty))
|
|
|
|
(define tpr (parse-T task-performer-spec))
|
|
|
|
(define ans (run/timeout (thunk (simulating-subgraphs tr tpr))))
|
|
|
|
(check-true (list? ans))
|
|
|
|
(check-false (empty? ans))))
|
2019-06-07 13:46:29 +00:00
|
|
|
|
|
|
|
(define task-manager-ty
|
2019-12-31 18:55:59 +00:00
|
|
|
`(Role
|
|
|
|
(tm)
|
|
|
|
(Reacts
|
|
|
|
(Asserted (JobManagerAlive))
|
|
|
|
(Role
|
|
|
|
(during-inner2)
|
|
|
|
(Shares (TaskManager Symbol Int))
|
|
|
|
(Reacts
|
|
|
|
(Asserted
|
|
|
|
(Observe
|
|
|
|
(TaskPerformance
|
|
|
|
Symbol
|
|
|
|
(Bind
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U
|
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int)))))
|
|
|
|
Discard)))
|
|
|
|
(Role
|
|
|
|
(during-inner3)
|
|
|
|
(Shares
|
|
|
|
(TaskPerformance
|
|
|
|
Symbol
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U
|
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int))))
|
|
|
|
(U (Finished (Hash String Int)) Symbol)))
|
|
|
|
(Reacts
|
|
|
|
(Asserted
|
|
|
|
(TaskPerformance
|
|
|
|
Symbol
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U
|
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int))))
|
|
|
|
(Bind (U (Finished (Hash String Int)) Symbol)))))
|
|
|
|
(Reacts OnStop)
|
|
|
|
(Reacts
|
|
|
|
(Retracted
|
|
|
|
(Observe
|
|
|
|
(TaskPerformance
|
|
|
|
Symbol
|
|
|
|
(Task
|
|
|
|
(Tuple Int Symbol)
|
|
|
|
(U
|
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int))))
|
|
|
|
Discard)))
|
|
|
|
(Stop during-inner3))))
|
|
|
|
(Reacts OnDataflow)
|
|
|
|
(Reacts
|
|
|
|
OnStart
|
|
|
|
(Role
|
|
|
|
(monitor-task-runner)
|
|
|
|
(Reacts
|
|
|
|
(Retracted (TaskRunner Symbol))
|
|
|
|
(Spawn ,task-runner-ty))
|
|
|
|
(Reacts (Asserted (TaskRunner Symbol)))
|
|
|
|
(Reacts
|
|
|
|
OnStart
|
|
|
|
(Spawn ,task-runner-ty))))
|
|
|
|
(Reacts (Retracted (JobManagerAlive)) (Stop during-inner2))))))
|
2019-06-07 13:46:29 +00:00
|
|
|
|
2019-12-31 18:55:59 +00:00
|
|
|
(module+ test
|
2019-06-07 13:46:29 +00:00
|
|
|
(test-case "parse and compile task-manager-ty"
|
|
|
|
(check-true (Role? (parse-T task-manager-ty)))
|
|
|
|
(check-true (role-graph? (compile (parse-T task-manager-ty)))))
|
|
|
|
(test-case
|
|
|
|
"work needs to be done"
|
|
|
|
;; even though the task manager plays both the TaskPerformer and TaskAssigner roles,
|
|
|
|
;; it does so situationally, so shouldn't directly simulate either
|
|
|
|
(define tm (parse-T task-manager-ty))
|
|
|
|
(check-false (simulates? tm (parse-T task-assigner-spec)))
|
|
|
|
(check-false (simulates? tm (parse-T task-performer-spec)))))
|
2019-06-17 15:26:13 +00:00
|
|
|
|
2019-06-17 21:15:08 +00:00
|
|
|
|
2020-01-07 16:52:02 +00:00
|
|
|
(module+ test
|
2019-06-18 14:26:04 +00:00
|
|
|
(test-case
|
|
|
|
"job manager subgraph(s) implement task assigner"
|
2020-05-29 19:18:18 +00:00
|
|
|
(define jmr (run/timeout (thunk (parse-T job-manager-actual))))
|
2019-06-18 14:26:04 +00:00
|
|
|
(define tar (parse-T task-assigner-spec))
|
2021-01-28 16:26:11 +00:00
|
|
|
(define ans (run/timeout (thunk (simulating-subgraphs jmr tar)) 4000))
|
2019-06-18 14:26:04 +00:00
|
|
|
(check-true (list? ans))
|
|
|
|
(check-false (empty? ans))))
|
|
|
|
|
2019-12-30 21:27:29 +00:00
|
|
|
(module+ done-facet-dying-too-soon
|
|
|
|
;; has a bug with done facet dying too soon
|
|
|
|
(define job-manager-v2
|
|
|
|
'(Role
|
|
|
|
(jm)
|
|
|
|
(Shares (JobManagerAlive))
|
2019-06-21 20:48:49 +00:00
|
|
|
(Reacts
|
2019-12-30 21:27:29 +00:00
|
|
|
(Asserted
|
|
|
|
(Job
|
|
|
|
(Bind Symbol)
|
|
|
|
(Bind (List (Task Int (U (MapWork String) (ReduceWork Int Int)))))))
|
2019-06-21 20:48:49 +00:00
|
|
|
(Role
|
2019-12-30 21:27:29 +00:00
|
|
|
(during-inner)
|
2019-06-21 20:48:49 +00:00
|
|
|
(Reacts
|
2019-12-30 21:27:29 +00:00
|
|
|
OnStart
|
2019-06-21 20:48:49 +00:00
|
|
|
(Role
|
2019-12-30 21:27:29 +00:00
|
|
|
(delegate-tasks)
|
2019-06-21 20:48:49 +00:00
|
|
|
(Reacts
|
2019-12-30 21:27:29 +00:00
|
|
|
OnDataflow
|
2019-06-21 20:48:49 +00:00
|
|
|
(Role
|
2019-12-30 21:27:29 +00:00
|
|
|
(perform)
|
2019-06-21 20:48:49 +00:00
|
|
|
(Reacts
|
2019-12-30 21:27:29 +00:00
|
|
|
OnStart
|
|
|
|
(Role
|
|
|
|
(select)
|
|
|
|
(Reacts (Forget (SelectedTM (Bind Symbol))))
|
|
|
|
(Reacts
|
|
|
|
OnDataflow
|
2019-06-21 20:48:49 +00:00
|
|
|
(Branch
|
|
|
|
(Effs
|
2019-12-30 21:27:29 +00:00
|
|
|
(Branch
|
|
|
|
(Effs
|
2019-06-21 20:48:49 +00:00
|
|
|
(Role
|
2019-12-30 21:27:29 +00:00
|
|
|
(assign)
|
|
|
|
(Shares
|
|
|
|
(TaskAssignment
|
|
|
|
Symbol
|
|
|
|
Symbol
|
|
|
|
(Task
|
|
|
|
Int
|
|
|
|
(U
|
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int))))))
|
|
|
|
(Know (SelectedTM Symbol))
|
2019-06-21 20:48:49 +00:00
|
|
|
(Reacts
|
2019-12-30 21:27:29 +00:00
|
|
|
(Asserted
|
|
|
|
(TaskState
|
|
|
|
Symbol
|
|
|
|
Symbol
|
|
|
|
Int
|
|
|
|
(Bind (U (Finished (Hash String Int)) Symbol))))
|
|
|
|
(Branch
|
|
|
|
(Effs)
|
|
|
|
(Effs)
|
|
|
|
(Effs (Stop assign))
|
|
|
|
(Effs
|
|
|
|
(Stop
|
|
|
|
perform
|
|
|
|
(Branch
|
|
|
|
(Effs
|
|
|
|
(Role
|
|
|
|
(done)
|
|
|
|
(Shares (JobFinished Symbol (Hash String Int))))
|
|
|
|
(Realizes (TasksFinished Symbol)))
|
|
|
|
(Effs))))))
|
|
|
|
(Reacts
|
|
|
|
OnStart
|
|
|
|
(Role
|
|
|
|
(take-slot)
|
|
|
|
(Reacts
|
|
|
|
(Asserted (TaskState Symbol Symbol Int Discard))
|
|
|
|
(Stop take-slot))))
|
|
|
|
(Reacts
|
|
|
|
(Retracted (TaskManager Symbol Discard))
|
|
|
|
(Stop assign))))
|
|
|
|
(Effs)))
|
|
|
|
(Effs)))))
|
|
|
|
(Reacts OnStop)
|
|
|
|
(Reacts OnStart)))
|
|
|
|
(Reacts (Realize (TasksFinished Symbol)) (Stop delegate-tasks))))
|
2019-06-21 20:48:49 +00:00
|
|
|
(Reacts
|
2019-12-30 21:27:29 +00:00
|
|
|
(Retracted
|
|
|
|
(Job
|
|
|
|
Symbol
|
|
|
|
(List (Task Int (U (MapWork String) (ReduceWork Int Int))))))
|
|
|
|
(Stop during-inner))))
|
|
|
|
(Reacts (Retracted (TaskManager (Bind Symbol) (Bind Int))))
|
|
|
|
(Reacts (Asserted (TaskManager (Bind Symbol) (Bind Int))))))
|
|
|
|
|
|
|
|
;; fixed above bug
|
|
|
|
(define job-manager-v3
|
|
|
|
'(Role
|
|
|
|
(jm)
|
|
|
|
(Shares (JobManagerAlive))
|
2019-06-21 20:48:49 +00:00
|
|
|
(Reacts
|
2019-12-30 21:27:29 +00:00
|
|
|
(Asserted
|
2019-06-21 20:48:49 +00:00
|
|
|
(Job
|
2019-12-30 21:27:29 +00:00
|
|
|
(Bind Symbol)
|
|
|
|
(Bind (List (Task Int (U (MapWork String) (ReduceWork Int Int)))))))
|
|
|
|
(Role
|
|
|
|
(during-inner)
|
|
|
|
(Reacts
|
|
|
|
OnStart
|
|
|
|
(Role
|
|
|
|
(delegate-tasks)
|
|
|
|
(Reacts
|
|
|
|
OnDataflow
|
|
|
|
(Role
|
|
|
|
(perform)
|
|
|
|
(Reacts
|
|
|
|
OnStart
|
|
|
|
(Role
|
|
|
|
(select)
|
|
|
|
(Reacts (Forget (SelectedTM (Bind Symbol))))
|
|
|
|
(Reacts
|
|
|
|
OnDataflow
|
|
|
|
(Branch
|
|
|
|
(Effs
|
|
|
|
(Branch
|
|
|
|
(Effs
|
|
|
|
(Role
|
|
|
|
(assign)
|
|
|
|
(Shares
|
|
|
|
(TaskAssignment
|
|
|
|
Symbol
|
|
|
|
Symbol
|
|
|
|
(Task
|
|
|
|
Int
|
|
|
|
(U
|
|
|
|
(MapWork String)
|
|
|
|
(ReduceWork (Hash String Int) (Hash String Int))))))
|
|
|
|
(Know (SelectedTM Symbol))
|
|
|
|
(Reacts
|
|
|
|
(Asserted
|
|
|
|
(TaskState
|
|
|
|
Symbol
|
|
|
|
Symbol
|
|
|
|
Int
|
|
|
|
(Bind (U (Finished (Hash String Int)) Symbol))))
|
|
|
|
(Branch
|
|
|
|
(Effs)
|
|
|
|
(Effs)
|
|
|
|
(Effs (Stop assign))
|
|
|
|
(Effs
|
|
|
|
(Stop
|
|
|
|
perform
|
|
|
|
(Branch
|
|
|
|
(Effs
|
|
|
|
(Realizes (TasksFinished Symbol (Hash String Int))))
|
|
|
|
(Effs))))))
|
|
|
|
(Reacts
|
|
|
|
OnStart
|
|
|
|
(Role
|
|
|
|
(take-slot)
|
|
|
|
(Reacts
|
|
|
|
(Asserted (TaskState Symbol Symbol Int Discard))
|
|
|
|
(Stop take-slot))))
|
|
|
|
(Reacts
|
|
|
|
(Retracted (TaskManager Symbol Discard))
|
|
|
|
(Stop assign))))
|
|
|
|
(Effs)))
|
|
|
|
(Effs)))))
|
|
|
|
(Reacts OnStop)
|
|
|
|
(Reacts OnStart)))
|
|
|
|
(Reacts
|
|
|
|
(Realize (TasksFinished Symbol (Bind (Hash String Int))))
|
|
|
|
(Stop
|
|
|
|
delegate-tasks
|
|
|
|
(Role (done) (Shares (JobFinished Symbol (Hash String Int))))))))
|
|
|
|
(Reacts
|
|
|
|
(Retracted
|
|
|
|
(Job
|
|
|
|
Symbol
|
|
|
|
(List (Task Int (U (MapWork String) (ReduceWork Int Int))))))
|
|
|
|
(Stop during-inner))))
|
|
|
|
(Reacts (Retracted (TaskManager (Bind Symbol) (Bind Int))))
|
|
|
|
(Reacts (Asserted (TaskManager (Bind Symbol) (Bind Int)))))))
|
2019-06-26 14:09:00 +00:00
|
|
|
|
2019-06-17 21:15:08 +00:00
|
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
;; Message Examples/Tests
|
|
|
|
|
|
|
|
(define msgy-r1
|
|
|
|
'(Role (m)
|
|
|
|
(Reacts (Asserted Int)
|
|
|
|
(Sends String)
|
|
|
|
(Role (m2)
|
|
|
|
(Shares (x))))))
|
|
|
|
|
|
|
|
(define msgy-r2
|
|
|
|
'(Role (m)
|
|
|
|
(Reacts (Asserted Int)
|
|
|
|
(Role (m2)
|
|
|
|
(Shares (x))))))
|
|
|
|
|
|
|
|
(define msgy-spec
|
|
|
|
'(Role (n)
|
|
|
|
(Reacts (Asserted Int)
|
|
|
|
(Sends String)
|
|
|
|
(Role (n2)
|
|
|
|
(Shares (x))))))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"basic functionality of roles with messages"
|
|
|
|
(define mr1 (parse-T msgy-r1))
|
|
|
|
(check-true (Role? mr1))
|
|
|
|
(define mr2 (parse-T msgy-r2))
|
|
|
|
(check-true (Role? mr2))
|
|
|
|
(define mrs (parse-T msgy-spec))
|
|
|
|
(check-true (Role? mrs))
|
|
|
|
(define rg1 (compile mr1))
|
|
|
|
(check-true (role-graph? rg1))
|
|
|
|
(define rg2 (compile mr2))
|
|
|
|
(check-true (role-graph? rg2))
|
|
|
|
(define rgs (compile mrs))
|
|
|
|
(check-true (role-graph? rgs))
|
|
|
|
(check-true (simulates? mr1 mr1))
|
|
|
|
(check-true (simulates? mr2 mr2))
|
|
|
|
(check-true (simulates? mrs mrs))
|
|
|
|
(check-true (simulates? mr1 mrs))
|
|
|
|
(check-false (simulates? mr2 mrs))))
|
2020-05-29 15:15:07 +00:00
|
|
|
|
|
|
|
(module+ demo-leader-subgraph
|
|
|
|
(define leader
|
|
|
|
'(Role ; = react
|
|
|
|
(get-quotes)
|
|
|
|
(Reacts ; = on
|
|
|
|
(Asserted (BookQuoteT String (Bind Int)))
|
|
|
|
(Branch
|
|
|
|
(Effs (Branch (Effs (Stop get-quotes)) (Effs)))
|
|
|
|
(Effs
|
|
|
|
(Role
|
|
|
|
(poll-members)
|
|
|
|
(Reacts
|
|
|
|
(Asserted (BookInterestT String (Bind String) Discard))
|
|
|
|
(Branch
|
|
|
|
(Effs (Stop poll-members (Branch (Effs (Stop get-quotes)) (Effs))))
|
|
|
|
(Effs))
|
|
|
|
(Branch
|
|
|
|
(Effs
|
|
|
|
(Stop get-quotes (Role (announce) (Shares (BookOfTheMonthT String)))))
|
|
|
|
(Effs)))
|
|
|
|
(Reacts (Retracted (BookInterestT String (Bind String) Bool)))
|
|
|
|
(Reacts (Asserted (BookInterestT String (Bind String) Bool)))
|
|
|
|
(Reacts (Retracted (BookInterestT String (Bind String) Bool)))
|
|
|
|
(Reacts (Asserted (BookInterestT String (Bind String) Bool)))))))
|
|
|
|
(Reacts (Retracted (ClubMemberT (Bind String))))
|
|
|
|
(Reacts (Asserted (ClubMemberT (Bind String))))))
|
|
|
|
|
|
|
|
(define leader-impl (parse-T leader))
|
|
|
|
(define simulating (simulating-subgraphs leader-impl leader-spec))
|
|
|
|
(displayln (length simulating))
|
|
|
|
(define largest (argmax role-graph-size simulating))
|
|
|
|
(render-to-file largest "largest-simulating.dot")
|
|
|
|
)
|
|
|
|
|
|
|
|
(module+ demo-removing-internal-events
|
|
|
|
(define ty
|
|
|
|
'(Role (x)
|
|
|
|
(Reacts OnStart
|
|
|
|
(Role (y)
|
|
|
|
(Shares (Hi))
|
|
|
|
(Reacts (Asserted (Bye))
|
|
|
|
(Stop y))))))
|
|
|
|
(define r (parse-T ty))
|
|
|
|
(define rg (compile r))
|
2021-01-11 16:50:05 +00:00
|
|
|
(define rgi (compile/internal-events rg))
|
2020-05-29 15:15:07 +00:00
|
|
|
(render-to-file rg "before.dot")
|
|
|
|
(render-to-file rgi "after.dot")
|
|
|
|
)
|
2020-12-21 16:07:29 +00:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"regression: ok for implementation not to have edges if the current state matches"
|
|
|
|
(define a (role-graph
|
|
|
|
(set 'seller341 'during-inner343)
|
|
|
|
(hash
|
|
|
|
(set 'seller341 'during-inner343)
|
|
|
|
(state
|
|
|
|
(set 'seller341 'during-inner343)
|
|
|
|
'#hash()
|
|
|
|
(set
|
|
|
|
'#s(Observe #s(Observe #s(Struct BookQuoteT (#s(Base String) #s(Mk⋆)))))
|
|
|
|
'#s(Struct BookQuoteT (#s(Base String) #s(Base Int))))))))
|
|
|
|
(define b (role-graph
|
|
|
|
(set 'seller)
|
|
|
|
(hash
|
|
|
|
(set 'seller)
|
|
|
|
(state
|
|
|
|
(set 'seller)
|
|
|
|
(hash
|
|
|
|
'#s(Asserted #s(Observe #s(Struct BookQuoteT (#s(Base String) #s(Mk⋆)))))
|
|
|
|
(set (transition '() (set '_ 'seller))))
|
|
|
|
(set
|
|
|
|
'#s(Observe #s(Observe #s(Struct BookQuoteT (#s(Base String) #s(Mk⋆)))))))
|
|
|
|
(set '_ 'seller)
|
|
|
|
(state
|
|
|
|
(set '_ 'seller)
|
|
|
|
'#hash()
|
|
|
|
(set
|
|
|
|
'#s(Observe #s(Observe #s(Struct BookQuoteT (#s(Base String) #s(Mk⋆)))))
|
|
|
|
'#s(Struct BookQuoteT (#s(Base String) #s(Base Int))))))))
|
|
|
|
(check-true (run/timeout (thunk (simulates?/rg a b))))))
|