2024-12-20 11:38:09 -06:00
|
|
|
module REPL where
|
|
|
|
|
|
|
|
import Eval
|
2025-01-01 18:05:21 -06:00
|
|
|
import FileEval
|
2024-12-20 11:38:09 -06:00
|
|
|
import Lexer
|
|
|
|
import Parser
|
|
|
|
import Research
|
|
|
|
|
2025-02-02 10:50:28 -06:00
|
|
|
import Control.Exception (SomeException, catch)
|
|
|
|
import Control.Monad.IO.Class (liftIO)
|
|
|
|
import Control.Monad.Catch (handle, MonadCatch)
|
|
|
|
import Control.Monad.Trans.Class (lift)
|
|
|
|
import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT)
|
|
|
|
import Data.Char (isSpace)
|
|
|
|
import Data.List ( dropWhile
|
|
|
|
, dropWhileEnd
|
|
|
|
, isPrefixOf)
|
2024-12-27 20:46:30 -06:00
|
|
|
import System.Console.Haskeline
|
2024-12-29 20:29:41 -06:00
|
|
|
|
|
|
|
import qualified Data.Map as Map
|
2024-12-20 11:38:09 -06:00
|
|
|
|
2024-12-29 21:49:57 -06:00
|
|
|
repl :: Env -> IO ()
|
2025-02-02 10:50:28 -06:00
|
|
|
repl env = runInputT settings (withInterrupt (loop env True))
|
2024-12-27 20:46:30 -06:00
|
|
|
where
|
2025-02-02 10:50:28 -06:00
|
|
|
settings :: Settings IO
|
|
|
|
settings = Settings
|
|
|
|
{ complete = completeWord Nothing " \t" completeCommands
|
|
|
|
, historyFile = Just ".tricu_history"
|
|
|
|
, autoAddHistory = True
|
|
|
|
}
|
|
|
|
|
|
|
|
completeCommands :: String -> IO [Completion]
|
|
|
|
completeCommands str = return $ map simpleCompletion $
|
|
|
|
filter (str `isPrefixOf`) commands
|
|
|
|
where
|
|
|
|
commands = ["!exit", "!decode", "!definitions", "!import"]
|
|
|
|
|
2025-01-30 14:19:30 -06:00
|
|
|
loop :: Env -> Bool -> InputT IO ()
|
|
|
|
loop env decode = handle (interruptHandler env decode) $ do
|
2024-12-29 08:29:25 -06:00
|
|
|
minput <- getInputLine "tricu < "
|
2025-01-27 16:46:41 -06:00
|
|
|
case minput of
|
|
|
|
Nothing -> outputStrLn "Exiting tricu"
|
|
|
|
Just s
|
2025-01-30 14:19:30 -06:00
|
|
|
| strip s == "" -> loop env decode
|
2025-01-27 16:46:41 -06:00
|
|
|
| strip s == "!exit" -> outputStrLn "Exiting tricu"
|
2025-01-30 14:19:30 -06:00
|
|
|
| strip s == "!decode" -> do
|
|
|
|
outputStrLn $ "Decoding " ++ (if decode then "disabled" else "enabled")
|
|
|
|
loop env (not decode)
|
2025-02-02 10:50:28 -06:00
|
|
|
| strip s == "!definitions" -> do
|
|
|
|
let defs = Map.keys $ Map.delete "!result" env
|
|
|
|
if null defs
|
|
|
|
then outputStrLn "No definitions discovered."
|
|
|
|
else do
|
|
|
|
outputStrLn "Available definitions:"
|
|
|
|
mapM_ outputStrLn defs
|
|
|
|
loop env decode
|
|
|
|
| "!import" `isPrefixOf` strip s -> handleImport env decode
|
2025-01-30 14:19:30 -06:00
|
|
|
| take 2 s == "--" -> loop env decode
|
2025-01-27 16:46:41 -06:00
|
|
|
| otherwise -> do
|
2025-01-30 14:19:30 -06:00
|
|
|
newEnv <- liftIO $ processInput env s decode `catch` errorHandler env
|
|
|
|
loop newEnv decode
|
2025-01-19 14:41:25 -06:00
|
|
|
|
2025-02-02 10:50:28 -06:00
|
|
|
handleImport :: Env -> Bool -> InputT IO ()
|
|
|
|
handleImport env decode = do
|
|
|
|
result <- runMaybeT $ do
|
|
|
|
let fileSettings = setComplete completeFilename defaultSettings
|
|
|
|
path <- MaybeT $ runInputT fileSettings $
|
|
|
|
getInputLineWithInitial "File path to load < " ("", "")
|
|
|
|
|
|
|
|
contents <- liftIO $ readFile (strip path)
|
|
|
|
|
|
|
|
if | Left err <- parseProgram (lexTricu contents) -> do
|
|
|
|
lift $ outputStrLn $ "Parse error: " ++ handleParseError err
|
|
|
|
MaybeT $ return Nothing
|
|
|
|
| Right ast <- parseProgram (lexTricu contents) -> do
|
|
|
|
ns <- MaybeT $ runInputT defaultSettings $
|
|
|
|
getInputLineWithInitial "Namespace (or !Local for no namespace) < " ("", "")
|
|
|
|
|
|
|
|
processedAst <- liftIO $ preprocessFile (strip path)
|
|
|
|
let namespacedAst | strip ns == "!Local" = processedAst
|
|
|
|
| otherwise = nsDefinitions (strip ns) processedAst
|
|
|
|
loadedEnv = evalTricu env namespacedAst
|
|
|
|
return loadedEnv
|
|
|
|
|
|
|
|
if | Nothing <- result -> do
|
|
|
|
outputStrLn "Import cancelled."
|
|
|
|
loop env decode
|
|
|
|
| Just loadedEnv <- result ->
|
|
|
|
loop (Map.delete "!result" loadedEnv) decode
|
|
|
|
|
2025-01-30 14:19:30 -06:00
|
|
|
interruptHandler :: Env -> Bool -> Interrupt -> InputT IO ()
|
|
|
|
interruptHandler env decode _ = do
|
2025-01-27 16:46:41 -06:00
|
|
|
outputStrLn "Interrupted with CTRL+C\n\
|
|
|
|
\You can use the !exit command or CTRL+D to exit"
|
2025-01-30 14:19:30 -06:00
|
|
|
loop env decode
|
2025-01-27 16:46:41 -06:00
|
|
|
|
2025-01-30 14:19:30 -06:00
|
|
|
processInput :: Env -> String -> Bool -> IO Env
|
|
|
|
processInput env input decode = do
|
2025-01-19 14:41:25 -06:00
|
|
|
let asts = parseTricu input
|
2024-12-30 14:19:43 -06:00
|
|
|
newEnv = evalTricu env asts
|
2025-01-30 14:19:30 -06:00
|
|
|
case Map.lookup "!result" newEnv of
|
|
|
|
Just r -> do
|
2025-02-02 10:50:28 -06:00
|
|
|
putStrLn $ "tricu > " ++
|
|
|
|
if decode
|
2025-01-30 14:19:30 -06:00
|
|
|
then decodeResult r
|
|
|
|
else show r
|
|
|
|
Nothing -> pure ()
|
2024-12-29 20:29:41 -06:00
|
|
|
return newEnv
|
2025-02-02 10:50:28 -06:00
|
|
|
|
2024-12-29 21:49:57 -06:00
|
|
|
errorHandler :: Env -> SomeException -> IO (Env)
|
2024-12-29 20:29:41 -06:00
|
|
|
errorHandler env e = do
|
|
|
|
putStrLn $ "Error: " ++ show e
|
|
|
|
return env
|
2025-02-02 10:50:28 -06:00
|
|
|
|
2025-01-02 19:08:14 -06:00
|
|
|
strip :: String -> String
|
|
|
|
strip = dropWhileEnd isSpace . dropWhile isSpace
|