-
Notifications
You must be signed in to change notification settings - Fork 7
/
RenderFlow.hs
114 lines (109 loc) · 4.93 KB
/
RenderFlow.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Rendering just the state space of a state machine might be sometimes
-- limiting.
--
-- Given that the `StateMachineT` data type encodes a lot of information on the
-- structure of a machine, we can actually use it to render a more informative
-- flow.
module Crem.Render.RenderFlow where
import Crem.Render.Render
import Crem.StateMachine
import "nothunks" NoThunks.Class (NoThunks (..), allNoThunks)
-- | A tree-like structure which could be used to attach metadata to any
-- similar tree-like structure with only leaves and nodes with exactly two
-- child.
data TreeMetadata a
= LeafLabel a
| BinaryLabel (TreeMetadata a) (TreeMetadata a)
deriving stock (Show)
instance NoThunks a => NoThunks (TreeMetadata a) where
showTypeOf _ = "TreeMetadata"
wNoThunks ctxt tm =
case tm of
LeafLabel x -> noThunks ctxt x
BinaryLabel x y -> allNoThunks [noThunks ctxt x, noThunks ctxt y]
-- | Given a `StateMachineT` and a `TreeMetadata` of @MachineLabel@s, we can
-- create a flow representation of our machine.
--
-- For every leaf it will render the state space, while for every other node,
-- it will render the flow between the composed machines.
--
-- More details available in [\/docs\/how-to-render-a-machine.md](https://github.com/tweag/crem/tree/main/docs/how-to-render-a-machine.md)
renderFlow :: TreeMetadata MachineLabel -> StateMachineT m input output -> Either String (Mermaid, MachineLabel, MachineLabel)
renderFlow (LeafLabel label) (Basic machine) =
Right
( Mermaid ("state " <> getLabel label <> " {")
<> renderLabelledGraph label (baseMachineAsGraph machine)
<> Mermaid "}"
, label
, label
)
renderFlow (BinaryLabel leftLabels rightLabels) (Sequential machine1 machine2) = do
(leftMermaid, leftLabelIn, leftLabelOut) <- renderFlow leftLabels machine1
(rightMermaid, rightLabelIn, rightLabelOut) <- renderFlow rightLabels machine2
Right
( leftMermaid
<> rightMermaid
<> Mermaid (getLabel leftLabelOut <> " --> " <> getLabel rightLabelIn)
, leftLabelIn
, rightLabelOut
)
renderFlow (BinaryLabel upperLabels lowerLabels) (Parallel machine1 machine2) = do
(upperMermaid, upperLabelIn, upperLabelOut) <- renderFlow upperLabels machine1
(lowerMermaid, lowerLabelIn, lowerLabelOut) <- renderFlow lowerLabels machine2
let
inLabel = "fork_" <> getLabel upperLabelIn <> getLabel lowerLabelIn
outLabel = "join_" <> getLabel upperLabelOut <> getLabel lowerLabelOut
Right
( upperMermaid
<> lowerMermaid
<> Mermaid ("state " <> inLabel <> " <<fork>>")
<> Mermaid ("state " <> outLabel <> " <<join>>")
<> Mermaid (inLabel <> " --> " <> getLabel upperLabelIn)
<> Mermaid (inLabel <> " --> " <> getLabel lowerLabelIn)
<> Mermaid (getLabel upperLabelOut <> " --> " <> outLabel)
<> Mermaid (getLabel lowerLabelOut <> " --> " <> outLabel)
, MachineLabel inLabel
, MachineLabel outLabel
)
renderFlow (BinaryLabel upperLabels lowerLabels) (Alternative machine1 machine2) = do
(upperMermaid, upperLabelIn, upperLabelOut) <- renderFlow upperLabels machine1
(lowerMermaid, lowerLabelIn, lowerLabelOut) <- renderFlow lowerLabels machine2
let
inLabel = "fork_choice_" <> getLabel upperLabelIn <> getLabel lowerLabelIn
outLabel = "join_choice_" <> getLabel upperLabelOut <> getLabel lowerLabelOut
Right
( upperMermaid
<> lowerMermaid
<> Mermaid ("state " <> inLabel <> " <<choice>>")
<> Mermaid ("state " <> outLabel <> " <<choice>>")
<> Mermaid (inLabel <> " --> " <> getLabel upperLabelIn)
<> Mermaid (inLabel <> " --> " <> getLabel lowerLabelIn)
<> Mermaid (getLabel upperLabelOut <> " --> " <> outLabel)
<> Mermaid (getLabel lowerLabelOut <> " --> " <> outLabel)
, MachineLabel inLabel
, MachineLabel outLabel
)
renderFlow (BinaryLabel forwardLabels backwardsLabels) (Feedback machine1 machine2) = do
(forwardMermaid, forwardLabelIn, forwardLabelOut) <- renderFlow forwardLabels machine1
(backwardMermaid, backawardLabelIn, backwardLabelOut) <- renderFlow backwardsLabels machine2
Right
( forwardMermaid
<> backwardMermaid
<> Mermaid (getLabel forwardLabelOut <> " --> " <> getLabel backawardLabelIn <> ": []")
<> Mermaid (getLabel backwardLabelOut <> " --> " <> getLabel forwardLabelIn <> ": []")
, forwardLabelIn
, forwardLabelOut
)
renderFlow (BinaryLabel leftLabels rightLabels) (Kleisli machine1 machine2) = do
(leftMermaid, leftLabelIn, leftLabelOut) <- renderFlow leftLabels machine1
(rightMermaid, rightLabelIn, rightLabelOut) <- renderFlow rightLabels machine2
Right
( leftMermaid
<> rightMermaid
<> Mermaid (getLabel leftLabelOut <> " --> " <> getLabel rightLabelIn <> ": []")
, leftLabelIn
, rightLabelOut
)
renderFlow labels _ = Left $ "Labels structure " <> show labels <> " does not match machine structure" -- TODO: this sucks