-
Notifications
You must be signed in to change notification settings - Fork 0
/
Main.hs
93 lines (79 loc) · 3.08 KB
/
Main.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import qualified Data.Map as M
import Control.Applicative
import Control.Monad.Trans.Class
import Data.Maybe
import System.Directory
import Control.Exception
import qualified System.FilePath as FP
import Data.Version
import Control.Monad
import qualified Text.PrettyPrint.Leijen as P
import Text.PrettyPrint.Leijen ((<//>), (</>), Pretty, pretty, displayS, renderPretty)
import System.Console.Haskeline as H
import Paths_unitcalc
import Eval
import Types
import Parse
import Util
dataDir = getAppUserDataDirectory "unitcalc"
dataFile filename = (FP.</> filename) <$> dataDir
historyLoc = dataFile "history.txt"
addedUnitsLoc = dataFile "addedUnits.txt"
main = do
putStrLn $ "unitcalc " ++ showVersion version ++ ", by Nathan Stoddard"
createDirectoryIfMissing True =<< dataDir
stdlibFilename <- stdlibLoc
addedUnitsFilename <- addedUnitsLoc
historyFilename <- historyLoc
addedUnitsExists <- doesFileExist addedUnitsFilename
env <- loadFile stdlibFilename emptyEnv
env <- if addedUnitsExists then join <$> mapM (loadFile addedUnitsFilename) env else pure env
case env of
Left err -> putStrLn err
Right env -> void $ runInputT (Settings noCompletion (Just historyFilename) True) $ repl env
repl :: Env -> InputT IO Env
repl env = do
input <- replGetInput Nothing
case input of
Nothing -> pure env
Just input -> do
let stmt = parseInput "" input parseReplCmd
case stmt of
Left err -> lift (putStrLn err) >> repl env
Right (RLoad path) -> loadFileRepl path env
Right (RStmt stmt) -> case evalStmt stmt env of
Left err -> lift (putStrLn err) >> repl env
Right (res, env') -> do
case stmt of
SExpr {} -> pure ()
_ -> lift $ do
addedUnitsFilename <- addedUnitsLoc
appendFile addedUnitsFilename (input ++ "\n")
lift $ putStrLn (prettyPrint res)
repl env'
loadFileRepl path env = do
env' <- lift $ loadFile path env
case env' of
Left err -> lift (putStrLn err) >> repl env
Right env' -> repl env'
handleCtrlC = H.handle . ctrlC where
ctrlC :: a -> AsyncException -> InputT IO a
ctrlC def UserInterrupt = pure def
ctrlC def e = lift (putStrLn $ "Unknown exception: " ++ show e) >> pure def
replGetInput cont = do
let prompt = if isJust cont then "... " else "> "
input_ <- handleCtrlC (Just "") $ getInputLine prompt
if isNothing input_ then pure Nothing else do
let input = fromJust input_
if null input || input == "exit" then pure Nothing else do
let
input' = case cont of
Just cont -> cont ++ "\n" ++ input
Nothing -> input
if countParens input' > 0 then replGetInput (Just input')
else pure (Just input')
countParens [] = 0
countParens (x:xs)
| x `elem` "(" = countParens xs + 1
| x `elem` ")" = countParens xs - 1
| otherwise = countParens xs