-
Notifications
You must be signed in to change notification settings - Fork 0
/
parser.ml
336 lines (275 loc) · 9.64 KB
/
parser.ml
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
open Scanner
open Expression
open Statement
type parser = {
tokens: token array;
mutable current: int;
}
let make_parser tokens = { tokens = Array.of_list tokens; current = 0; }
let at_end parser = parser.current >= Array.length parser.tokens - 1
let previous parser = Array.get parser.tokens (parser.current - 1)
let peek parser = Array.get parser.tokens parser.current
let advance parser =
if not (at_end parser) then
parser.current <- parser.current + 1;
previous parser
let advance_ parser = ignore (advance parser)
let check parser token_type =
if at_end parser then
false
else
(peek parser).token_type == token_type
let rec matches parser token_types =
match token_types with
| [] -> false
| t::ts -> if check parser t then begin
advance_ parser;
true
end else
matches parser ts
exception ParseError
exception Break
let error token message =
(* We can't override Error.error, so we inline that definition here *)
if token.token_type = Eof then
Error.report token.line " at end" message
else
Error.report token.line (" at '" ^ token.lexem ^ "'") message;
ParseError
let consume parser token_type message =
if check parser token_type then
advance parser
else
raise (error (peek parser) message)
let consume_ parser token_type message =
ignore (consume parser token_type message)
let synchronize parser =
advance_ parser;
try
while not (at_end parser) do
if (previous parser).token_type = Semicolon then
raise Break
else
match (peek parser).token_type with
| Class | Fun | Var | For | If | While | Print | Return -> raise Break
| _ -> advance_ parser;
done
with
| Break -> ()
let rec binary next_precedence token_types parser =
let expr = ref (next_precedence parser) in
while matches parser token_types do
let bin_op = previous parser in
let right = next_precedence parser in
expr := Binary { left = !expr; right; bin_op }
done;
!expr
and logical next_precedence token_type parser =
let expr = ref (next_precedence parser) in
while matches parser [token_type] do
let bin_op = previous parser in
let right = next_precedence parser in
expr := Logical { left = !expr; right; bin_op }
done;
!expr
and unary parser =
if matches parser [Bang; Minus] then
let unary_op = previous parser in
(* We're recursing here, maybe that's not intended? *)
let opperand = unary parser in
Unary { unary_op; opperand }
else
call parser
and call parser =
let expr = ref (primary parser) in
let continue = ref true in
while !continue do
if matches parser [LeftParen] then
expr := finishCall parser !expr
else
continue := false
done;
!expr
and finishCall parser callee =
let arguments = ref [] in
let count = ref 0 in
if not (check parser RightParen) then begin
arguments := expression parser :: !arguments;
count := !count + 1;
while matches parser [Comma] do
if !count >= 8 then
(* We intentionally don't throw to match the Java version, but that's
maybe not the best call here. *)
ignore (error (peek parser) "Cannot have more than 8 arguments.");
count := !count + 1;
arguments := expression parser :: !arguments;
done;
end;
let paren = consume parser RightParen "Expect ')' after arguments." in
Call { callee; paren; arguments = !arguments }
and primary parser =
let token = advance parser in
match token.token_type with
| False -> Literal { token; value = LoxBool false }
| True -> Literal { token; value = LoxBool true }
| Nil -> Literal { token; value = LoxNil }
| Number -> Literal { token; value = LoxNumber (float_of_string token.lexem) }
| String -> Literal { token; value = LoxString (token.lexem) }
| Identifier -> Variable token
| LeftParen ->
let expr = expression parser in
consume_ parser RightParen "Expect ')' after expression.";
Grouping { expression = expr }
| _ -> raise (error token "Expect expression.")
and multiplication parser = binary unary [Slash; Star] parser
and addition parser = binary multiplication [Minus; Plus] parser
and comparison parser = binary addition [Greater; GreaterEqual; Less; LessEqual] parser
and equality parser = binary comparison [BangEqual; EqualEqual] parser
and expression parser = assignment parser
and or_expr parser = logical and_expr Or parser
and and_expr parser = logical equality And parser
and assignment parser =
let expr = or_expr parser in
if matches parser [Equal] then
let equals = previous parser in
let new_value = assignment parser in
match expr with
| Variable name -> Assign { name; new_value }
| _ -> raise (error equals "Invalid assingment target.")
else
expr
and print_statement parser =
let e = expression parser in
consume_ parser Semicolon "Expect ';' after value.";
Print e
and return_statment parser =
let keyword = advance parser in
let value = if check parser Semicolon then
None
else
Some (expression parser)
in
consume_ parser Semicolon "Expected ';' after return value.";
Statement.Return { keyword; value }
and expression_statement parser =
let e = expression parser in
consume_ parser Semicolon "Expect ';' after expression.";
Expression e
and var_declaration parser =
let name = consume parser Identifier "Expect variable name." in
let initalizer = if matches parser [Equal] then Some (expression parser) else None in
consume_ parser Semicolon "Expect ';' after variable declaration.";
VarDecl { name; initalizer }
(* renamed to avoid `funciton` keyword in Ocaml. *)
and function_declaration parser kind =
let func_name = consume parser Identifier ("Expect " ^ kind ^ " name.") in
consume_ parser LeftParen ("Expect '(' after " ^ kind ^ " name.");
let parameters = ref [] in
let count = ref 0 in
if not (check parser RightParen) then begin
parameters := (consume parser Identifier "Expect parameter name.") :: !parameters;
count := !count + 1;
while matches parser [Comma] do
if !count >= 8 then
Error.error (peek parser).line "Cannot have more than 8 parameters.";
parameters := (consume parser Identifier "Expect parameter name.") :: !parameters;
count := !count + 1;
done;
end;
let params = List.rev !parameters in
consume_ parser RightParen "Expect ')' after parameters name.";
let func_body = block parser in
Function { func_name; params; func_body }
and block parser =
let open_brace = consume parser LeftBrace ("Expect '{' before block.") in
let statements = ref [] in
while not (at_end parser) && not (check parser RightBrace) do
match declaration parser with
| Some s -> statements := s :: !statements
| None -> ()
done;
consume_ parser RightBrace "Expect '}' after block.";
let statements = List.rev !statements in
{ statements; open_brace }
and if_stmnt parser =
consume_ parser LeftParen "Expect '(' after an 'if'.";
let condition = expression parser in
consume_ parser RightParen "Expect ')' after an if condition.";
let true_branch = statement parser in
let false_branch = if matches parser [Else] then
Some (statement parser)
else
None
in
IfStmnt { condition; true_branch; false_branch }
and while_stmnt parser =
consume_ parser LeftParen "Expect '(' after an 'if'.";
let loop_condition = expression parser in
consume_ parser RightParen "Expect ')' after an if condition.";
let body = statement parser in
While { body; loop_condition }
and for_stmnt parser =
(* This may be a bit much *)
consume_ parser LeftParen "Expect '(' after 'for'.";
let initalizer = if matches parser [Semicolon] then
None
else if matches parser [Var] then
Some (var_declaration parser)
else
Some (expression_statement parser)
in
let condition = if not (check parser Semicolon) then
Some (expression parser)
else
None
in
advance_ parser;
let increment = if not (check parser Semicolon) then
Some (expression parser)
else
None
in
consume_ parser RightParen "Expect ')' after for clauses.";
let body = ref (statement parser) in
(* body = { body; incrment }; *)
begin match increment with
| Some inc -> body := Block { open_brace = location !body; statements = [ !body; Expression inc ]; };
| None -> ()
end;
(* body = while (condition) {body ; increment } *)
begin match condition with
| Some loop_condition -> body := While { loop_condition; body = !body };
| None -> ()
end;
(* body = { initalizer; while (condition) {body ; increment } } *)
begin match initalizer with
| Some init -> body := Block { open_brace = location init; statements = [ init; !body ] };
| None -> ()
end;
!body
and declaration parser: stmnt option =
try
match (peek parser).token_type with
| Var -> advance_ parser; Some (var_declaration parser)
| Fun -> advance_ parser; Some (function_declaration parser "function")
| _ -> Some (statement parser)
with
| ParseError ->
synchronize parser; None
and statement parser =
match (peek parser).token_type with
| Print -> advance_ parser; print_statement parser
| Return -> return_statment parser
| LeftBrace -> Block (block parser)
| If -> advance_ parser; if_stmnt parser
| While -> advance_ parser; while_stmnt parser
| For -> advance_ parser; for_stmnt parser
| _ -> expression_statement parser
and parse parser =
let statements = ref [] in
while not (at_end parser) do
match declaration parser with
| None -> ()
| Some s -> statements := s :: !statements;
done;
List.rev !statements