-
Notifications
You must be signed in to change notification settings - Fork 1
/
ExprParser.hs
347 lines (277 loc) · 8.72 KB
/
ExprParser.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
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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude #-}
module ExprParser where
import Data.Text as T
import Import hiding (many, try, (<|>))
import Scanner
import Text.Parsec
-- https://craftinginterpreters.com/parsing-expressions.html
-- expression → assignment ;
-- assignment → IDENTIFIER "=" assignment
-- | equality ;
-- equality → comparison ( ( "!=" | "==" ) comparison )* ;
-- comparison → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
-- term → factor ( ( "-" | "+" ) factor )* ;
-- factor → unary ( ( "/" | "*" ) unary )* ;
-- unary → ( "!" | "-" ) unary
-- | primary ;
-- primary → NUMBER | STRING | "true" | "false" | "nil"
-- | "(" expression ")" ;
-- expression → literal
-- | unary
-- | binary
-- | grouping ;
-- literal → NUMBER | STRING | "true" | "false" | "nil" ;
-- grouping → "(" expression ")" ;
-- unary → ( "-" | "!" ) expression ;
-- binary → expression operator expression ;
-- operator → "==" | "!=" | "<" | "<=" | ">" | ">="
-- | "+" | "-" | "*" | "/" ;
type LoxParserResult = Either ParseError Expr
data BinOp = NotEqual | EqualEqual | Gt | Gte | Lt | Lte | Plus | Minus | Star | Slash
deriving (Show, Eq)
data UnaryOp = UnaryMinus | UnaryBang deriving (Show, Eq)
data LogicOp = And | Or deriving (Show, Eq)
type Program = [Declaration]
data Declaration = DeclVar Decl | DeclStatement Statement deriving (Show, Eq)
data Decl = Decl T.Text (Maybe Expr) deriving (Show, Eq)
data Statement = StmtExpr Expr | StmtPrint Expr | StmtIf IfElse | StmtBlock [Declaration]
| StmtWhile While
deriving (Show, Eq)
data IfElse = IfElse Expr Statement (Maybe Statement) deriving (Show, Eq)
data While = While Expr Statement deriving (Show, Eq)
data Expr
= Number Double
| Literal T.Text
| Identifier T.Text
| LoxBool Bool
| LoxNil
| Paren Expr
| Unary UnaryOp Expr
| Binary Expr BinOp Expr
| Assignment T.Text Expr
| Logical Expr LogicOp Expr
deriving (Show, Eq)
-- satisfy = tokenPrim (t -> String) (SourcePos -> t -> s -> SourcePos) (t -> Maybe a)
-- satisfy :: (Stream s m Char) => (Char -> Bool) -> ParsecT s u m Char
-- satisfy f = tokenPrim (\c -> show [c])
-- (\pos c _cs -> updatePosChar pos c)
-- (\c -> if f c then Just c else Nothing)
type Parser a = ParsecT [LoxTokInfo] () Identity a
satisfyT :: (LoxTokInfo -> Maybe a) -> Parser a
satisfyT f = tokenPrim showTok updateTokPos match
where
showTok ti = show $ tokinfo_type ti
updateTokPos _ _ (s : _) = tok_position s
updateTokPos pos _ [] = pos
match t = f t
-- this is similar to chainl in `Text.Parsec` but works on `BinOp`
-- adopted from https://jakewheat.github.io/intro_to_parsing/
leftChain :: Parser Expr -> Parser BinOp -> Parser Expr
leftChain p op = do
expr <- p
maybeAddSuffix expr
where
addSuffix e0 = do
op' <- op
e1 <- p
maybeAddSuffix (Binary e0 op' e1)
maybeAddSuffix e = addSuffix e <|> return e
leftChainLogic :: Parser Expr -> Parser LogicOp -> Parser Expr
leftChainLogic p op = do
expr <- p
maybeAddSuffix expr
where
addSuffix e0 = do
op' <- op
e1 <- p
maybeAddSuffix (Logical e0 op' e1)
maybeAddSuffix e = addSuffix e <|> return e
-- primary
number :: Parser Expr
number = satisfyT f
where
f (LoxTokInfo (NUMBER x) _ _ _) = Just (Number x)
f _ = Nothing
literal :: Parser Expr
literal = satisfyT f
where
f (LoxTokInfo (STRING x) _ _ _) = Just (Literal $ T.pack x)
f _ = Nothing
loxIdentifier :: Parser Expr
loxIdentifier = satisfyT f
where
f (LoxTokInfo (IDENTIFIER x) _ _ _) = Just (Identifier $ T.pack x)
f _ = Nothing
loxBool :: Parser Expr
loxBool = satisfyT f
where
f (LoxTokInfo TRUE _ _ _) = Just (LoxBool True)
f (LoxTokInfo FALSE _ _ _) = Just (LoxBool False)
f _ = Nothing
loxNil :: Parser Expr
loxNil = satisfyT f
where
f (LoxTokInfo NIL _ _ _) = Just LoxNil
f _ = Nothing
loxParenExpr :: Parser Expr
loxParenExpr = do
satisfyT parenOpen *> loxExpr <* satisfyT parenClose
where
-- use LoxNil as placeholder, since we do not have an equilivalent Expr for Paren
parenOpen (LoxTokInfo LEFT_PAREN _ _ _) = Just ()
parenOpen _ = Nothing
parenClose (LoxTokInfo RIGHT_PAREN _ _ _) = Just ()
parenClose _ = Nothing
loxPrimary :: Parser Expr
loxPrimary = number <|> literal <|> loxBool <|> loxNil <|> loxParenExpr <|> loxIdentifier
unary' :: Parser Expr
unary' = Unary <$> satisfyT f <*> unary
where
f (LoxTokInfo BANG _ _ _) = Just UnaryBang
f (LoxTokInfo MINUS _ _ _) = Just UnaryMinus
f _ = Nothing
unary :: Parser Expr
unary = unary' <|> loxPrimary
factor :: Parser Expr
factor = leftChain unary (satisfyT f)
where
f x = case tokinfo_type x of
STAR -> Just Star
SLASH -> Just Slash
_ -> Nothing
term :: Parser Expr
term = leftChain factor (satisfyT f)
where
f x = case tokinfo_type x of
MINUS -> Just Minus
PLUS -> Just Plus
_ -> Nothing
comparison :: Parser Expr
comparison = leftChain term (satisfyT f)
where
f x = case tokinfo_type x of
GREATER -> Just Gt
GREATER_EQUAL -> Just Gte
LESS -> Just Lt
LESS_EQUAL -> Just Lte
_ -> Nothing
equality :: Parser Expr
equality = leftChain comparison (satisfyT f)
where
f x = case tokinfo_type x of
BANG_EQUAL -> Just NotEqual
EQUAL_EQUAL -> Just EqualEqual
_ -> Nothing
assignment :: Parser Expr
assignment = do
name <- satisfyT identifier -- for this version this will suffice
void $ satisfyT equals
rhs <- try assignment <|> equality
return $ Assignment name rhs
where
equals x = case tokinfo_type x of
EQUAL -> Just ()
_ -> Nothing
identifier (LoxTokInfo (IDENTIFIER x) _ _ _) = Just (T.pack x)
identifier _ = Nothing
loxExpr :: Parser Expr
loxExpr = try assignment <|> loxLogicOr
loxLogicOr :: Parser Expr
loxLogicOr = leftChainLogic loxLogicAnd (satisfyT f)
where
f x = case tokinfo_type x of
OR -> Just Or
_ -> Nothing
loxLogicAnd :: Parser Expr
loxLogicAnd = leftChainLogic equality (satisfyT f)
where
f x = case tokinfo_type x of
AND -> Just And
_ -> Nothing
semi :: Parser ()
semi = satisfyT f
where
f x = case tokinfo_type x of
SEMICOLON -> Just ()
_ -> Nothing
loxPrintStmt :: Parser Expr
loxPrintStmt = do
void $ satisfyT f
loxExpr
where
f x = case tokinfo_type x of
PRINT -> Just ()
_ -> Nothing
ifStmt :: Parser Statement
ifStmt = do
void $ satisfyT if_keyword
condition <- loxParenExpr
if_statement <- loxStatement
else_statement <- optionMaybe elseStmt
return $ StmtIf $ IfElse condition if_statement else_statement
where
if_keyword x = case tokinfo_type x of
IF -> Just ()
_ -> Nothing
else_keyword x = case tokinfo_type x of
ELSE -> Just ()
_ -> Nothing
elseStmt = do
void $ satisfyT else_keyword
loxStatement
whileStmt :: Parser Statement
whileStmt = do
void $ satisfyT while_keyword
condition <- loxParenExpr
statement <- loxStatement
return $ StmtWhile $ While condition statement
where
while_keyword x = case tokinfo_type x of
WHILE -> Just ()
_ -> Nothing
loxStatement :: Parser Statement
loxStatement = StmtExpr <$> (try loxExpr <* semi) <|> StmtPrint <$> (try loxPrintStmt <* semi) <|> try ifStmt <|> try whileStmt <|> loxBlock
loxBlock :: Parser Statement
loxBlock = do
void $ satisfyT left_brace
prog <- loxProgram
void $ satisfyT right_brace
return $ StmtBlock prog
where
left_brace x = case tokinfo_type x of
LEFT_BRACE -> Just ()
_ -> Nothing
right_brace x = case tokinfo_type x of
RIGHT_BRACE -> Just ()
_ -> Nothing
loxDeclStatment :: Parser Declaration
loxDeclStatment = DeclStatement <$> loxStatement
loxAssignment :: Parser Expr
loxAssignment = do
satisfyT f
loxExpr
where
f x = case tokinfo_type x of
EQUAL -> Just ()
_ -> Nothing
loxDeclaration :: Parser Declaration
loxDeclaration = do
void $ satisfyT f
var_name <- satisfyT fi
expr <- optionMaybe loxAssignment
void semi
return $ DeclVar $ Decl var_name expr
where
f x = case tokinfo_type x of
VAR -> Just ()
_ -> Nothing
fi x = case tokinfo_type x of
IDENTIFIER ix -> Just (T.pack ix)
_ -> Nothing
loxDeclarations :: Parser Declaration
loxDeclarations = try loxDeclaration <|> DeclStatement <$> loxStatement
loxProgram :: Parser Program
loxProgram = many1 loxDeclarations -- endBy1 loxDeclarations semi
scannerLoxTokens :: [LoxTokInfo] -> LoxParserResult
scannerLoxTokens = parse loxExpr ""