add messages to proto

This commit is contained in:
Sam Caldwell 2019-06-17 17:15:08 -04:00
parent fa8822e40d
commit 202bcd6842
1 changed files with 268 additions and 153 deletions

View File

@ -14,9 +14,11 @@
;; a T is one of
;; - (Role FacetName (Listof EP)), also abbreviated as just Role
;; - (Spawn τ)
;; - (Sends τ)
;; - (Stop FacetName Body)
(struct Role (nm eps) #:transparent)
(struct Spawn (ty) #:transparent)
(struct Sends (ty) #:transparent)
(struct Stop (nm body) #:transparent)
;; a EP is one of
@ -93,10 +95,15 @@
;; -----------------------------------------------------------------------------
;; Compiling Roles to state machines
;; a State is a (state StateName (Hashof D (Setof StateName)))
;; a State is a (state StateName (Hashof D (Setof Transition)))
;; where each D in the hash satisfies external-evt?
;; a StateName is a (Setof FacetName)
;; let's assume that all FacetNames are unique
;; a Transition is a (transition (Listof TransitionEffect) StateName)
(struct transition (effs dest) #:transparent)
;; a TransitionEffect is one of
;; - (send τ)
(struct send (ty) #:transparent)
;; ok, this is also ignoring Spawn actions for now, would show up in the transitions hash
(struct state (name transitions) #:transparent)
@ -131,14 +138,15 @@
(for/hash ([(D effs) (in-hash agg-txn)]
#:when (external-evt? D))
;; TODO - may want to remove self loops here
(define destinations
(define txns
(for*/set ([eff* (in-set effs)]
[dst (in-set (apply-effects eff* current ft roles#))])
dst))
(values D destinations)))
[txn (in-set (apply-effects eff* current ft roles#))])
txn))
(values D txns)))
(define new-work
(for*/list ([st-set (in-hash-values transitions)]
[st (in-set st-set)]
(for*/list ([txns (in-hash-values transitions)]
[txn (in-set txns)]
[st (in-value (transition-dest txn))]
#:unless (equal? st current)
#:unless (hash-has-key? states st))
st))
@ -179,7 +187,7 @@
(Observe (book-quote String )))
(check-true (hash-has-key? transitions (Asserted quote-request)))
(check-equal? (hash-ref transitions (Asserted quote-request))
(set (set 'seller 'fulfill))))
(set (transition '() (set 'seller 'fulfill)))))
(test-case
"compile role that quits"
(define r
@ -197,7 +205,7 @@
(define transitions (state-transitions state0))
(check-true (hash-has-key? transitions (Asserted Int)))
(check-equal? (hash-ref transitions (Asserted Int))
(set (set))))
(set (transition '() (set)))))
(test-case
"leader-revised should have a quote/poll cycle"
@ -212,8 +220,11 @@
(define bq (Asserted (book-quote String Int)))
(check-true (hash? gq-transitions))
(check-true (hash-has-key? gq-transitions bq))
(define dests (hash-ref gq-transitions bq))
(check-true (set? dests))
(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))
(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)))
@ -222,7 +233,10 @@
(define bi (Asserted (book-interest String String )))
(check-true (hash? gqpm-transitions))
(check-true (hash-has-key? gqpm-transitions bi))
(define dests2 (hash-ref gqpm-transitions bi))
(define txns2 (hash-ref gqpm-transitions bi))
(check-true (set? txns2))
(define dests2 (for/set ([t (in-set txns2)])
(transition-dest t)))
(check-true (set? dests2))
(check-true (set-member? dests2 (set 'get-quotes))))
@ -284,6 +298,8 @@
[(cons (cons parent T) rest)
(match T
;; TODO - handle Spawn
[(Sends _)
(loop rest downs ups)]
[(Role nm eps)
;; record the parent/child relationship
(define downs2 (hash-update downs
@ -427,6 +443,7 @@
;; a RoleEffect is one of
;; - (start RoleName)
;; - (stop RoleName)
;; - (send τ)
;; TODO - leaving out Spawn here
(struct start (nm) #:transparent)
(struct stop (nm) #:transparent)
@ -436,10 +453,11 @@
;; It always includes the keys StartEvt and StopEvt.
(define txn-desc0 (hash StartEvt (set) StopEvt (set)))
;; (Listof RoleEffect) StateName
;; (Listof RoleEffect)
;; StateName
;; FacetTree
;; (Hashof FacetName TransitionDesc)
;; -> (Setof StateName)
;; -> (Setof Transition)
;; 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#)
@ -447,9 +465,11 @@
[effs effs])
(match effs
['()
(set st)]
(set (transition '() st))]
[(cons eff rest)
(match eff
[(send _)
(set (transition (list eff) st))]
[(start nm)
(define st+ (set-add st nm))
(define start-effs (hash-ref (hash-ref txn# nm) StartEvt))
@ -461,25 +481,24 @@
[result (in-set (loop st+ (append rest eff*)))])
result)])]
[(stop nm)
;; better include nm
(define children (find-children ft nm st))
(define st-
(for/fold ([st st])
([c (in-list children)])
(set-remove st c)))
(for/fold ([sts (set st-)])
(for/fold ([txns (set (transition '() st-))])
([f-name (in-list children)])
(define stop-effs (hash-ref (hash-ref txn# f-name) StopEvt))
(cond
[(set-empty? stop-effs)
(for*/set ([st (in-set sts)]
[result (in-set (loop st rest))])
result)]
[else
(for*/set ([st (in-set sts)]
[effs* (in-set stop-effs)]
[result (in-set (loop st (append rest effs*)))])
result)]))])])))
(define stop-effs+ (if (set-empty? stop-effs)
(set '())
stop-effs))
(for*/set ([txn (in-set txns)]
[st (in-value (transition-dest txn))]
[effs* (in-set stop-effs+)]
[next-txn (in-set (loop st (append effs* rest)))])
(transition (append (transition-effs txn)
(transition-effs next-txn))
(transition-dest next-txn))))])])))
;; FacetTree FacetName (Setof FacetName) -> (List FacetName)
;; return the facets in names that are children of the given facet nm, ordered
@ -519,8 +538,10 @@
(for*/list ([act (in-list (Body->actions body))]
[role (in-list (enumerate-roles act))])
role)]
[(Sends _)
'()]
[(Spawn _)
(error)]))
(error 'enumerate-roles "Spawn not yet implemented")]))
;; Role -> TransitionDesc
;; determine how the event handlers in a role alter the facet tree
@ -645,6 +666,8 @@
(set-union agg effs++))]
[(Role nm _)
(set (list (start nm)))]
[(Sends τ)
(set (list (send τ)))]
[(Stop nm more)
(define effects (Body->effects more))
(cond
@ -754,6 +777,15 @@
[_
#f]))
;; TransitionEffect TransitionEffect -> Bool
;; subtyping lifted over transition effects
(define (eff<:? e1 e2)
(match (list e1 e2)
[(list (send t1) (send t2))
(<:? t1 t2)]
[_
#f]))
;; Role -> (Setof τ)
;; Compute the set of assertions the role contributes (on its own, not
;; considering parent assertions)
@ -788,17 +820,18 @@
(Observe String)))
;; an Equation is (equiv StateName StateName)
;; INVARIANT: lhs is "implementation", rhs is "specification"
;;
;; a Goal is one of
;; - Equation
;; - (one-of (Setof StateMatch))
;;
;; a StateMatch is a (Setof Equation)
;; a StateMatch is a (Setof (equiv Transition Transition))
(struct equiv (a b) #:transparent)
(struct one-of (opts) #:transparent)
;; (Setof StateName) (Setof StateName) -> (Setof (Setof Equation))
;; Create potential state matchings
;; (Setof Transition) (Setof Transition) -> (Setof StateMatch)
;; Create potential edge matchings
;; 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.
@ -905,9 +938,22 @@
verify/with-current-assumed))]
[(one-of matchings)
(for/or ([matching (in-set matchings)])
(define matching-hypos
(for/set ([eq (in-set matching)])
(match-define (equiv t1 t2) eq)
(equiv (transition-dest t1) (transition-dest t2))))
(for/and ([goal (in-set matching)])
(define hypotheses (set-remove matching goal))
(verify goal (set-union hypotheses assumptions))))])))
(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])
))])))
(verify (equiv st0-1 st0-2) (set)))
;; (Sequenceof StateName) Role -> (Hashof StateName (Setof τ))
@ -935,8 +981,8 @@
(for/or ([assertion1 (in-set as1)])
(<:? assertion2 assertion1))))
;; (Hashof D (Setof StateName))
;; (Hashof D (Setof StateName))
;; (Hashof D (Setof Transition))
;; (Hashof D (Setof Transition))
;; (Goal -> Bool) -> Bool
;; Determine if:
;; for each event D going from sn2,
@ -946,28 +992,45 @@
;; 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
;; and the effects on the edge going to Y are a supersequence of the effects
;; on the edge to Y
(define (same-on-specified-events? transitions1 transitions2 verify)
(for/and ([(D2 dests2) (in-hash transitions2)])
(define dests1
(for/and ([(D2 edges2) (in-hash transitions2)])
(define edges1
(for/fold ([agg (set)])
([(D1 dests) (in-hash transitions1)]
([(D1 txns1) (in-hash transitions1)]
#: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)))
(set-union agg dests)))
(set-union agg txns1)))
(cond
[(set-empty? dests1)
[(set-empty? edges1)
#f]
[else
(define combos (make-combinations dests1 dests2))
(define combos (make-combinations edges1 edges2))
(verify (one-of combos))])))
;; (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 StateName))
;; (Hashof D (Setof StateName))
;; (Hashof D (Setof Transition))
;; (Hashof D (Setof Transition))
;; StateName
;; (Goal -> Bool) -> Bool
;; Determine if:
@ -985,8 +1048,8 @@
(D<:? evt2 evt1)))
evt1))
(for*/and ([evt (in-set extra-evts)]
[dest (in-set (hash-ref transitions1 evt))])
(verify (equiv dest sn2))))
[txn (in-set (hash-ref transitions1 evt))])
(verify (equiv (transition-dest txn) sn2))))
(module+ test
(test-case
@ -1108,13 +1171,15 @@
(for/hash ([st (in-list states*)])
(define orig-txn# (state-transitions (hash-ref state# st)))
(define txn#
(for*/hash ([(D dests) (in-hash orig-txn#)]
#:when (event-enabled? D)
[dests+ (in-value (set-intersect dests states))]
;; empty dests+ might mean want to ignore this set of
;; events? TODO
#:unless (set-empty? dests+))
(values D dests+)))
(for/hash ([D (in-hash-keys orig-txn#)]
#:when (event-enabled? D))
(define orig-txns (hash-ref orig-txn# D))
(define new-txns
(for/set ([txn (in-set orig-txns)]
#:when (set-member? states (transition-dest txn)))
txn))
;; TODO - what if new-txns is empty?
(values D new-txns)))
(values st (state st txn#))))
(for ([st0 (in-list states*)])
(define rg (role-graph st0 st#))
@ -1142,9 +1207,9 @@
(search more seen)]
[else
(define connections
(for*/list ([dests (in-hash-values txn#)]
[d (in-set dests)])
d))
(for*/list ([txn* (in-hash-values txn#)]
[txn (in-set txn*)])
(transition-dest txn)))
(search (append more connections)
(set-add seen name))])])))
@ -1153,10 +1218,10 @@
"reachable states"
(define rg
(role-graph (set 'X 'Y 'Z)
(hash (set 'X 'Y 'Z) (state (set 'X 'Y 'Z) (hash (Asserted Int) (set (set 'X 'Y 'Z))
(Retracted Int) (set (set 'X 'Y))))
(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 'X) (state (set 'X) '#hash())
(set 'X 'Y) (state (set 'X 'Y) (hash (Asserted Int) (set (set 'X 'Y 'Z)))))))
(set 'X 'Y) (state (set 'X 'Y) (hash (Asserted Int) (set (transition '() (set 'X 'Y 'Z))))))))
(define reachable (reachable-states rg))
(check-true (set-member? reachable (set 'X 'Y 'Z)))
(check-true (set-member? reachable (set 'X 'Y)))
@ -1172,9 +1237,9 @@
(set 'during-inner2 'during-inner1 'tm)
(hash
(Asserted (Struct 'TaskAssignment (list)))
(set (set 'during-inner2 'during-inner1 'tm))
(set (transition '() (set 'during-inner2 'during-inner1 'tm)))
(Retracted (Struct 'TaskAssignment (list)))
(set (set 'during-inner1 'tm))))
(set (transition '() (set 'during-inner1 'tm)))))
(set 'tm)
(state (set 'tm) '#hash())
(set 'during-inner1 'tm)
@ -1182,7 +1247,7 @@
(set 'during-inner1 'tm)
(hash
(Asserted (Struct 'TaskAssignment (list)))
(set (set 'during-inner2 'during-inner1 'tm)))))))
(set (transition '() (set 'during-inner2 'during-inner1 'tm))))))))
(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)))
@ -1205,104 +1270,108 @@
;; ---------------------------------------------------------------------------
;; Visualization
(module+ vis
;; TODO - for now, assume there are no names that need escaping
;; 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 -> 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))
;; 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 "}\""))
;; 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 StateName -> DotString
;; describe an edge between the states with the corresponding label
(define (render-edge from evt to)
(define target-dot (state-name->dot-name to))
(define edge-label (D->label evt))
(format "~a -> ~a [label=\"~a\"];" from target-dot edge-label))
;; 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 τ))]
[(== StartEvt)
"Start"]
[(== StopEvt)
"Stop"]
[(== DataflowEvt)
"Dataflow"]))
;; 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 τ))]
[(== 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))
(paren-join (cons "U" slots))]))
)
;; τ -> 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))
(paren-join (cons "U" slots))]))
;; ---------------------------------------------------------------------------
;; Converting types from the turnstile implementation
@ -1315,6 +1384,8 @@
(Role name parsed-eps)]
[(list 'Spawn t)
(Spawn (parse-T t))]
[(list 'Sends t)
(Sends (parse-τ t))]
[(list 'Stop name body ...)
(define bdy (if (= (length body) 1)
(first body)
@ -1994,3 +2065,47 @@
(Stop during-inner))))
(Reacts (Retracted (TaskManager (Bind Symbol) (Bind Int))))
(Reacts (Asserted (TaskManager (Bind Symbol) (Bind Int))))))
;; ---------------------------------------------------------------------------
;; 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))))