2020-05-29 19:19:09 +00:00
|
|
|
#lang racket
|
|
|
|
|
2020-06-12 20:25:06 +00:00
|
|
|
;; TODO - syntax for LTL
|
|
|
|
;; TODO - atomic blocks
|
|
|
|
;; TODO - mark acceptable end states
|
|
|
|
;; TODO - avoid collisions with SPIN keywords, e.g. `run`
|
|
|
|
|
2020-05-29 19:19:09 +00:00
|
|
|
(require "proto.rkt")
|
|
|
|
|
|
|
|
(module+ test
|
2020-06-08 20:18:57 +00:00
|
|
|
(require rackunit)
|
|
|
|
(require "test-utils.rkt"))
|
|
|
|
|
|
|
|
;; a SpinProgram is a
|
|
|
|
;; (sprog [Assignment [Listof SpinProcess]])
|
|
|
|
(struct sprog [assignment procs] #:transparent)
|
|
|
|
|
2020-05-29 19:19:09 +00:00
|
|
|
|
|
|
|
;; a SpinProcess is a
|
2020-06-12 18:05:22 +00:00
|
|
|
;; (sproc SName [Setof SName] Assignment [Listof SName] [Setof SpinState])
|
|
|
|
(struct sproc [name state-names init initial-assertions states] #:transparent)
|
2020-06-08 20:18:57 +00:00
|
|
|
|
|
|
|
;; an Assignment is a [Hashof SVar SValue]
|
2020-05-29 19:19:09 +00:00
|
|
|
|
|
|
|
;; a SName is a Symbol that is a legal variable name in Spin
|
|
|
|
|
|
|
|
;; a SVar is a
|
|
|
|
;; (svar SName SType)
|
|
|
|
(struct svar [name ty] #:transparent)
|
|
|
|
|
|
|
|
;; a SValue is one of
|
|
|
|
;; - Int
|
|
|
|
;; - Bool
|
|
|
|
;; - SName
|
|
|
|
;; and must be a valid Spin literal
|
|
|
|
|
|
|
|
;; a SType is one of
|
|
|
|
;; - 'SInt
|
|
|
|
;; - 'SBool
|
|
|
|
;; - 'mtype
|
|
|
|
(define SInt 'SInt)
|
|
|
|
(define SBool 'SBool)
|
|
|
|
(define mtype 'mtype)
|
|
|
|
|
|
|
|
;; a SpinState is a
|
|
|
|
;; (sstate SName [Sequenceof SBranch])
|
|
|
|
(struct sstate [name branches] #:transparent)
|
|
|
|
|
2020-06-08 20:18:57 +00:00
|
|
|
;; a SBranch is a
|
|
|
|
;; (sbranch D+ SName [Listof SAction])
|
|
|
|
(struct sbranch [event dest actions] #:transparent)
|
|
|
|
|
|
|
|
;; a SAction is one of
|
|
|
|
;; - (assert ?)
|
|
|
|
;; - (retract ?)
|
|
|
|
;; - (send ?)
|
|
|
|
;; - (incorporate D+)
|
2020-06-10 18:40:07 +00:00
|
|
|
;; - (transition-to SName)
|
2020-06-08 20:18:57 +00:00
|
|
|
(struct assert [ty] #:transparent)
|
|
|
|
(struct retract [ty] #:transparent)
|
|
|
|
;; send defined in proto.rkt
|
|
|
|
(struct incorporate [evt] #:transparent)
|
2020-06-10 18:40:07 +00:00
|
|
|
(struct transition-to [dest] #:transparent)
|
2020-06-08 20:18:57 +00:00
|
|
|
|
|
|
|
;; each process has a local variable that determines its current state
|
|
|
|
(define CURRENT-STATE (svar 'current mtype))
|
|
|
|
|
2020-06-10 21:09:30 +00:00
|
|
|
;; a NameEnvironment is a [Hashof τ SName]
|
|
|
|
|
2020-06-08 20:18:57 +00:00
|
|
|
;; [Sequenceof RoleGraph] -> SpinProgram
|
2020-06-10 21:09:30 +00:00
|
|
|
(define (program->spin rgs)
|
|
|
|
(define assertion-tys (all-assertions rgs))
|
|
|
|
(define event-tys (all-events rgs))
|
2020-06-12 19:27:52 +00:00
|
|
|
(define event-subty# (make-event-map assertion-tys event-tys))
|
2020-06-12 18:05:22 +00:00
|
|
|
(define all-mentioned-tys (set-union assertion-tys event-tys))
|
|
|
|
(define name-env (make-name-env all-mentioned-tys))
|
|
|
|
(define globals (initial-assertion-vars-for all-mentioned-tys name-env))
|
2020-06-12 19:27:52 +00:00
|
|
|
(define procs (for/list ([rg rgs]) (rg->spin rg event-subty# name-env)))
|
2020-06-08 20:18:57 +00:00
|
|
|
(sprog globals procs))
|
|
|
|
|
2020-06-12 19:27:52 +00:00
|
|
|
;; RoleGraph [Hashof τ [Setof τ]] NameEnvironment -> SpinProcess
|
|
|
|
(define (rg->spin rg event-subty# name-env #:name [name (gensym 'proc)])
|
2020-06-08 20:18:57 +00:00
|
|
|
(match-define (role-graph st0 states) rg)
|
|
|
|
(define all-events (all-event-types (in-hash-values states)))
|
2020-06-10 21:09:30 +00:00
|
|
|
(define state-renaming (make-state-rename (hash-keys states)))
|
2020-06-08 20:18:57 +00:00
|
|
|
(define states- (for/list ([st (in-hash-values states)])
|
2020-06-12 19:27:52 +00:00
|
|
|
(state->spin st states event-subty# name-env state-renaming)))
|
2020-06-10 21:09:30 +00:00
|
|
|
(define st0- (hash-ref state-renaming st0))
|
2020-06-12 18:05:22 +00:00
|
|
|
;; ergh the invariant for when I tack on _assertions to a name is getting tricksy
|
2020-06-12 19:27:52 +00:00
|
|
|
(define st0-assertions (rename-all name-env (super-type-closure (state-assertions (hash-ref states st0)) event-subty#)))
|
2020-06-10 21:09:30 +00:00
|
|
|
(define assignment (local-variables-for st0- all-events name-env))
|
2020-06-12 18:05:22 +00:00
|
|
|
(sproc name (hash-values-set state-renaming) assignment st0-assertions (list->set states-)))
|
2020-06-10 21:09:30 +00:00
|
|
|
|
2020-06-12 19:27:52 +00:00
|
|
|
;; State [Sequenceof State] [Hashof τ [Setof τ]] NameEnvironment [Hashof StateName SName] -> SpinState
|
|
|
|
(define (state->spin st states event-subty# name-env state-env)
|
2020-06-10 21:09:30 +00:00
|
|
|
(match-define (state name transitions assertions) st)
|
|
|
|
(define name- (hash-ref state-env name))
|
|
|
|
(define branches (for*/list ([(D+ txns) (in-hash transitions)]
|
|
|
|
[txn (in-set txns)])
|
|
|
|
(match-define (transition effs dest) txn)
|
|
|
|
(match-define (state _ _ dest-assertions) (hash-ref states dest))
|
|
|
|
(define dest- (hash-ref state-env dest))
|
2020-06-12 19:27:52 +00:00
|
|
|
(branch-on D+ assertions dest- dest-assertions effs event-subty# name-env)))
|
2020-06-10 21:09:30 +00:00
|
|
|
(sstate name- branches))
|
|
|
|
|
|
|
|
;; [Setof τ] -> NameEnvironment
|
|
|
|
(define (make-name-env tys)
|
|
|
|
(let loop ([name-depth 3])
|
|
|
|
(when (> name-depth 10)
|
|
|
|
(raise-argument-error 'make-name-env "types able to be named" tys))
|
|
|
|
(define renaming
|
|
|
|
(for/hash ([ty (in-set tys)])
|
|
|
|
(values ty
|
|
|
|
(type->id ty #:depth name-depth))))
|
|
|
|
(define names (hash-values-set renaming))
|
|
|
|
(cond
|
|
|
|
[(equal? (set-count names) (set-count tys))
|
|
|
|
renaming]
|
|
|
|
[else
|
|
|
|
(loop (add1 name-depth))])))
|
|
|
|
|
|
|
|
;; SName -> SName
|
|
|
|
(define (assertions-var-name s)
|
|
|
|
(string->symbol (format "~a_assertions" s)))
|
|
|
|
|
|
|
|
;; SName -> SName
|
|
|
|
(define (active-var-name s)
|
|
|
|
(string->symbol (format "know_~a" s)))
|
|
|
|
|
2020-06-12 20:27:30 +00:00
|
|
|
;; [Setof τ] [Setof τ] -> [Hashof τ [Setof τ]]
|
|
|
|
(define (make-event-map assertion-tys event-tys)
|
|
|
|
;; TODO - potentially use non-empty intersection
|
|
|
|
(for/hash ([a (in-set assertion-tys)])
|
|
|
|
(values a
|
|
|
|
(all-supertypes-of a event-tys))))
|
|
|
|
|
|
|
|
;; τ [Setof τ] -> [Setof τ]
|
|
|
|
(define (all-supertypes-of τ tys)
|
|
|
|
(for*/set ([ty (in-set tys)]
|
|
|
|
#:when (<:? τ ty))
|
|
|
|
ty))
|
|
|
|
|
|
|
|
;; [Setof τ] [Hashof τ [Setof τ]]
|
|
|
|
(define (super-type-closure asserts event-subty#)
|
|
|
|
(for*/set ([a (in-set asserts)]
|
|
|
|
[supers (in-value (hash-ref event-subty# a))]
|
|
|
|
[τ (in-set (set-add supers a))])
|
|
|
|
τ))
|
|
|
|
|
2020-06-10 21:09:30 +00:00
|
|
|
;; [Setof τ] NameEnvironment -> Assignment
|
|
|
|
(define (initial-assertion-vars-for assertion-tys name-env)
|
|
|
|
(for/hash ([τ (in-set assertion-tys)])
|
2020-06-12 18:05:22 +00:00
|
|
|
(values (svar (assertions-var-name (hash-ref name-env τ)) SInt)
|
2020-06-08 20:18:57 +00:00
|
|
|
0)))
|
|
|
|
|
2020-06-12 18:05:22 +00:00
|
|
|
;; NameEnvironment [Setof τ] -> [Sequenceof SName]
|
|
|
|
(define (rename-all name-env asserts)
|
|
|
|
(for/set ([a (in-set asserts)])
|
|
|
|
(hash-ref name-env a)))
|
|
|
|
|
2020-06-08 20:18:57 +00:00
|
|
|
;; [Sequenceof RoleGraph] -> [Setof τ]
|
|
|
|
(define (all-assertions rgs)
|
|
|
|
;; RoleGraph -> (Setof τ)
|
|
|
|
(define (all-assertions-of rg)
|
|
|
|
(for*/set ([st (in-hash-values (role-graph-states rg))]
|
|
|
|
[τ (in-set (state-assertions st))])
|
|
|
|
τ))
|
|
|
|
(for/fold ([as (set)])
|
|
|
|
([rg rgs])
|
|
|
|
(set-union as (all-assertions-of rg))))
|
|
|
|
|
2020-06-10 21:09:30 +00:00
|
|
|
;; [Sequenceof RoleGraph] -> [Setof τ]
|
|
|
|
(define (all-events rgs)
|
|
|
|
;; RoleGraph -> (Setof τ)
|
|
|
|
(define (all-events-of rg)
|
|
|
|
(all-event-types (hash-values (role-graph-states rg))))
|
|
|
|
(for/fold ([as (set)])
|
|
|
|
([rg rgs])
|
|
|
|
(set-union as (all-events-of rg)))
|
|
|
|
)
|
2020-06-08 20:18:57 +00:00
|
|
|
|
|
|
|
;; [Sequenceof State] -> ?
|
|
|
|
(define (all-event-types states)
|
|
|
|
(for*/set ([st states]
|
|
|
|
[D+ (in-hash-keys (state-transitions st))])
|
|
|
|
(match D+
|
|
|
|
[(or (Asserted τ) (Retracted τ))
|
|
|
|
τ]
|
|
|
|
[(Message τ)
|
|
|
|
(raise-argument-error 'all-event-types "messages not supported yet" D+)]
|
|
|
|
[_
|
|
|
|
(raise-argument-error 'all-event-types "internal events not allowed" D+)])))
|
|
|
|
|
2020-06-12 18:05:22 +00:00
|
|
|
;; SName [Setof τ] NameEnvironment -> Assignment
|
2020-06-10 21:09:30 +00:00
|
|
|
(define (local-variables-for st0 all-events name-env)
|
2020-06-08 20:18:57 +00:00
|
|
|
(define assign
|
|
|
|
(for/hash ([evt (in-set all-events)])
|
2020-06-10 21:09:30 +00:00
|
|
|
(values (svar (active-var-name (hash-ref name-env evt))
|
|
|
|
SBool)
|
2020-06-08 20:18:57 +00:00
|
|
|
#f)))
|
|
|
|
(hash-set assign CURRENT-STATE st0))
|
|
|
|
|
2020-06-12 19:27:52 +00:00
|
|
|
;; D+ [Setof τ] SName [Setof τ] [Listof TransitionEffect] [Hashof τ [Setof τ]] NameEnvironment -> SBranch
|
|
|
|
(define (branch-on D+ curr-assertions dest dest-assertions effs event-subty# name-env)
|
|
|
|
(define new-assertions (super-type-closure (set-subtract dest-assertions curr-assertions) event-subty#))
|
|
|
|
(define retractions (super-type-closure (set-subtract curr-assertions dest-assertions) event-subty#))
|
2020-06-10 21:09:30 +00:00
|
|
|
(define (lookup ty) (hash-ref name-env ty))
|
|
|
|
(define asserts (set-map new-assertions (compose assert lookup)))
|
|
|
|
(define retracts (set-map retractions (compose retract lookup)))
|
|
|
|
(unless (empty? effs)
|
|
|
|
(raise-argument-error 'branch-on "messages not supported" effs))
|
|
|
|
(define renamed-evt (rename-event D+ name-env))
|
|
|
|
(sbranch renamed-evt dest (list* (transition-to dest)
|
|
|
|
(incorporate renamed-evt)
|
|
|
|
(append asserts retracts effs))))
|
|
|
|
|
|
|
|
;; D+ NameEnvironment -> D+
|
|
|
|
(define (rename-event D+ name-env)
|
|
|
|
(match D+
|
|
|
|
[(Asserted τ)
|
|
|
|
(Asserted (hash-ref name-env τ))]
|
|
|
|
[(Retracted τ)
|
|
|
|
(Retracted (hash-ref name-env τ))]
|
|
|
|
[(Message τ)
|
|
|
|
(raise-argument-error 'rename-event "messages not implemented yet" D+)]))
|
2020-06-08 20:18:57 +00:00
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"sanity: compile book seller type"
|
|
|
|
(define/timeout seller-rg (compile seller-actual))
|
2020-06-10 21:09:30 +00:00
|
|
|
(define name-env (hash
|
|
|
|
(Observe (Observe (Struct 'BookQuoteT (list (Base 'String) (Mk⋆)))))
|
|
|
|
'Obs_Obs_BookQuoteT
|
|
|
|
(Observe (Struct 'BookQuoteT (list (Base 'String) (Mk⋆))))
|
|
|
|
'Obs_BookQuoteT_String_star
|
|
|
|
(Struct 'BookQuoteT (list (Base 'String) (U (list (Base 'Int) (Base 'Int)))))
|
|
|
|
'BookQuoteT_String_U_Int_Int))
|
2020-06-12 19:27:52 +00:00
|
|
|
(define event# (hash
|
|
|
|
(Observe (Observe (Struct 'BookQuoteT (list (Base 'String) (Mk⋆)))))
|
|
|
|
(set)
|
|
|
|
(Struct 'BookQuoteT (list (Base 'String) (U (list (Base 'Int) (Base 'Int)))))
|
|
|
|
(set)))
|
|
|
|
(define/timeout seller-spin (rg->spin seller-rg event# name-env))
|
2020-06-08 20:18:57 +00:00
|
|
|
(check-true (sproc? seller-spin))))
|
2020-06-10 18:40:07 +00:00
|
|
|
|
|
|
|
(define tab-level (make-parameter 0))
|
|
|
|
|
|
|
|
(define TAB-WIDTH 2)
|
|
|
|
|
|
|
|
(define (indent)
|
|
|
|
(display (make-string (* TAB-WIDTH (tab-level)) #\space)))
|
|
|
|
|
|
|
|
(define-syntax-rule (with-indent bdy ...)
|
|
|
|
(parameterize ([tab-level (add1 (tab-level))])
|
|
|
|
bdy ...))
|
|
|
|
|
2020-06-10 21:09:30 +00:00
|
|
|
(define SPIN_ID_RX #rx"[a-zA-Z][a-zA-Z0-9_]*")
|
|
|
|
(define SPIN_ID_TRAILING_CHAR #rx"[a-zA-Z0-9_]+")
|
|
|
|
|
|
|
|
;; (U Symbol String) -> Bool
|
|
|
|
(define (spin-id? s)
|
|
|
|
(when (symbol? s)
|
|
|
|
(set! s (symbol->string s)))
|
|
|
|
(regexp-match? SPIN_ID_RX s))
|
|
|
|
|
|
|
|
;; (U Symbol String) -> SName
|
|
|
|
(define (make-spin-id s)
|
|
|
|
(when (symbol? s)
|
|
|
|
(set! s (symbol->string s)))
|
|
|
|
(define with_legal_prefix (string-append "ty_" s))
|
|
|
|
(match (regexp-match* SPIN_ID_TRAILING_CHAR with_legal_prefix)
|
|
|
|
['("ty_")
|
|
|
|
(raise-argument-error 'make-spin-id "unable to make spin id" s)]
|
|
|
|
[(cons fst rst)
|
|
|
|
(define match-str (apply string-append fst rst))
|
|
|
|
(define without-added-prefix (substring match-str 3))
|
|
|
|
(if (spin-id? without-added-prefix)
|
|
|
|
(string->symbol without-added-prefix)
|
|
|
|
(string->symbol match-str))]))
|
|
|
|
|
|
|
|
;; τ -> SName
|
|
|
|
(define (type->id ty #:depth [depth 3])
|
|
|
|
(define ctors (type-constructors ty depth))
|
|
|
|
(define rough-name (string-join (map symbol->string ctors) "_"))
|
|
|
|
(make-spin-id rough-name))
|
|
|
|
|
|
|
|
;; [Listof StateName] -> [Hashof StateName SName]
|
|
|
|
(define (make-state-rename state-names)
|
|
|
|
(let loop ([prefix 3])
|
|
|
|
(define renaming (for/hash ([nm (in-list state-names)])
|
|
|
|
(values nm
|
|
|
|
(state-name->spin-id nm #:prefix prefix))))
|
|
|
|
(define distinct-names (hash-values-set renaming))
|
|
|
|
(cond
|
|
|
|
[(equal? (set-count distinct-names) (set-count state-names))
|
|
|
|
renaming]
|
|
|
|
[(> prefix 20)
|
|
|
|
(raise-argument-error 'make-state-rename "able to make renaming" state-names)]
|
|
|
|
[else
|
|
|
|
(loop (add1 prefix))])))
|
|
|
|
|
|
|
|
;; StateName -> SName
|
|
|
|
(define (state-name->spin-id nm #:prefix [prefix 3])
|
2020-06-12 18:05:22 +00:00
|
|
|
(cond
|
|
|
|
[(set-empty? nm)
|
|
|
|
(gensym 'inert)]
|
|
|
|
[else
|
2020-06-12 20:22:43 +00:00
|
|
|
(define (take-prefix s) (substring s 0 (min prefix (string-length s))))
|
2020-06-12 18:05:22 +00:00
|
|
|
(define rough-name (string-join (set-map nm (compose take-prefix symbol->string)) "_"))
|
|
|
|
(make-spin-id rough-name)]))
|
2020-06-10 21:09:30 +00:00
|
|
|
|
|
|
|
;; τ -> [Listof Symbol]
|
|
|
|
(define (type-constructors ty depth)
|
|
|
|
(cond
|
|
|
|
[(zero? depth) '()]
|
|
|
|
[else
|
|
|
|
(match ty
|
|
|
|
[(Struct name tys)
|
|
|
|
;; TODO - consider camel-casing struct name
|
|
|
|
(cons name (append-map (λ (ty) (type-constructors ty (sub1 depth))) tys))]
|
|
|
|
[(Observe ty)
|
|
|
|
(cons 'Obs (type-constructors ty (sub1 depth)))]
|
|
|
|
[(U tys)
|
|
|
|
(cons 'U (append-map (λ (ty) (type-constructors ty (sub1 depth))) tys))]
|
2020-06-12 19:27:52 +00:00
|
|
|
[(== ⋆)
|
|
|
|
(list 'star)]
|
2020-06-10 21:09:30 +00:00
|
|
|
[(Base name)
|
|
|
|
(list name)]
|
|
|
|
[(List _)
|
|
|
|
(list 'List)]
|
|
|
|
[(Set _)
|
|
|
|
(list 'Set)]
|
|
|
|
[(Hash _ _)
|
|
|
|
(list 'Hash)]
|
|
|
|
[(internal-label _ _)
|
|
|
|
(raise-argument-error 'type-constructors "internal events not supported" ty)])]))
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case
|
|
|
|
"type-constructors basics"
|
|
|
|
(define bi (Struct 'BookInterestT (list (Base 'String) (Base 'String) (Base 'Bool))))
|
|
|
|
(check-equal? (type-constructors bi 1)
|
|
|
|
'(BookInterestT))
|
|
|
|
(check-equal? (type-constructors bi 2)
|
|
|
|
'(BookInterestT String String Bool))
|
|
|
|
(check-equal? (type-constructors bi 3)
|
|
|
|
'(BookInterestT String String Bool))
|
|
|
|
(check-equal? (type-constructors (Observe bi) 3)
|
|
|
|
'(Obs BookInterestT String String Bool)))
|
|
|
|
(test-case
|
|
|
|
"type->id basics"
|
|
|
|
(define bi (Struct 'BookInterestT (list (Base 'String) (Base 'String) (Base 'Bool))))
|
|
|
|
(check-equal? (type->id bi)
|
|
|
|
'BookInterestT_String_String_Bool)
|
|
|
|
(check-equal? (type->id (Observe bi))
|
|
|
|
'Obs_BookInterestT_String_String_Bool)
|
|
|
|
(check-equal? (type->id (Struct 'hi-mom '()))
|
|
|
|
'himom)
|
|
|
|
(check-equal? (type->id bi #:depth 1)
|
|
|
|
'BookInterestT)
|
|
|
|
(check-exn exn:fail?
|
|
|
|
(lambda () (type->id (Struct '--- '())))
|
|
|
|
"unable to make spin id")))
|
|
|
|
|
2020-06-12 18:05:22 +00:00
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Code Generation
|
|
|
|
|
|
|
|
(define SPIN-PRELUDE (file->string "spin-prelude.pml"))
|
|
|
|
|
|
|
|
;; SpinThang FilePath -> Void
|
|
|
|
(define (gen-spin/to-file spin name)
|
|
|
|
(with-output-to-file name
|
|
|
|
(lambda () (gen-spin spin))
|
|
|
|
#:mode 'text
|
|
|
|
#:exists 'replace))
|
|
|
|
|
2020-06-10 18:40:07 +00:00
|
|
|
;; SpinThang -> Void
|
|
|
|
(define (gen-spin spin)
|
|
|
|
(match spin
|
|
|
|
[(sprog assignment procs)
|
2020-06-12 18:05:22 +00:00
|
|
|
(display SPIN-PRELUDE)
|
|
|
|
(gen-assignment assignment)
|
|
|
|
(newline)
|
|
|
|
(for ([p procs])
|
|
|
|
(gen-spin p)
|
2020-06-12 19:39:02 +00:00
|
|
|
(newline))
|
|
|
|
(gen-sanity-ltl assignment)]
|
2020-06-12 18:05:22 +00:00
|
|
|
[(sproc name state-names init asserts states)
|
|
|
|
(indent) (declare-mtype state-names)
|
2020-06-10 18:40:07 +00:00
|
|
|
(indent) (printf "active proctype ~a() {\n" name)
|
|
|
|
(with-indent
|
|
|
|
(gen-assignment init)
|
2020-06-12 18:05:22 +00:00
|
|
|
(for ([a asserts])
|
|
|
|
(gen-spin (assert a)))
|
2020-06-10 18:40:07 +00:00
|
|
|
(indent) (displayln "do")
|
|
|
|
(with-indent
|
|
|
|
(for ([st states])
|
|
|
|
(gen-spin st)))
|
|
|
|
(indent) (displayln "od;")
|
|
|
|
)
|
2020-06-10 21:09:30 +00:00
|
|
|
(indent) (displayln "}")]
|
2020-06-10 18:40:07 +00:00
|
|
|
[(sstate name branches)
|
|
|
|
(indent) (printf ":: ~a == ~a ->\n" (svar-name CURRENT-STATE) name)
|
|
|
|
(with-indent
|
|
|
|
(indent) (displayln "if")
|
|
|
|
(with-indent
|
2020-06-12 18:05:22 +00:00
|
|
|
(cond
|
|
|
|
[(empty? branches)
|
|
|
|
(indent) (displayln ":: true -> skip;")]
|
|
|
|
[else
|
|
|
|
(for ([branch branches])
|
|
|
|
(gen-spin branch))]))
|
2020-06-10 18:40:07 +00:00
|
|
|
(indent) (displayln "fi;"))]
|
|
|
|
[(sbranch event dest actions)
|
2020-06-10 21:09:30 +00:00
|
|
|
(indent) (printf ":: ~a ->\n" (predicate-for event))
|
2020-06-10 18:40:07 +00:00
|
|
|
;; TODO - make the body atomic
|
|
|
|
(with-indent
|
|
|
|
(for ([act actions])
|
|
|
|
(gen-spin act)))]
|
|
|
|
[(assert x)
|
|
|
|
(indent) (printf "ASSERT(~a);\n" x)]
|
|
|
|
[(retract x)
|
|
|
|
(indent) (printf "RETRACT(~a);\n" x)]
|
|
|
|
[(send x)
|
|
|
|
(raise-argument-error 'gen-spin "message sending not supported yet" spin)]
|
|
|
|
[(incorporate evt)
|
|
|
|
(indent) (update-for evt)]
|
|
|
|
[(transition-to dest)
|
|
|
|
(indent) (printf "~a = ~a;\n" (svar-name CURRENT-STATE) dest)]))
|
|
|
|
|
2020-06-12 18:05:22 +00:00
|
|
|
;; [Setof SName] -> Void
|
|
|
|
(define (declare-mtype state-names)
|
|
|
|
(display "mtype = {")
|
|
|
|
(display (string-join (set-map state-names symbol->string) ", "))
|
|
|
|
(displayln "}"))
|
|
|
|
|
2020-06-10 18:40:07 +00:00
|
|
|
;; Assignment -> Void
|
|
|
|
(define (gen-assignment assign)
|
|
|
|
(for ([(var val) (in-hash assign)])
|
|
|
|
(indent) (printf "~a = ~a;\n"
|
|
|
|
(declare-var var)
|
|
|
|
(spin-val->string val))))
|
|
|
|
|
|
|
|
;; SVar -> Void
|
|
|
|
(define (declare-var var)
|
|
|
|
(match-define (svar name ty) var)
|
|
|
|
(format "~a ~a" (spin-type->string ty) name))
|
|
|
|
|
|
|
|
;; SValue -> String
|
|
|
|
(define (spin-val->string v)
|
|
|
|
(cond
|
|
|
|
[(boolean? v)
|
|
|
|
(if v "true" "false")]
|
|
|
|
[(exact-integer? v)
|
|
|
|
(~a v)]
|
|
|
|
[(symbol? v)
|
|
|
|
(~a v)]))
|
|
|
|
|
|
|
|
;; SType -> String
|
|
|
|
(define (spin-type->string ty)
|
|
|
|
(match ty
|
|
|
|
[(== SInt) "int"]
|
|
|
|
[(== SBool) "bool"]
|
|
|
|
[(== mtype) "mtype"]))
|
|
|
|
|
|
|
|
;; D+ -> String
|
|
|
|
(define (predicate-for event)
|
|
|
|
(match event
|
2020-06-10 21:09:30 +00:00
|
|
|
[(Asserted nm)
|
2020-06-12 18:05:22 +00:00
|
|
|
(define assertion-var nm)
|
2020-06-10 21:09:30 +00:00
|
|
|
(define active-var (active-var-name nm))
|
2020-06-10 18:40:07 +00:00
|
|
|
(format "ASSERTED(~a) && !~a" assertion-var active-var)]
|
2020-06-10 21:09:30 +00:00
|
|
|
[(Retracted nm)
|
2020-06-12 18:05:22 +00:00
|
|
|
(define assertion-var nm)
|
2020-06-10 21:09:30 +00:00
|
|
|
(define active-var (active-var-name nm))
|
2020-06-10 18:40:07 +00:00
|
|
|
(format "RETRACTED(~a) && ~a" assertion-var active-var)]
|
2020-06-10 21:09:30 +00:00
|
|
|
[(Message nm)
|
2020-06-10 18:40:07 +00:00
|
|
|
(raise-argument-error 'predicate-for "message sending not supported yet" event)]))
|
|
|
|
|
|
|
|
;; D+ -> Void
|
|
|
|
(define (update-for event)
|
|
|
|
(match event
|
2020-06-10 21:09:30 +00:00
|
|
|
[(Asserted nm)
|
|
|
|
(define active-var (active-var-name nm))
|
2020-06-10 18:40:07 +00:00
|
|
|
(printf "~a = ~a;\n" active-var (spin-val->string #t))]
|
2020-06-10 21:09:30 +00:00
|
|
|
[(Retracted nm)
|
|
|
|
(define active-var (active-var-name nm))
|
2020-06-10 18:40:07 +00:00
|
|
|
(printf "~a = ~a;\n" active-var (spin-val->string #f))]
|
2020-06-10 21:09:30 +00:00
|
|
|
[(Message nm)
|
2020-06-10 18:40:07 +00:00
|
|
|
(raise-argument-error 'predicate-for "message sending not supported yet" event)]))
|
|
|
|
|
2020-06-12 19:39:02 +00:00
|
|
|
;; Assignment -> Void
|
|
|
|
(define (gen-sanity-ltl assignment)
|
|
|
|
(indent) (displayln "ltl sanity {")
|
|
|
|
(with-indent
|
|
|
|
(indent) (displayln "[](")
|
|
|
|
(with-indent
|
|
|
|
(for ([assertion-var (in-hash-keys assignment)])
|
|
|
|
(indent) (printf "~a >= 0 &&\n" (svar-name assertion-var)))
|
|
|
|
(indent) (displayln "true"))
|
|
|
|
(indent) (displayln ")"))
|
|
|
|
(indent) (displayln "}"))
|
|
|
|
|
2020-06-10 21:09:30 +00:00
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Misc Utils
|
2020-06-10 18:40:07 +00:00
|
|
|
|
2020-06-10 21:09:30 +00:00
|
|
|
;; [Hashof K V] -> [Setof V]
|
|
|
|
(define (hash-values-set h)
|
|
|
|
(for/set ([x (in-hash-values h)])
|
|
|
|
x))
|
2020-06-12 18:05:22 +00:00
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Test Case
|
|
|
|
|
|
|
|
(module+ leader-and-seller
|
|
|
|
(define leader-rg (compile (parse-T
|
|
|
|
'(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 seller-rg (compile seller-actual))
|
|
|
|
(define member-rg (compile member-actual))
|
|
|
|
(define book-club-spin (program->spin (list leader-rg seller-rg member-rg)))
|
2020-06-12 19:45:06 +00:00
|
|
|
(gen-spin/to-file book-club-spin "gen-book-club.pml")
|
|
|
|
;; handwritten LTL formula I've added to gen-book-club.pml:
|
|
|
|
#|
|
|
|
|
&&
|
|
|
|
<> (BookQuoteT_String_star_assertions > 0)
|
|
|
|
&&
|
|
|
|
[] (ASSERTED(Obs_BookQuoteT_String_star) -> <> ASSERTED(BookQuoteT_String_star))
|
|
|
|
&&
|
|
|
|
[] (ASSERTED(Obs_BookInterestT_String_star_star) -> <> ASSERTED(BookInterestT_String_star_star))
|
|
|
|
|#
|
|
|
|
)
|
2020-06-12 18:05:22 +00:00
|
|
|
|
2020-06-12 20:22:43 +00:00
|
|
|
(module+ flink
|
|
|
|
(define (import r)
|
|
|
|
(define r+ (parse-T r))
|
|
|
|
(compile/internal-events (compile r+) #f))
|
|
|
|
(define jm-rg (import job-manager-actual))
|
|
|
|
(define tm-rg (import task-manager-ty))
|
|
|
|
(define tr-rg (import task-runner-ty))
|
|
|
|
(define flink-spin (program->spin (list tr-rg tm-rg jm-rg)))
|
|
|
|
(gen-spin/to-file flink-spin "gen-flink.pml"))
|
|
|
|
|
2020-06-12 18:05:22 +00:00
|
|
|
(require racket/trace)
|
|
|
|
#;(trace make-spin-id)
|
|
|
|
#;(trace state->spin)
|
|
|
|
#;(trace state-name->spin-id)
|