blob: 47ca4ca0c8dc0a97a16d403132aad479af8e58b0 (
plain) (
blame)
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
module Eval
( evalMain
) where
import Control.Exception
import Control.Monad.State
import Debug.Trace
import Helper
import Parser
import Reducer
import System.Console.Haskeline
import System.Environment
import System.Exit
import System.IO
import Text.Parsec hiding ( State
, try
)
evalVar :: String -> Program (Failable Expression)
evalVar var = state $ \e ->
( case lookup var e of
Nothing -> Left $ UndeclaredFunction var
Just x -> Right x
, e
)
evalApp :: Expression -> Expression -> Program (Failable Expression)
evalApp f g =
evalExp f
>>= (\case
Left e -> pure $ Left e
Right f' -> fmap (Application f') <$> evalExp g
)
evalExp :: Expression -> Program (Failable Expression)
evalExp idx@(Bruijn _ ) = pure $ Right idx
evalExp ( Variable var) = evalVar var
evalExp ( Abstraction exp) = evalExp exp >>= pure . fmap Abstraction
evalExp ( Application f g) = evalApp f g
-- TODO: Duplicate function error
evalDefine :: String -> Expression -> Program (Failable Expression)
evalDefine name exp =
evalExp exp
>>= (\case
Left e -> pure $ Left e
Right f -> modify ((name, f) :) >> pure (Right f)
)
eval :: [String] -> Environment -> IO Environment
eval [] env = pure env
eval (line : ls) env = case parse parseLine "FILE" line of
Left err -> print err >> pure env
Right instr -> case instr of
Define name exp ->
let (res, env') = evalDefine name exp `runState` env
in case res of
Left err -> print err >> eval ls env'
Right _ -> (putStrLn $ name <> " = " <> show exp) >> eval ls env'
_ -> eval ls env
evalFunc :: String -> Environment -> IO Environment
evalFunc func env = case lookup func env of
Nothing -> (putStrLn $ func <> " not found") >> pure env
Just exp -> (print $ reduce exp) >> pure env
-- TODO: Less duplicate code (liftIO?)
evalRepl :: String -> Environment -> InputT IO Environment
evalRepl line env = case parse parseReplLine "REPL" line of
Left err -> outputStrLn (show err) >> pure env
Right instr -> case instr of
Define name exp ->
let (res, env') = evalDefine name exp `runState` env
in case res of
Left err -> outputStrLn (show err) >> pure env'
Right _ -> (outputStrLn $ name <> " = " <> show exp) >> pure env'
Evaluate exp ->
let (res, env') = evalExp exp `runState` env
in outputStrLn
(case res of
Left err -> show err
Right exp ->
"<> "
<> (show exp)
<> "\n*> "
<> (show reduced)
<> "\t("
<> (show $ ternaryToDecimal reduced)
<> ")"
where reduced = reduce exp
)
>> pure env
Load path ->
liftIO
$ (try $ readFile path :: IO (Either IOError String))
>>= (\case -- TODO: Make this more abstract and reusable
Left exception -> print (exception :: IOError) >> pure env
Right file -> eval (filter (not . null) $ lines file) [] >>= pure
)
_ -> pure env
evalFile :: String -> IO ()
evalFile path = do
file <- try $ readFile path :: IO (Either IOError String)
case file of
Left exception -> print (exception :: IOError)
Right file ->
eval (filter (not . null) $ lines file) []
>>= evalFunc "main"
>> return ()
repl :: Environment -> InputT IO ()
repl env =
getInputLine "λ "
>>= (\case
Nothing -> pure ()
Just line -> evalRepl line env >>= repl
)
usage :: IO ()
usage = putStrLn "Invalid arguments. Use 'bruijn [file]' instead"
evalMain :: IO ()
evalMain = do
args <- getArgs
case args of
[] -> runInputT defaultSettings { historyFile = Just ".bruijn-history" }
$ repl []
[path] -> evalFile path
_ -> usage
|