{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DerivingStrategies #-}
module Test.StateMachine.BoxDrawer
( EventType(..)
, Fork(..)
, exec
) where
import Prelude
import Text.PrettyPrint.ANSI.Leijen
(Doc, text, vsep)
import Test.StateMachine.Types
(Pid(..))
data EventType = Open | Close
deriving stock (Show)
data Event = Event EventType Pid String
data Cmd = Top | Start String | Active | Deactive | Ret String | Bottom
compile :: [Event] -> ([Cmd], [Cmd])
compile = go (Deactive, Deactive)
where
infixr 9 `add`
add :: (a,b) -> ([a], [b]) -> ([a], [b])
add (x,y) (xs, ys) = (x:xs, y:ys)
set :: (a, a) -> Pid -> a -> (a, a)
set (_x, y) (Pid 1) x' = (x', y)
set (x, _y) (Pid 2) y' = (x, y')
set _ pid _ = error $ "compile.set: unknown pid " ++ show pid
go :: (Cmd, Cmd) -> [Event] -> ([Cmd], [Cmd])
go _ [] = ([], [])
go st (Event Open pid l : rest) =
set st pid Top `add` set st pid (Start l) `add` go (set st pid Active) rest
go st (Event Close pid l : rest) =
set st pid (Ret l) `add` set st pid Bottom `add` go (set st pid Deactive) rest
size :: Cmd -> Int
size Top = 4
size (Start l) = 6 + length l
size Active = 2
size Deactive = 0
size (Ret l) = 4 + length l
size Bottom = 4
adjust :: Int -> Cmd -> String
adjust n Top = "┌" ++ replicate (n - 4) '─' ++ "┐"
adjust n (Start l) = "│ " ++ l ++ replicate (n - length l - 6) ' ' ++ " │"
adjust n Active = "│" ++ replicate (n - 4) ' ' ++ "│"
adjust n Deactive = replicate (n - 2) ' '
adjust n (Ret l) = "│ " ++ replicate (n - 8 - length l) ' ' ++ "→ " ++ l ++ " │"
adjust n Bottom = "└" ++ replicate (n - 4) '─' ++ "┘"
next :: ([Cmd], [Cmd]) -> [String]
next (left, right) = take (length left `max` length right) $ zipWith merge left' right'
where
left' = map (adjust $ maximum $ 0:map size left) (left ++ repeat Deactive)
right' = map (adjust $ maximum $ 0:map size right) (right ++ repeat Deactive)
merge x y = x ++ " │ " ++ y
toEvent :: [(EventType, Pid)] -> ([String], [String]) -> [Event]
toEvent [] ([], []) = []
toEvent [] ps = error $ "toEvent: residue inputs: " ++ show ps
toEvent ((e , Pid 1):evT) (x:xs, ys) = Event e (Pid 1) x : toEvent evT (xs, ys)
toEvent ((_e, Pid 1):_evT) ([] , _ys) = error "toEvent: no input from pid 1"
toEvent ((e , Pid 2):evT) (xs , y:ys) = Event e (Pid 2) y : toEvent evT (xs, ys)
toEvent ((_e, Pid 2):_evT) (_xs , []) = error "toEvent: no input from pid 2"
toEvent (e : _) _ = error $ "toEvent: unknown pid " ++ show e
compilePrefix :: [String] -> [Cmd]
compilePrefix [] = []
compilePrefix (cmd:res:prefix) = Top : Start cmd : Ret res : Bottom : compilePrefix prefix
compilePrefix [cmd] = error $ "compilePrefix: doesn't have response for cmd: " ++ cmd
data Fork a = Fork a a a
deriving stock Functor
exec :: [(EventType, Pid)] -> Fork [String] -> Doc
exec evT (Fork lops pops rops) = vsep $ map text (preBoxes ++ parBoxes)
where
preBoxes = map (adjust $ maximum $ 0:map ((2+) . length) (take 1 parBoxes)) $ compilePrefix pops
parBoxes = next . compile $ toEvent evT (lops, rops)