summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJan Tuomi <jans.tuomi@gmail.com>2022-02-18 12:59:58 +0200
committerJan Tuomi <jans.tuomi@gmail.com>2022-02-18 12:59:58 +0200
commite8240a95aed965aa2ba82286283a47ef3afc59ca (patch)
tree8805467f3ee6bc7dab06e39f0d262d9956ef96ac /src
parentdc1ef25f1a7f8e36e93928e5b0288e203833e14a (diff)
Add some docs
Diffstat (limited to 'src')
-rw-r--r--src/LTypes.hs15
-rw-r--r--src/Main.hs16
2 files changed, 31 insertions, 0 deletions
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