blob: 74bed01a118bbf6c8ce313a41f05b38fb4f95b30 (
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
|
module Eval
( evalMain
) where
import Binary
import Control.Exception
import Control.Monad.State
import qualified Data.BitString as Bit
import qualified Data.ByteString as Byte
import Debug.Trace
import Helper
import Parser
import Reducer
import System.Console.Haskeline
import System.Environment
import System.Exit
import System.IO
import Text.Megaparsec hiding ( State
, try
)
loadFile :: String -> IO Environment
loadFile path = do
file <- try $ readFile path :: IO (Either IOError String)
case file of
Left exception -> print (exception :: IOError) >> pure []
Right file -> eval (filter (not . null) $ lines file) [] False
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 -> Bool -> IO Environment
eval [] env _ = pure env
eval (line : ls) env isRepl = case parse lineParser "BRUIJN" line of
Left err -> putStrLn (errorBundlePretty 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 -> putStrLn (show err) >> eval ls env' isRepl
Right _ -> if isRepl
then (putStrLn $ name <> " = " <> show exp) >> pure env'
else eval ls env' isRepl
Import path -> loadFile path
Evaluate exp ->
let (res, env') = evalExp exp `runState` env
in putStrLn
(case res of
Left err -> show err
Right exp ->
"<> "
<> (show exp)
<> "\n*> "
<> (show reduced)
<> "\t("
<> (show $ binaryToDecimal reduced)
<> ")"
where reduced = reduce exp
)
>> eval ls env isRepl
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 isRepl
_ -> eval ls env isRepl
where lineParser = if isRepl then parseReplLine else parseLine
evalFunc :: String -> Environment -> Maybe Expression
evalFunc func env = do
exp <- lookup func env
pure $ reduce exp
repl :: Environment -> InputT IO ()
repl env =
getInputLine "λ "
>>= (\case
Nothing -> pure ()
Just line -> (lift $ eval [line] env True) >>= repl
)
evalFile :: String -> (a -> IO ()) -> (Expression -> a) -> IO ()
evalFile path write conv = do
env <- loadFile path
case evalFunc "main" env of
Nothing -> putStrLn $ "ERROR: main function not found"
Just exp -> write $ conv exp
exec :: String -> (String -> IO (Either IOError a)) -> (a -> String) -> IO ()
exec path read conv = do
file <- read path
case file of
Left exception -> print (exception :: IOError)
Right file -> print $ reduce $ fromBinary $ conv file
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] -> evalFile path
(Byte.putStr . Bit.realizeBitStringStrict)
(toBitString . toBinary)
["-C", path] -> evalFile path putStrLn toBinary
["-e", path] ->
exec path (try . Byte.readFile) (fromBitString . Bit.bitString)
["-E", path] -> exec path (try . readFile) id
['-' : _] -> usage
[path ] -> evalFile path print id
|