forked from rescript-lang/rescript
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser_flow.ml
407 lines (366 loc) · 13.3 KB
/
parser_flow.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
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
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
module Sedlexing = Flow_sedlexing
module Ast = Flow_ast
open Token
open Parser_env
open Parser_common
let filter_duplicate_errors =
let module PrintableErrorSet = Set.Make (struct
type t = Loc.t * Parse_error.t
let compare (a_loc, a_error) (b_loc, b_error) =
let loc = Loc.compare a_loc b_loc in
if loc = 0 then
Parse_error.compare a_error b_error
else
loc
end) in
fun errs ->
let errs = List.rev errs in
let (_, deduped) =
List.fold_left
(fun (set, deduped) err ->
if PrintableErrorSet.mem err set then
(set, deduped)
else
(PrintableErrorSet.add err set, err :: deduped))
(PrintableErrorSet.empty, [])
errs
in
List.rev deduped
module rec Parse : PARSER = struct
module Type = Type_parser.Type (Parse)
module Declaration = Declaration_parser.Declaration (Parse) (Type)
module Pattern_cover = Pattern_cover.Cover (Parse)
module Expression = Expression_parser.Expression (Parse) (Type) (Declaration) (Pattern_cover)
module Object = Object_parser.Object (Parse) (Type) (Declaration) (Expression) (Pattern_cover)
module Statement =
Statement_parser.Statement (Parse) (Type) (Declaration) (Object) (Pattern_cover)
module Pattern = Pattern_parser.Pattern (Parse) (Type)
module JSX = Jsx_parser.JSX (Parse)
let identifier ?restricted_error env =
(match Peek.token env with
| T_LET when in_strict_mode env -> error env Parse_error.StrictReservedWord
| T_LET when no_let env -> error_unexpected env
| T_LET -> ()
| T_AWAIT when allow_await env -> error env Parse_error.UnexpectedReserved
| T_AWAIT -> ()
| T_YIELD when allow_yield env -> error env Parse_error.UnexpectedReserved
| T_YIELD when in_strict_mode env -> error env Parse_error.StrictReservedWord
| T_YIELD -> ()
| t when token_is_strict_reserved t -> strict_error env Parse_error.StrictReservedWord
| t when token_is_reserved t -> error_unexpected env
| t ->
(match restricted_error with
| Some err when token_is_restricted t -> strict_error env err
| _ -> ()));
identifier_name env
let rec program env =
let leading = Eat.program_comments env in
let stmts = module_body_with_directives env (fun _ -> false) in
let end_loc = Peek.loc env in
Expect.token env T_EOF;
let loc =
match stmts with
| [] -> end_loc
| _ -> Loc.btwn (fst (List.hd stmts)) (fst (List.hd (List.rev stmts)))
in
let all_comments = List.rev (comments env) in
( loc,
{
Ast.Program.statements = stmts;
comments = Flow_ast_utils.mk_comments_opt ~leading ();
all_comments;
} )
and directives =
let check env token =
match token with
| T_STRING (loc, _, _, octal) ->
if octal then strict_error_at env (loc, Parse_error.StrictOctalLiteral)
| _ -> failwith ("Nooo: " ^ token_to_string token ^ "\n")
in
let rec statement_list env term_fn item_fn (string_tokens, stmts) =
match Peek.token env with
| T_EOF -> (env, string_tokens, stmts)
| t when term_fn t -> (env, string_tokens, stmts)
| T_STRING _ as string_token ->
let possible_directive = item_fn env in
let stmts = possible_directive :: stmts in
(match possible_directive with
| (_, Ast.Statement.Expression { Ast.Statement.Expression.directive = Some raw; _ }) ->
let strict = in_strict_mode env || raw = "use strict" in
let string_tokens = string_token :: string_tokens in
statement_list (env |> with_strict strict) term_fn item_fn (string_tokens, stmts)
| _ -> (env, string_tokens, stmts))
| _ -> (env, string_tokens, stmts)
in
fun env term_fn item_fn ->
let env = with_allow_directive true env in
let (env, string_tokens, stmts) = statement_list env term_fn item_fn ([], []) in
let env = with_allow_directive false env in
List.iter (check env) (List.rev string_tokens);
(env, stmts)
and module_item env =
let decorators = Object.decorator_list env in
match Peek.token env with
| T_EXPORT -> Statement.export_declaration ~decorators env
| T_IMPORT ->
error_on_decorators env decorators;
let statement =
match Peek.ith_token ~i:1 env with
| T_LPAREN
| T_PERIOD ->
Statement.expression env
| _ -> Statement.import_declaration env
in
statement
| T_DECLARE when Peek.ith_token ~i:1 env = T_EXPORT ->
error_on_decorators env decorators;
Statement.declare_export_declaration env
| _ -> statement_list_item env ~decorators
and module_body_with_directives env term_fn =
let (env, directives) = directives env term_fn module_item in
let stmts = module_body ~term_fn env in
List.fold_left (fun acc stmt -> stmt :: acc) stmts directives
and module_body =
let rec module_item_list env term_fn acc =
match Peek.token env with
| T_EOF -> List.rev acc
| t when term_fn t -> List.rev acc
| _ -> module_item_list env term_fn (module_item env :: acc)
in
(fun ~term_fn env -> module_item_list env term_fn [])
and statement_list_with_directives ~term_fn env =
let (env, directives) = directives env term_fn statement_list_item in
let stmts = statement_list ~term_fn env in
let stmts = List.fold_left (fun acc stmt -> stmt :: acc) stmts directives in
(stmts, in_strict_mode env)
and statement_list =
let rec statements env term_fn acc =
match Peek.token env with
| T_EOF -> List.rev acc
| t when term_fn t -> List.rev acc
| _ -> statements env term_fn (statement_list_item env :: acc)
in
(fun ~term_fn env -> statements env term_fn [])
and statement_list_item ?(decorators = []) env =
if not (Peek.is_class env) then error_on_decorators env decorators;
let open Statement in
match Peek.token env with
| T_LET -> let_ env
| T_CONST -> const env
| _ when Peek.is_function env -> Declaration._function env
| _ when Peek.is_class env -> class_declaration env decorators
| T_INTERFACE -> interface env
| T_DECLARE -> declare env
| T_TYPE -> type_alias env
| T_OPAQUE -> opaque_type env
| T_ENUM when (parse_options env).enums -> Declaration.enum_declaration env
| _ -> statement env
and statement env =
let open Statement in
match Peek.token env with
| T_EOF ->
error_unexpected ~expected:"the start of a statement" env;
(Peek.loc env, Ast.Statement.Empty { Ast.Statement.Empty.comments = None })
| T_SEMICOLON -> empty env
| T_LCURLY -> block env
| T_VAR -> var env
| T_BREAK -> break env
| T_CONTINUE -> continue env
| T_DEBUGGER -> debugger env
| T_DO -> do_while env
| T_FOR -> for_ env
| T_IF -> if_ env
| T_RETURN -> return env
| T_SWITCH -> switch env
| T_THROW -> throw env
| T_TRY -> try_ env
| T_WHILE -> while_ env
| T_WITH -> with_ env
| T_ELSE -> if_ env
| T_COLON
| T_RPAREN
| T_RCURLY
| T_RBRACKET
| T_COMMA
| T_PERIOD
| T_PLING_PERIOD
| T_ARROW
| T_IN
| T_INSTANCEOF
| T_CATCH
| T_FINALLY
| T_CASE
| T_DEFAULT
| T_EXTENDS
| T_STATIC
| T_EXPORT
| T_ELLIPSIS ->
error_unexpected ~expected:"the start of a statement" env;
Eat.token env;
statement env
| _ when Peek.is_function env ->
let func = Declaration._function env in
function_as_statement_error_at env (fst func);
func
| T_LET when Peek.ith_token ~i:1 env = T_LBRACKET ->
let loc = Loc.btwn (Peek.loc env) (Peek.ith_loc ~i:1 env) in
error_at env (loc, Parse_error.AmbiguousLetBracket);
Statement.expression env
| _ when Peek.is_identifier env -> maybe_labeled env
| _ when Peek.is_class env ->
error_unexpected env;
Eat.token env;
Statement.expression env
| _ -> Statement.expression env
and expression env =
let start_loc = Peek.loc env in
let expr = Expression.assignment env in
match Peek.token env with
| T_COMMA -> Expression.sequence env ~start_loc [expr]
| _ -> expr
and expression_or_pattern env =
let start_loc = Peek.loc env in
let expr_or_pattern = Expression.assignment_cover env in
match Peek.token env with
| T_COMMA ->
let expr = Pattern_cover.as_expression env expr_or_pattern in
let seq = Expression.sequence env ~start_loc [expr] in
Cover_expr seq
| _ -> expr_or_pattern
and conditional = Expression.conditional
and assignment = Expression.assignment
and left_hand_side = Expression.left_hand_side
and object_initializer = Object._initializer
and object_key = Object.key
and class_declaration = Object.class_declaration
and class_expression = Object.class_expression
and is_assignable_lhs = Expression.is_assignable_lhs
and number = Expression.number
and identifier_with_type =
let with_loc_helper no_optional restricted_error env =
let name = identifier ~restricted_error env in
let optional = (not no_optional) && Peek.token env = T_PLING in
if optional then (
if not (should_parse_types env) then error env Parse_error.UnexpectedTypeAnnotation;
Expect.token env T_PLING
);
let annot = Type.annotation_opt env in
let open Ast.Pattern.Identifier in
{ name; optional; annot }
in
fun env ?(no_optional = false) restricted_error ->
with_loc (with_loc_helper no_optional restricted_error) env
and block_body env =
let start_loc = Peek.loc env in
let leading = Peek.comments env in
Expect.token env T_LCURLY;
let term_fn t = t = T_RCURLY in
let body = statement_list ~term_fn env in
let end_loc = Peek.loc env in
let internal =
if body = [] then
Peek.comments env
else
[]
in
Expect.token env T_RCURLY;
let trailing = Eat.trailing_comments env in
( Loc.btwn start_loc end_loc,
{
Ast.Statement.Block.body;
comments = Flow_ast_utils.mk_comments_with_internal_opt ~leading ~trailing ~internal ();
} )
and function_block_body ~expression env =
let start_loc = Peek.loc env in
let leading = Peek.comments env in
Expect.token env T_LCURLY;
let term_fn t = t = T_RCURLY in
let (body, strict) = statement_list_with_directives ~term_fn env in
let end_loc = Peek.loc env in
let internal =
if body = [] then
Peek.comments env
else
[]
in
Expect.token env T_RCURLY;
let trailing =
match (expression, Peek.token env) with
| (true, _)
| (_, (T_RCURLY | T_EOF)) ->
Eat.trailing_comments env
| _ when Peek.is_line_terminator env -> Eat.comments_until_next_line env
| _ -> []
in
( Loc.btwn start_loc end_loc,
{
Ast.Statement.Block.body;
comments = Flow_ast_utils.mk_comments_with_internal_opt ~leading ~trailing ~internal ();
},
strict )
and jsx_element_or_fragment = JSX.element_or_fragment
and pattern = Pattern.pattern
and pattern_from_expr = Pattern.from_expr
end
let do_parse env parser fail =
let ast = parser env in
let error_list = filter_duplicate_errors (errors env) in
if fail && error_list <> [] then raise (Parse_error.Error error_list);
(ast, error_list)
let with_eof parser env =
let ast = parser env in
Expect.token env T_EOF;
ast
let parse_statement env fail = do_parse env (with_eof Parse.statement_list_item) fail
let parse_expression env fail = do_parse env (with_eof Parse.expression) fail
let parse_program fail ?(token_sink = None) ?(parse_options = None) filename content =
let env = init_env ~token_sink ~parse_options filename content in
do_parse env Parse.program fail
let program ?(fail = true) ?(token_sink = None) ?(parse_options = None) content =
parse_program fail ~token_sink ~parse_options None content
let program_file ?(fail = true) ?(token_sink = None) ?(parse_options = None) content filename =
parse_program fail ~token_sink ~parse_options filename content
let json_file ?(fail = true) ?(token_sink = None) ?(parse_options = None) content filename =
let env = init_env ~token_sink ~parse_options filename content in
match Peek.token env with
| T_LBRACKET
| T_LCURLY
| T_STRING _
| T_NUMBER _
| T_TRUE
| T_FALSE
| T_NULL ->
do_parse env Parse.expression fail
| T_MINUS ->
(match Peek.ith_token ~i:1 env with
| T_NUMBER _ -> do_parse env Parse.expression fail
| _ ->
error_unexpected ~expected:"a number" env;
raise (Parse_error.Error (errors env)))
| _ ->
let errs =
match errors env with
| [] ->
error_unexpected ~expected:"a valid JSON value" env;
errors env
| errs -> errs
in
raise (Parse_error.Error errs)
let jsx_pragma_expression =
let left_hand_side env =
let ast = Parse.left_hand_side (with_no_new true env) in
Expect.token env T_EOF;
ast
in
fun content filename ->
let env = init_env ~token_sink:None ~parse_options:None filename content in
do_parse env left_hand_side true
let string_is_valid_identifier_name str =
let lexbuf = Sedlexing.Utf8.from_string str in
Flow_lexer.is_valid_identifier_name lexbuf