blob: a5941c1e26f281284d8902908c181ef582e75e58 (
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
module Eval
( evalMain
) where
import Binary
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)
)
evalTest :: Expression -> Expression -> Program (Failable Instruction)
evalTest exp1 exp2 =
evalExp exp1
>>= (\case
Left exp1 -> pure $ Left exp1
Right exp1 -> fmap (Test exp1) <$> evalExp exp2
)
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 _ -> eval ls env'
Test exp1 exp2 ->
let (res, _) = evalTest exp1 exp2 `runState` env
in case res of
Left err -> putStrLn (show err) >> pure env
Right (Test exp1' exp2') ->
when
(reduce exp1' /= reduce exp2')
( putStrLn
$ "ERROR: test failed: "
<> (show exp1)
<> " != "
<> (show exp2)
)
>> eval ls env
_ -> eval ls env
evalFunc :: String -> Environment -> Maybe Expression
evalFunc func env = do
exp <- lookup func env
pure $ reduce exp
-- TODO: Less duplicate code (liftIO?)
-- TODO: Generally improve eval code
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 $ binaryToDecimal 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
)
Test exp1 exp2 ->
let (res, _) = evalTest exp1 exp2 `runState` env
in case res of
Left err -> outputStrLn (show err) >> pure env
Right (Test exp1' exp2') ->
when
(reduce exp1' /= reduce exp2')
( outputStrLn
$ "ERROR: test failed: "
<> (show exp1)
<> " != "
<> (show exp2)
)
>> pure env
_ -> 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 -> do
env <- eval (filter (not . null) $ lines file) []
case evalFunc "main" env of
Nothing -> putStrLn $ "main function not found"
Just exp -> print exp
compile :: String -> IO ()
compile path = do
file <- try $ readFile path :: IO (Either IOError String)
case file of
Left exception -> print (exception :: IOError)
Right file -> do
env <- eval (filter (not . null) $ lines file) []
case lookup "main" env of
Nothing -> putStrLn $ "main function not found"
Just exp -> putStrLn $ toBinary exp
exec :: String -> IO ()
exec path = do
file <- try $ readFile path :: IO (Either IOError String)
case file of
Left exception -> print (exception :: IOError)
Right file -> print $ reduce $ fromBinary file
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 []
["-c", path] -> compile path -- TODO: -C: raw binary
["-e", path] -> exec path -- TODO: -E: raw binary
[path] -> evalFile path
_ -> usage
|