-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathParser.elm
374 lines (299 loc) · 5.92 KB
/
Parser.elm
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
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
module Monkey.Parser exposing
( Program(..), Stmt(..), Expr(..), UnaryOp(..), BinOp(..), Id, Block
, Error
, parse
)
import Monkey.Lexer exposing (..)
import Parser as P exposing ((|=), (|.), Parser)
type Program
= Program (List Stmt)
type Stmt
= Let Id Expr
| Return Expr
| ExprStmt Expr
type Expr
= Var Id
| Num Int
| Bool Bool
| String String
| Array (List Expr)
| Hash (List (Expr, Expr))
| Prefix UnaryOp Expr
| Infix BinOp Expr Expr
| Call Expr (List Expr)
| Index Expr Expr
| If Expr Block (Maybe Block)
| Function (List Id) Block
type UnaryOp
= Not
| Negate
type BinOp
= Equal
| NotEqual
| LessThan
| GreaterThan
| Add
| Sub
| Mul
| Div
type alias Id = String
type alias Block = List Stmt
type alias Error = List P.DeadEnd
parse : String -> Result Error Program
parse = P.run program
program : Parser Program
program =
P.succeed Program
|. spaces
|= many stmt
|. P.end
stmt : Parser Stmt
stmt =
P.oneOf
[ letStmt
, returnStmt
, exprStmt
]
letStmt : Parser Stmt
letStmt =
P.succeed Let
|. rLet
|= identifier
|. equal
|= expr
|. semicolon
returnStmt : Parser Stmt
returnStmt =
P.succeed Return
|. rReturn
|= expr
|. semicolon
exprStmt : Parser Stmt
exprStmt =
P.succeed ExprStmt
|= expr
|. optional semicolon
expr : Parser Expr
expr = equality
equality : Parser Expr
equality =
binary comparison <|
P.oneOf
[ P.map (always (Infix Equal)) doubleEqual
, P.map (always (Infix NotEqual)) bangEqual
]
comparison : Parser Expr
comparison =
binary term <|
P.oneOf
[ P.map (always (Infix LessThan)) lessThan
, P.map (always (Infix GreaterThan)) greaterThan
]
-- Term ::= Term ( '+' | '-' ) Factor | Factor
--
-- Consider, t ::= t '+' f | f. We need to eliminate the left-recursion.
--
-- The production generates the following:
--
-- f, f + f, (f + f) + f, ((f + f) + f) + f
--
-- The parentheses are only there to indicate the associativity, left in this
-- case.
--
-- We can rewrite it as:
--
-- t ::= f X
-- X ::= '+' f | ε
--
-- which is equivalent to:
--
-- t ::= f ('+' f)*
--
-- However, it generates the following:
--
-- f, f + f, f + (f + f), f + (f + (f + f))
--
-- i.e. associativity to the right. We need to account for that.
term : Parser Expr
term =
binaryLeftAssoc factor <|
P.oneOf
[ P.map (always (Infix Add)) plus
, P.map (always (Infix Sub)) hyphen
]
factor : Parser Expr
factor =
binaryLeftAssoc unary <|
P.oneOf
[ P.map (always (Infix Mul)) asterisk
, P.map (always (Infix Div)) slash
]
unary : Parser Expr
unary =
P.oneOf
[ P.succeed (<|)
|= P.oneOf
[ P.map (always (Prefix Not)) bang
, P.map (always (Prefix Negate)) hyphen
]
|= P.lazy (\_ -> unary)
, operator
]
-- Operator ::= Operator ( Call | Index ) | Primary
--
-- This production generates the following:
--
-- x, x(...), x[...], x(...)(...), x(...)[...], x[...](...), x[...][...], etc.
--
-- N.B. x(1)(2)(3) means (((x(1))(2))(3).
--
-- Let's remove the left-recursion to get:
--
-- Operator ::= Primary ( Call | Index )*
--
-- Then, we need to do a little extra processing to get the meaning we want.
operator : Parser Expr
operator =
let
callOrIndex =
P.oneOf
[ P.map (flip Call) call
, P.map (flip Index) index
]
call =
parens <| P.lazy (\_ -> expr)
index =
P.succeed identity
|. leftSquareBracket
|= P.lazy (\_ -> expr)
|. rightSquareBracket
in
P.succeed (List.foldl (<|))
|= primary
|= many callOrIndex
primary : Parser Expr
primary =
P.oneOf
[ var
, num
, bool
, str
, array
, hash
, ifExpr
, function
, group
]
var : Parser Expr
var =
P.map Var identifier
num : Parser Expr
num =
P.map Num number
bool : Parser Expr
bool =
P.map Bool boolean
str : Parser Expr
str =
P.map String string
array : Parser Expr
array =
P.lazy (\_ -> expr)
|> squares
|> P.map Array
hash : Parser Expr
hash =
let
keyValue =
P.succeed Tuple.pair
|= P.lazy (\_ -> expr)
|. colon
|= P.lazy (\_ -> expr)
in
brackets keyValue
|> P.map Hash
ifExpr : Parser Expr
ifExpr =
P.succeed If
|. rIf
|. leftParen
|= P.lazy (\_ -> expr)
|. rightParen
|= block
|= optional
( P.succeed identity
|. rElse
|= block
)
function : Parser Expr
function =
P.succeed Function
|. rFn
|= params
|= block
group : Parser Expr
group =
P.succeed identity
|. leftParen
|= P.lazy (\_ -> expr)
|. rightParen
block : Parser (List Stmt)
block =
P.succeed identity
|. leftBracket
|= many (P.lazy (\_ -> stmt))
|. rightBracket
params : Parser (List Id)
params =
parens identifier
many : Parser a -> Parser (List a)
many p =
let
helper rev =
P.oneOf
[ P.succeed (\x -> P.Loop (x :: rev))
|= p
, P.succeed ()
|> P.map (\_ -> P.Done (List.reverse rev))
]
in
P.loop [] helper
optional : Parser a -> Parser (Maybe a)
optional p =
P.oneOf
[ P.map Just p
, P.succeed Nothing
]
binary : Parser a -> Parser (a -> a -> a) -> Parser a
binary p op =
let
buildExpr left maybeRest =
case maybeRest of
Just (f, right) ->
f left right
Nothing ->
left
in
P.succeed buildExpr
|= p
|= optional
( P.succeed Tuple.pair
|= op
|= p
)
binaryLeftAssoc : Parser a -> Parser (a -> a -> a) -> Parser a
binaryLeftAssoc p op =
let
buildLeftAssocExpr =
List.foldl (\(f, y) x -> f x y)
in
P.succeed buildLeftAssocExpr
|= p
|= many
( P.succeed Tuple.pair
|= op
|= p
)
flip : (a -> b -> c) -> b -> a -> c
flip f b a =
f a b