-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathA4.hs
162 lines (144 loc) · 4.65 KB
/
A4.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
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
module A4 where
import Control.Applicative
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import A4Def
import ParserLib
-- This can help testing by reading from a file so you can test multi-line input
-- and also have little hassle with \
parseFile :: String -> IO (Maybe Expr)
parseFile filename = do
inp <- readFile filename
let ans = runParser mainParser inp
return ans
-- Used lesson4 from the lecture notes as a base
mainParser :: Parser Expr
mainParser = whitespaces *> expr <* eof
where
expr = cond <|> lambda <|> let' <|> infix'
cond = do
keyword "if"
ifBlock <- expr
keyword "then"
thenBlock <- expr
keyword "else"
elseBlock <- expr
pure (Cond ifBlock thenBlock elseBlock)
lambda = do
char '\\'
whitespaces
param <- var
char '-'
char '>'
whitespaces
body <- expr
pure (Lambda param body)
let' = do
keyword "let"
eqns <- many equation
keyword "in"
e <- expr
pure (Let eqns e)
equation = do
v <- var
char '=' *> whitespaces
e <- expr
char ';' *> whitespaces
pure (v, e)
infix' = chainl2 arith (string "==" *> whitespaces *> pure (Prim2 Eq))
<|> chainl2 arith (char '<' *> whitespaces *> pure (Prim2 Lt))
<|> arith
arith = chainl1 addend ((char '-' *> whitespaces *> pure (Prim2 Minus))
<|> (char '+' *> whitespaces *> pure (Prim2 Plus)))
addend = chainl1 factor ((char '*' *> whitespaces *> pure (Prim2 Mul))
<|> (char '/' *> whitespaces *> pure (Prim2 Div))
<|> (char '%' *> whitespaces *> pure (Prim2 Mod)))
factor = chainl1 atom (whitespaces *> pure (App)) <|> atom
atom = fmap Num integer
<|> keyword "True" *> pure (Bln True)
<|> keyword "False" *> pure (Bln False)
<|> between (char '(' *> whitespaces)
(char ')' *> whitespaces)
expr
<|> (fmap Var var)
var = identifier ["if", "then", "else", "let", "in", "True", "False"]
-- | Same as chainl1 except it only it's 0 or 1 not 1 or many
-- left-associative way.
chainl2 :: Parser a -- ^ operand parser
-> Parser (a -> a -> a) -- ^ operator parser
-> Parser a -- ^ evaluated answer
chainl2 arg op = do
a <- arg
optional a
where
optional x = do
f <- op
y <- arg
return (f x y)
-- Used lecture notes as a base for these function
mainInterp :: Expr -> Either Error Value
mainInterp = interp Map.empty
intOrDie :: Value -> Either Error Integer
intOrDie (VN i) = pure i
intOrDie _ = Left TypeError
boolOrDie :: Value -> Either Error Bool
boolOrDie (VB b) = pure b
boolOrDie _ = Left TypeError
interp :: Map String Value -> Expr -> Either Error Value
interp _ (Num i) = pure (VN i)
interp _ (Bln b) = pure (VB b)
interp env (Var v) = case Map.lookup v env of
Just a -> pure a
Nothing -> Left VarNotFound
interp env (Prim2 Eq e1 e2) = do
a <- interp env e1
b <- interp env e2
case a of
VB _ -> case b of
VB _ -> let i = boolOrDie a;
j = boolOrDie b;
in pure (VB (i == j))
VN _ -> Left TypeError
VN _ -> case b of
VB _ -> Left TypeError
VN _ -> let i = intOrDie a;
j = intOrDie b;
in pure (VB (i == j))
interp env (Prim2 op e1 e2) = do
a <- interp env e1
i <- intOrDie a
b <- interp env e2
j <- intOrDie b
case op of
Plus -> pure(VN (i + j))
Mul -> pure(VN (i * j))
Minus -> pure(VN (i - j))
Div -> case j == 0 of
False -> pure(VN (i `div` j))
True -> Left DivByZero
Mod -> pure(VN (mod i j))
Lt -> pure(VB (i < j))
interp env (Cond test eThen eElse) = do
a <- interp env test
case a of
VB True -> interp env eThen
VB False -> interp env eElse
_ -> Left TypeError
interp env (Let equations inClause) = do
extendedEqs <- extend env equations
interp extendedEqs inClause
where
extend env [] = pure env
extend env ((v, rhs) : equations) = do
a <- interp env rhs
let extendedEqs = Map.insert v a env
extend extendedEqs equations
interp env (Lambda v body) = pure (VClosure env v body)
interp env (App f e) = do
c <- interp env f
case c of
VClosure fEnv v body -> do
eVal <- interp env e
let bEnv = Map.insert v eVal fEnv
interp bEnv body
_ -> Left TypeError