module SMR.CLI.Repl where
import SMR.Core.Exp
import qualified SMR.CLI.Help as Help
import qualified SMR.CLI.Driver.Load as Driver
import qualified SMR.Core.Step as Step
import qualified SMR.Core.World as World
import qualified SMR.Prim.Name as Prim
import qualified SMR.Prim.Op as Prim
import qualified SMR.Prim.Op.Base as Prim
import qualified SMR.Source.Parser as Source
import qualified SMR.Source.Lexer as Source
import qualified SMR.Source.Pretty as Source
import qualified SMR.Source.Expected as Source
import qualified Data.Text.Lazy.IO as TL
import qualified Data.Text.Lazy.Builder as BL
import qualified System.Console.Haskeline as HL
import qualified Data.Char as Char
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Data.Text as Text
import Control.Monad.IO.Class
import Data.Text (Text)
import Data.Set (Set)
import Data.Monoid
data Mode s p w
= ModeNone
| ModeParse
| ModePush (Exp s p)
| ModeStep (Step.Config s p w) (Exp s p)
data State s p w
= State
{
stateMode :: Mode s p w
, stateDecls :: [Decl s p]
, stateFiles :: [FilePath]
, stateWorld :: World.World w }
type RState = State Text Prim.Prim ()
type RConfig = Step.Config Text Prim.Prim ()
type RWorld = World.World ()
type RDecl = Decl Text Prim.Prim
type RExp = Exp Text Prim.Prim
replStart :: RState -> IO ()
replStart state
= HL.runInputT HL.defaultSettings
$ do HL.outputStrLn "Shimmer, version 0.1. The Lambda Machine."
HL.outputStrLn "Type :help for help."
replReload state
replLoop :: RState -> HL.InputT IO ()
replLoop state
= do minput <- HL.getInputLine "> "
case minput of
Nothing
-> return ()
Just input
| all Char.isSpace input
-> case stateMode state of
ModeNone -> replLoop state
ModePush xx -> replPush_next state xx
ModeStep c xx -> replStep_next state c xx
_ -> replLoop state
| otherwise
-> case words input of
":quit" : [] -> replQuit state
":help" : [] -> replHelp state
":reload" : [] -> replReload state
":r" : [] -> replReload state
":grammar" : [] -> replGrammar state
":prims" : [] -> replPrims state
":decls" : xs
-> let strip ('@' : name) = name
strip name = name
in replDecls state
$ Set.fromList $ map Text.pack
$ map strip xs
":parse" : xs -> replParse state (unwords xs)
":push" : xs -> replPush state (unwords xs)
":step" : xs -> replStep state (unwords xs)
":steps" : xs -> replSteps state (unwords xs)
":trace" : xs -> replTrace state (unwords xs)
_ -> replSteps state input
replQuit :: RState -> HL.InputT IO ()
replQuit _state
= do return ()
replHelp :: RState -> HL.InputT IO ()
replHelp state
= do HL.outputStr $ Help.helpCommands
replLoop state
replGrammar :: RState -> HL.InputT IO ()
replGrammar state
= do HL.outputStr $ Help.helpGrammar
replLoop state
replPrims :: RState -> HL.InputT IO ()
replPrims state
= do HL.outputStrLn
$ " name params description"
HL.outputStrLn
$ " ---- ------ -----------"
HL.outputStr
$ unlines
[ " #unit unit value"
, " #true boolean true"
, " #false boolean false"
, " #nat'NAT natural number"
, " #list list constructor" ]
HL.outputStr
$ unlines
$ [ leftPad 16 (" #" ++ (Text.unpack $ name))
++ leftPad 10 (concat [showForm f | f <- Prim.primEvalForm p])
++ Text.unpack (Prim.primEvalDesc p)
| p@(Prim.PrimEval { Prim.primEvalName = Prim.PrimOp name })
<- Prim.primOps ]
replLoop state
showForm :: Form -> String
showForm PVal = "!"
showForm PExp = "~"
leftPad :: Int -> [Char] -> [Char]
leftPad n ss
= ss ++ replicate (n length ss) ' '
replDecls :: RState -> Set Name -> HL.InputT IO ()
replDecls state names
= do liftIO $ mapM_ (printDecl names)
$ stateDecls state
replLoop state
printDecl :: Set Name -> RDecl -> IO ()
printDecl names decl
| Set.null names
= do TL.putStr
$ BL.toLazyText
$ Source.buildDecl decl
| DeclMac name _ <- decl
, Set.member name names
= do TL.putStr
$ BL.toLazyText
$ Source.buildDecl decl
| otherwise
= return ()
replReload :: RState -> HL.InputT IO ()
replReload state
= do
decls <- liftIO
$ fmap concat $ mapM Driver.runLoadFileDecls
$ stateFiles state
replLoop (state
{ stateDecls = decls })
replParse :: RState -> String -> HL.InputT IO ()
replParse state str
= do result <- liftIO $ replParseExp state str
case result of
Nothing
-> replLoop state
Just xx
-> do liftIO $ TL.putStrLn
$ BL.toLazyText
$ Source.buildExp Source.CtxTop xx
HL.outputStr "\n"
replLoop state
replPush :: RState -> String -> HL.InputT IO ()
replPush state str
= do result <- liftIO $ replParseExp state str
case result of
Nothing -> replLoop state
Just xx -> replPush_next state xx
replPush_next :: RState -> RExp -> HL.InputT IO ()
replPush_next state xx
= case pushDeep xx of
Nothing -> replLoop $ state { stateMode = ModeNone }
Just xx'
-> do liftIO $ TL.putStrLn
$ BL.toLazyText
$ Source.buildExp Source.CtxTop xx'
replLoop $ state { stateMode = ModePush xx' }
replStep :: RState -> String -> HL.InputT IO ()
replStep state str
= replLoadExp state str replStep_next
replStep_next
:: RState -> RConfig -> RExp
-> HL.InputT IO ()
replStep_next state config xx
= do erx <- liftIO $ Step.step config (stateWorld state) xx
case erx of
Left Step.ResultDone
-> replLoop $ state { stateMode = ModeNone }
Left (Step.ResultError msg)
-> do HL.outputStrLn
$ Text.unpack
$ Text.pack "error: " <> msg
Right xx'
-> do liftIO $ TL.putStrLn
$ BL.toLazyText
$ Source.buildExp Source.CtxTop xx'
replLoop $ state { stateMode = ModeStep config xx' }
replSteps :: RState -> String -> HL.InputT IO ()
replSteps state str
= replLoadExp state str replSteps_next
replSteps_next
:: RState -> RConfig -> RExp
-> HL.InputT IO ()
replSteps_next state config xx
= do erx <- liftIO $ Step.steps config (stateWorld state) xx
case erx of
Left msg
-> do HL.outputStrLn
$ Text.unpack
$ Text.pack "error: " <> msg
Right xx'
-> do liftIO $ TL.putStrLn
$ BL.toLazyText
$ Source.buildExp Source.CtxTop xx'
replLoop $ state { stateMode = ModeNone }
replTrace :: RState -> String -> HL.InputT IO ()
replTrace state str
= replLoadExp state str replTrace_next
replTrace_next
:: RState -> RConfig -> RExp
-> HL.InputT IO ()
replTrace_next state config !xx0
= loop xx0
where
loop !xx
= do erx <- liftIO $ Step.step config (stateWorld state) xx
case erx of
Left (Step.ResultError msg)
-> do HL.outputStrLn
$ Text.unpack
$ Text.pack "error: " <> msg
Left Step.ResultDone
-> replLoop $ state { stateMode = ModeNone }
Right xx'
-> do liftIO $ TL.putStrLn
$ BL.toLazyText
$ Source.buildExp Source.CtxTop xx'
loop xx'
replLoadExp
:: RState -> String
-> (RState -> RConfig -> RExp -> HL.InputT IO ())
-> HL.InputT IO ()
replLoadExp state str eat
= do result <- liftIO $ replParseExp state str
case result of
Nothing -> replLoop state
Just xx
-> let
decls = Map.fromList
$ [ (n, x) | DeclMac n x <- stateDecls state ]
prims = Map.fromList
$ [ (Prim.primEvalName p, p) | p <- Prim.primOps ]
config = Step.Config
{ Step.configUnderLambdas = True
, Step.configHeadArgs = True
, Step.configDeclsMac = decls
, Step.configPrims = prims }
in eat state config xx
replParseExp :: RState -> String -> IO (Maybe RExp)
replParseExp _state str
= do let (ts, _loc, _csRest)
= Source.lexTokens (Source.L 1 1) str
let config
= Source.Config
{ Source.configReadSym = Just
, Source.configReadPrm = Prim.readPrim Prim.primNames }
case Source.parseExp config ts of
Left err
-> do liftIO $ putStrLn
$ "parse error\n"
++ Source.pprParseError err
return Nothing
Right xx
-> return (Just xx)