From e8240a95aed965aa2ba82286283a47ef3afc59ca Mon Sep 17 00:00:00 2001 From: Jan Tuomi Date: Fri, 18 Feb 2022 12:59:58 +0200 Subject: Add some docs --- src/LTypes.hs | 15 +++++++++++++++ src/Main.hs | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) (limited to 'src') diff --git a/src/LTypes.hs b/src/LTypes.hs index e3f9b59..2d921da 100644 --- a/src/LTypes.hs +++ b/src/LTypes.hs @@ -5,6 +5,8 @@ import Data.Map (Map) import qualified Data.Map as M import Utils +-- | An LWord is a typed value that can read from the source and +-- pushed onto the stack. LWords are the core data structure. data LWord = LSymbol String | LInteger Integer @@ -16,10 +18,15 @@ data LWord | LPhrase [LWord] deriving (Show, Eq) +-- | An LWordT represents the type of an 'LWord'. Each LWord has one corresponding LWordT, +-- expect for the LWordT AnyT, which represents any LWord value. data LWordT = LSymbolT | LIntegerT | LFloatT | LBoolT | LCharT | LLabelT | LPhraseT | AnyT deriving (Show) +-- | Produce an exception referencing an expected 'LWordT' and the encountered 'LWord' consumeErr wordT word = LException $ "expected " ++ show wordT ++ ", encountered " ++ show word +-- | Attempt to consume one 'LWord' from the supplied stack. If the word at the top of the stack matches +-- the supplied 'LWordT', return that word and the updated stack. If not, return an exception. consume1 :: LWordT -> [LWord] -> ExceptT LException IO (LWord, [LWord]) consume1 wordT [] = throwError $ LException $ "expected " ++ show wordT ++ ", encountered empty stack" @@ -39,12 +46,14 @@ consume1 wordT@LLabelT (word : stack') = consume1 wordT@LPhraseT (word : stack') = case word of LPhrase _ -> pure (word, stack'); _ -> throwError $ consumeErr wordT word +-- | Invokes 'consume1' twice. consume2 :: LWordT -> LWordT -> [LWord] -> ExceptT LException IO (LWord, LWord, [LWord]) consume2 wordT1 wordT2 stack = do (ret1, stack1) <- consume1 wordT1 stack (ret2, stack2) <- consume1 wordT2 stack1 pure (ret1, ret2, stack2) +-- | Invokes 'consume1' three times. consume3 :: LWordT -> LWordT -> LWordT -> [LWord] -> ExceptT LException IO (LWord, LWord, LWord, [LWord]) consume3 wordT1 wordT2 wordT3 stack = do (ret1, stack1) <- consume1 wordT1 stack @@ -52,6 +61,7 @@ consume3 wordT1 wordT2 wordT3 stack = do (ret3, stack3) <- consume1 wordT3 stack2 pure (ret1, ret2, ret3, stack3) +-- | Convert an 'LWord' to a string representation. reprWord :: LWord -> String reprWord (LSymbol a) = a reprWord (LInteger a) = show a @@ -62,6 +72,11 @@ reprWord (LLabel a) = a reprWord (LStringLitRef a) = "StrLit(" ++ a ++ ")" reprWord (LPhrase a) = "P[ " ++ map reprWord a $> unwords ++ " ]" +-- | Represents the interpreter state. The state changes one processed 'LWord' at a time. +-- Contains: +-- * 'lDict': A mapping from 'LLabel' string values to 'LWord'. Used for storing variables. +-- * 'lStack': A list of 'LWord's, representing the global stack. The first element is the top of the stack. +-- * 'lDefs': A mapping from 'LSymbol' string values to phrases of 'LWord's. Used for defining custom words. data LState = LState { lDict :: Map String LWord, lStack :: [LWord], diff --git a/src/Main.hs b/src/Main.hs index 1673663..293ac22 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -14,6 +14,8 @@ import System.Exit (exitFailure, exitSuccess) import System.IO (BufferMode (NoBuffering), hSetBuffering, stdin) import Utils +-- | Parse command line options and positional arguments, and +-- apply them to the supplied config getConfig config [] = config getConfig config ("--debug" : rest) = let newConfig = config {configDebugMode = True} @@ -22,12 +24,18 @@ getConfig config (fileName : rest) = let newConfig = config {configFileNameM = Just fileName} in getConfig newConfig rest +-- | Extract source file name from config. If not defined, +-- throw Exception getFileName :: Config -> ExceptT LException IO String getFileName Config {configFileNameM = fileNameM} = do case fileNameM of Just str -> pure str Nothing -> throwError $ LException "no filename specified" +-- | Setup config based on args, +-- parse the source, +-- construct initial interpreter state, +-- and start interpreting by calling interpretSource bootstrap :: [String] -> ExceptT LException IO () bootstrap args = do let initialConfig = @@ -39,6 +47,7 @@ bootstrap args = do let config = getConfig initialConfig args fileName <- getFileName config + -- Print debug information about the parse result if in debug mode debugPrint config $ fmt "executing file %%\n" [fileName] source <- liftIO . readFile $ fileName (parsedSource, strLitRefMap) <- parseSource source @@ -51,6 +60,8 @@ bootstrap args = do $> M.toList .> map reprKeyValPair .> intercalate "\n" ] debugPrint config "interpreter output:" + + -- Construct initial state let initialState = LState { lDict = M.empty, @@ -60,8 +71,13 @@ bootstrap args = do lSource = parsedSource, lStrLitRefMap = strLitRefMap } + + -- Start interpreting source void $ interpretSource config initialState +-- | Entrypoint: read args and call bootstrap +-- Upon exception, the exception will be printed to stdout and the +-- program will be terminated with a non-zero status main :: IO () main = do hSetBuffering stdin NoBuffering -- cgit v1.3