{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE UndecidableInstances #-} -- For ShowLetName module Symantic.Parser.Machine.View where import Data.Bool (Bool(..)) import Data.Either (Either(..)) import Data.Function (($), (.), id, on) import Data.Functor ((<$>)) import Data.Kind (Type) import Data.Ord (Ord(..)) import Data.Semigroup (Semigroup(..)) import Data.String (String) import Data.Tuple (fst) import GHC.TypeLits (symbolVal) import Text.Show (Show(..)) import qualified Data.HashMap.Strict as HM import qualified Data.List as List import qualified Data.Map.Strict as Map import qualified Data.Tree as Tree import qualified Language.Haskell.TH.Syntax as TH import Prelude (error) import Symantic.Parser.Grammar.ObserveSharing (ShowLetName(..)) import Symantic.Parser.Machine.Instructions import Symantic.Univariant.Letable (SomeLet(..)) import Symantic.Parser.Machine.Generate -- * Type 'ViewMachine' data ViewMachine (showName::Bool) inp (vs:: [Type]) a = ViewMachine { viewGen :: Gen inp vs a -- ^ Provide 'GenAnalysis', which next important for debugging -- and improving golden tests, see 'viewInstrCmd'. , unViewMachine :: CallTrace -> LetMap GenAnalysis -> -- Output of 'runGenAnalysis'. Tree.Forest (String, String) -> Tree.Forest (String, String) } viewMachine :: ViewMachine sN inp vs a -> ViewMachine sN inp vs a viewMachine = id -- | Helper to view a command. viewInstrCmd :: Either TH.Name (Gen inp vs a) -> CallTrace -> LetMap GenAnalysis -> (String, String) -> Tree.Forest (String, String) -> Tree.Tree (String, String) viewInstrCmd gen ct lm (n, no) = Tree.Node $ (n <> "\nminReads="<>showsPrec 11 (minReads ga) "" <> "\nmayRaise="<>showsPrec 11 (Map.keys (mayRaise ga)) "" , no) where ga = case gen of Right r -> (\f -> f ct) $ genAnalysis r $ (\f _ct -> f) <$> lm Left l -> HM.findWithDefault (error (show (l, HM.keys lm))) l lm -- | Helper to view an argument. viewInstrArg :: String -> Tree.Forest (String, String) -> Tree.Tree (String, String) viewInstrArg n = Tree.Node $ ("<"<>n<>">", "") instance Show (ViewMachine sN inp vs a) where show vm = List.unlines $ drawTrees $ unViewMachine vm [] (runGenAnalysis (genAnalysisByLet (viewGen vm))) [] where draw :: Tree.Tree (String, String) -> [String] draw (Tree.Node (x, n) ts0) = shift "" " " (List.zipWith (<>) (List.lines x) (n : List.repeat "")) <> shift "| " "| " (drawTrees ts0) drawTrees [] = [] drawTrees [t] = draw t drawTrees (t:ts) = draw t <> drawTrees ts shift ind0 ind = List.zipWith (<>) (ind0 : List.repeat ind) instance InstrValuable (ViewMachine sN) where pushValue a k = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("pushValue "<>showsPrec 10 a "", "") [] : unViewMachine k ct lm next , viewGen = gen } where gen = pushValue a (viewGen k) popValue k = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("popValue", "") [] : unViewMachine k ct lm next , viewGen = gen } where gen = popValue (viewGen k) lift2Value f k = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("lift2Value "<>showsPrec 10 f "", "") [] : unViewMachine k ct lm next , viewGen = gen } where gen = lift2Value f (viewGen k) swapValue k = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("swapValue", "") [] : unViewMachine k ct lm next , viewGen = gen } where gen = swapValue (viewGen k) instance InstrExceptionable (ViewMachine sN) where raiseException lbl err = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("raiseException "<>show (symbolVal lbl), "") [] : next , viewGen = gen } where gen = raiseException lbl err popException lbl k = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("popException "<>show (symbolVal lbl), "") [] : unViewMachine k ct lm next , viewGen = gen } where gen = popException lbl (viewGen k) catchException lbl ok ko = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("catchException "<>show (symbolVal lbl), "") [ viewInstrArg "ok" (unViewMachine ok ct lm []) , viewInstrArg "ko" (unViewMachine ko ct lm []) ] : next , viewGen = gen } where gen = catchException lbl (viewGen ok) (viewGen ko) instance InstrBranchable (ViewMachine sN) where caseBranch l r = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("case", "") [ viewInstrArg "left" (unViewMachine l ct lm []) , viewInstrArg "right" (unViewMachine r ct lm []) ] : next , viewGen = gen } where gen = caseBranch (viewGen l) (viewGen r) choicesBranch ps bs d = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("choicesBranch "<>show ps, "") ( ((\b -> viewInstrArg "branch" $ unViewMachine b ct lm []) <$> bs) <> [ viewInstrArg "default" (unViewMachine d ct lm []) ] ) : next , viewGen = gen } where gen = choicesBranch ps (viewGen <$> bs) (viewGen d) instance ShowLetName sN TH.Name => InstrCallable (ViewMachine sN) where defLet defs k = ViewMachine { unViewMachine = \ct lm next -> (<> unViewMachine k ct lm next) $ List.sortBy (compare `on` (((fst <$>) <$>) . Tree.levels)) $ ((\(n, SomeLet sub) -> viewInstrCmd (Left n) ct lm ("let", " "<>showLetName @sN n) (unViewMachine sub ct lm [])) <$> HM.toList defs) , viewGen = gen } where gen = defLet ((\(SomeLet x) -> SomeLet (viewGen x)) <$> defs) (viewGen k) jump ln@(LetName n) = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("jump", " "<>showLetName @sN n) [] : next , viewGen = gen } where gen = jump ln call ln@(LetName n) k = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("call", " "<>showLetName @sN n) [] : unViewMachine k (n:ct) lm next , viewGen = gen } where gen = call ln (viewGen k) ret = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("ret", "") [] : next , viewGen = gen } where gen = ret instance ShowLetName sN TH.Name => InstrJoinable (ViewMachine sN) where defJoin ln@(LetName n) j k = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Left n) ct lm ("join", " "<>showLetName @sN n) (unViewMachine j ct lm []) : unViewMachine k (n:ct) lm next , viewGen = gen } where gen = defJoin ln (viewGen j) (viewGen k) refJoin ln@(LetName n) = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("refJoin", " "<>showLetName @sN n) [] : next , viewGen = gen } where gen = refJoin ln instance InstrInputable (ViewMachine sN) where pushInput k = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("pushInput", "") [] : unViewMachine k ct lm next , viewGen = gen } where gen = pushInput (viewGen k) loadInput k = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("loadInput", "") [] : unViewMachine k ct lm next , viewGen = gen } where gen = loadInput (viewGen k) instance InstrReadable tok Gen => InstrReadable tok (ViewMachine sN) where read es p k = ViewMachine { unViewMachine = \ct lm next -> viewInstrCmd (Right gen) ct lm ("read "<>showsPrec 10 p "", "") [] : unViewMachine k ct lm next , viewGen = gen } where gen = read es p (viewGen k)