Cleaning up and abstracting the (FFP to AST) lowerer
[hiphop-php.git] / hphp / hack / src / parser / full_fidelity_statement_parser.ml
blobe08616b1a7e532c00069ac88f359975b791a842c
1 (**
2 * Copyright (c) 2016, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
9 *)
10 module WithSyntax(Syntax: Syntax_sig.Syntax_S) = struct
11 module Token = Syntax.Token
12 module Trivia = Token.Trivia
13 module TriviaKind = Full_fidelity_trivia_kind
14 module SyntaxKind = Full_fidelity_syntax_kind
15 module TokenKind = Full_fidelity_token_kind
16 module SourceText = Full_fidelity_source_text
17 module SyntaxError = Full_fidelity_syntax_error
18 module SimpleParserSyntax =
19 Full_fidelity_simple_parser.WithSyntax(Syntax)
20 module SimpleParser = SimpleParserSyntax.WithLexer(
21 Full_fidelity_lexer.WithToken(Syntax.Token))
23 module ParserHelperSyntax = Full_fidelity_parser_helpers.WithSyntax(Syntax)
24 module ParserHelper = ParserHelperSyntax
25 .WithLexer(Full_fidelity_lexer.WithToken(Syntax.Token))
26 module type SCWithKind_S = SmartConstructorsWrappers.SyntaxKind_S
28 module type ExpressionParser_S = Full_fidelity_expression_parser_type
29 .WithSyntax(Syntax)
30 .WithLexer(Full_fidelity_lexer.WithToken(Syntax.Token))
31 .ExpressionParser_S
33 module type DeclarationParser_S = Full_fidelity_declaration_parser_type
34 .WithSyntax(Syntax)
35 .WithLexer(Full_fidelity_lexer.WithToken(Syntax.Token))
36 .DeclarationParser_S
38 module type TypeParser_S = Full_fidelity_type_parser_type
39 .WithSyntax(Syntax)
40 .WithLexer(Full_fidelity_lexer.WithToken(Syntax.Token))
41 .TypeParser_S
43 module type StatementParser_S = Full_fidelity_statement_parser_type
44 .WithSyntax(Syntax)
45 .WithLexer(Full_fidelity_lexer.WithToken(Syntax.Token))
46 .StatementParser_S
48 open TokenKind
49 open Syntax
51 module WithSmartConstructors (SCI : SCWithKind_S with module Token = Syntax.Token)
52 = struct
54 module WithExpressionAndDeclAndTypeParser
55 (ExpressionParser : ExpressionParser_S with module SC = SCI)
56 (DeclParser : DeclarationParser_S with module SC = SCI)
57 (TypeParser : TypeParser_S with module SC = SCI) :
58 (StatementParser_S with module SC = SCI) = struct
60 module Parser = SimpleParser.WithSmartConstructors (SCI)
61 include Parser
62 include ParserHelper.WithParser(Parser)
64 let parse_type_specifier parser =
65 let type_parser =
66 TypeParser.make
67 parser.env
68 parser.lexer
69 parser.errors
70 parser.context
71 parser.sc_state in
72 let (type_parser, spec) = TypeParser.parse_type_specifier type_parser in
73 let env = TypeParser.env type_parser in
74 let lexer = TypeParser.lexer type_parser in
75 let errors = TypeParser.errors type_parser in
76 let context = TypeParser.context type_parser in
77 let sc_state = TypeParser.sc_state type_parser in
78 let parser = { env; lexer; errors; context; sc_state } in
79 (parser, spec)
81 let with_expr_parser
82 : 'a . t -> (ExpressionParser.t -> ExpressionParser.t * 'a) -> t * 'a
83 = fun parser f ->
84 let expr_parser =
85 ExpressionParser.make
86 parser.env
87 parser.lexer
88 parser.errors
89 parser.context
90 parser.sc_state in
91 let (expr_parser, node) = f expr_parser in
92 let env = ExpressionParser.env expr_parser in
93 let lexer = ExpressionParser.lexer expr_parser in
94 let errors = ExpressionParser.errors expr_parser in
95 let context = ExpressionParser.context expr_parser in
96 let sc_state = ExpressionParser.sc_state expr_parser in
97 let parser = { env; lexer; errors; context; sc_state } in
98 (parser, node)
100 let parse_expression parser =
101 with_expr_parser parser ExpressionParser.parse_expression
103 let with_decl_parser : 'a . t -> (DeclParser.t -> DeclParser.t * 'a) -> t * 'a
104 = fun parser f ->
105 let decl_parser =
106 DeclParser.make
107 parser.env
108 parser.lexer
109 parser.errors
110 parser.context
111 parser.sc_state
113 let (decl_parser, node) = f decl_parser in
114 let env = DeclParser.env decl_parser in
115 let lexer = DeclParser.lexer decl_parser in
116 let errors = DeclParser.errors decl_parser in
117 let context = DeclParser.context decl_parser in
118 let sc_state = DeclParser.sc_state decl_parser in
119 let parser = { env; lexer; errors; context; sc_state } in
120 (parser, node)
122 let rec parse_statement parser =
123 match peek_token_kind parser with
124 | Async
125 | Coroutine
126 | Function -> parse_possible_php_function parser
127 | Abstract
128 | Final
129 | Interface
130 | Trait
131 | Class -> parse_php_class parser
132 | Fallthrough -> parse_possible_erroneous_fallthrough parser
133 | For -> parse_for_statement parser
134 | Foreach -> parse_foreach_statement parser
135 | Do -> parse_do_statement parser
136 | While -> parse_while_statement parser
137 | Declare -> parse_declare_statement parser
138 | Using ->
139 let missing = make_missing parser in
140 parse_using_statement parser missing
141 | Await when peek_token_kind ~lookahead:1 parser = Using ->
142 let parser, await_kw = assert_token parser Await in
143 parse_using_statement parser await_kw
144 | If -> parse_if_statement parser
145 | Switch -> parse_switch_statement parser
146 | Try -> parse_try_statement parser
147 | Break -> parse_break_statement parser
148 | Continue -> parse_continue_statement parser
149 | Return -> parse_return_statement parser
150 | Throw -> parse_throw_statement parser
151 | LeftBrace -> parse_compound_statement parser
152 | Static ->
153 parse_function_static_declaration_or_expression_statement parser
154 | Echo -> parse_echo_statement parser
155 | Global -> parse_global_statement_or_expression_statement parser
156 | Unset -> parse_unset_statement parser
157 | Case ->
158 let (parser, result) = parse_case_label parser in
159 (* TODO: This puts the error in the wrong place. We should highlight
160 the entire label, not the trailing colon. *)
161 let parser = with_error parser SyntaxError.error2003 in
162 (parser, result)
163 | Default ->
164 let (parser, result) = parse_default_label parser in
165 (* TODO: This puts the error in the wrong place. We should highlight
166 the entire label, not the trailing colon. *)
167 let parser = with_error parser SyntaxError.error2004 in
168 (parser, result)
169 | Name when peek_token_kind ~lookahead:1 parser = Colon ->
170 parse_goto_label parser
171 | Goto -> parse_goto_statement parser
172 | QuestionGreaterThan ->
173 let (p, s, _) = parse_markup_section parser ~is_leading_section:false in
174 (p, s)
175 | Semicolon -> parse_expression_statement parser
176 (* ERROR RECOVERY: when encountering a token that's invalid now but the
177 * context says is expected later, make the whole statement missing
178 * and continue on, starting at the unexpected token. *)
179 (* TODO T20390825: Make sure this this won't cause premature recovery. *)
180 | kind when Parser.expects parser kind ->
181 let missing = make_missing parser in
182 (parser, missing)
183 | _ -> parse_expression_statement parser
185 and parse_markup_section parser ~is_leading_section =
186 let parser, prefix =
187 (* for markup section at the beginning of the file
188 treat ?> as a part of markup text *)
189 (* The closing ?> tag is not legal hack, but accept it here and give an
190 error in a later pass *)
191 if not is_leading_section
192 && peek_token_kind parser = TokenKind.QuestionGreaterThan then
193 let (parser, prefix) = next_token parser in
194 parser, make_token prefix
195 else
196 let missing = make_missing parser in
197 (parser, missing)
199 let parser, markup, suffix_opt = scan_markup parser ~is_leading_section in
200 let markup = make_token markup in
201 let (suffix, is_echo_tag, has_suffix) =
202 match suffix_opt with
203 | Some (less_than_question, language_opt) ->
204 let less_than_question_token = make_token less_than_question in
205 (* if markup section ends with <?= tag
206 then script section embedded between tags should be treated as if it
207 will be an argument to 'echo'. Technically it should be restricted to
208 expression but since it permits trailing semicolons we parse it as
209 expression statement.
210 TODO: consider making it even more loose and parse it as declaration
211 for better error recovery in cases when user
212 accidentally type '<?=' instead of '<?php' so declaration in script
213 section won't throw parser off the rails. *)
214 let language, is_echo_tag =
215 match language_opt with
216 | Some language ->
217 make_token language, (Token.kind language = TokenKind.Equal)
218 | None -> make_missing parser, false
220 make_markup_suffix less_than_question_token language,
221 is_echo_tag, true
222 | None -> make_missing parser, false, false
224 let parser, expression =
225 if is_echo_tag then parse_statement parser
226 else
227 let missing = make_missing parser in
228 (parser, missing)
230 let s = make_markup_section prefix markup suffix expression in
231 parser, s, has_suffix
233 and parse_possible_php_function parser =
234 (* ERROR RECOVERY: PHP supports nested named functions, but Hack does not.
235 (Hack only supports anonymous nested functions as expressions.)
237 If we have a statement beginning with function left-paren, then parse it
238 as a statement expression beginning with an anonymous function; it will
239 then have to end with a semicolon.
241 If it starts with something else, parse it as a function.
243 TODO: Give an error for nested nominal functions in a later pass.
246 let kind0 = peek_token_kind ~lookahead:0 parser in
247 let kind1 = peek_token_kind ~lookahead:1 parser in
248 match kind0, kind1 with
249 | Async, Function
250 when peek_token_kind ~lookahead:2 parser = LeftParen ->
251 parse_expression_statement parser
252 | Coroutine, Function
253 when peek_token_kind ~lookahead:2 parser = LeftParen ->
254 parse_expression_statement parser
255 | Function, LeftParen (* Verbose-style lambda *)
256 | (Async | Coroutine), LeftParen (* Async / coroutine, compact-style lambda *)
257 | Async, LeftBrace (* Async block *)
258 -> parse_expression_statement parser
259 | _ -> with_decl_parser parser DeclParser.parse_function
261 and parse_php_class parser =
262 (* PHP allows classes nested inside of functions, but hack does not *)
263 (* TODO check for hack error: no classish declarations inside functions *)
264 let missing = make_missing parser in
265 with_decl_parser parser
266 (fun parser -> DeclParser.parse_classish_declaration parser missing)
268 (* Helper: parses ( expr ) *)
269 and parse_paren_expr parser =
270 let (parser, left_paren) = require_left_paren parser in
271 let (parser, expr_syntax) = parse_expression parser in
272 let (parser, right_paren) = require_right_paren parser in
273 (parser, left_paren, expr_syntax, right_paren)
275 and parse_for_statement parser =
276 (* SPEC
277 for-statement:
278 for ( for-initializer-opt ; for-control-opt ; \
279 for-end-of-loop-opt ) statement
281 Each clause is an optional, comma-separated list of expressions.
282 Note that unlike most such lists in Hack, it may *not* have a trailing
283 comma.
284 TODO: There is no compelling reason to not allow a trailing comma
285 from the grammatical point of view. Each clause unambiguously ends in
286 either a semi or a paren, so we can allow a trailing comma without
287 difficulty.
290 let parser, for_keyword_token = assert_token parser For in
291 let parser, for_left_paren = require_left_paren parser in
292 let parser, for_initializer_expr = parse_comma_list_opt
293 parser Semicolon SyntaxError.error1015 parse_expression in
294 let parser, for_first_semicolon = require_semicolon parser in
295 let parser, for_control_expr = parse_comma_list_opt
296 parser Semicolon SyntaxError.error1015 parse_expression in
297 let parser, for_second_semicolon = require_semicolon parser in
298 let parser, for_end_of_loop_expr = parse_comma_list_opt
299 parser RightParen SyntaxError.error1015 parse_expression in
300 let parser, for_right_paren = require_right_paren parser in
301 let parser, for_statement =
302 let _, open_token = next_token parser in
303 match Token.kind open_token with
304 | Colon -> parse_alternate_loop_statement parser ~terminator:Endfor
305 | _ -> parse_statement parser in
306 let syntax = make_for_statement for_keyword_token for_left_paren
307 for_initializer_expr for_first_semicolon for_control_expr
308 for_second_semicolon for_end_of_loop_expr for_right_paren for_statement
310 (parser, syntax)
312 and parse_foreach_statement parser =
313 let parser, foreach_keyword_token = assert_token parser Foreach in
314 let parser, foreach_left_paren = require_left_paren parser in
315 let parser, foreach_collection_name = parse_expression parser in
316 let parser, await_token = optional_token parser Await in
317 let parser, as_token = require_as parser in
318 (* let (parser1, token) = next_token parser in *)
319 let (parser, after_as) = parse_expression parser in
320 let parser = Parser.expect_in_new_scope parser [ RightParen ] in
321 let (parser, foreach_key, foreach_arrow, foreach_value) =
322 match Token.kind (peek_token parser) with
323 | RightParen ->
324 let missing1 = make_missing parser in
325 let missing2 = make_missing parser in
326 (parser, missing1, missing2, after_as)
327 | EqualGreaterThan ->
328 let parser, arrow = assert_token parser EqualGreaterThan in
329 let parser, value = parse_expression parser in
330 (parser, after_as, arrow, value)
331 | _ ->
332 (* TODO ERROR RECOVERY. Now assumed that the arrow is missing
333 * and goes on to parse the next expression *)
334 let parser, token = next_token parser in
335 let parser, foreach_value = parse_expression parser in
336 (parser, after_as, make_error (make_token token), foreach_value)
338 let parser, right_paren_token = require_right_paren parser in
339 let parser = Parser.pop_scope parser [ RightParen ] in
340 let parser, foreach_statement =
341 let _, open_token = next_token parser in
342 match Token.kind open_token with
343 | Colon -> parse_alternate_loop_statement parser ~terminator:Endforeach
344 | _ -> parse_statement parser in
345 let syntax =
346 make_foreach_statement foreach_keyword_token foreach_left_paren
347 foreach_collection_name await_token as_token foreach_key foreach_arrow
348 foreach_value right_paren_token foreach_statement in
349 (parser, syntax)
351 and parse_do_statement parser =
352 let (parser, do_keyword_token) =
353 assert_token parser Do in
354 let (parser, statement_node) =
355 parse_statement parser in
356 let (parser, do_while_keyword_token) = require_while parser in
357 let (parser, left_paren_token, expr_node, right_paren_token) =
358 parse_paren_expr parser in
359 let (parser, do_semicolon_token) = require_semicolon parser in
360 let syntax = make_do_statement do_keyword_token statement_node
361 do_while_keyword_token left_paren_token expr_node right_paren_token
362 do_semicolon_token in
363 (parser, syntax)
365 and parse_while_statement parser =
366 let (parser, while_keyword_token) =
367 assert_token parser While in
368 let (parser, left_paren_token, expr_node, right_paren_token) =
369 parse_paren_expr parser in
370 let (parser, statement_node) =
371 let _, open_token = next_token parser in
372 match Token.kind open_token with
373 | Colon -> parse_alternate_loop_statement parser ~terminator:Endwhile
374 | _ -> parse_statement parser in
375 let syntax = make_while_statement while_keyword_token left_paren_token
376 expr_node right_paren_token statement_node in
377 (parser, syntax)
379 (* SPEC:
380 declare-statement:
381 declare ( expression ) ;
382 declare ( expression ) compound-statement
384 TODO: Update the specification of the grammar
386 and parse_declare_statement parser =
387 let (parser, declare_keyword_token) =
388 assert_token parser Declare in
389 let (parser, left_paren_token, expr_node, right_paren_token) =
390 parse_paren_expr parser in
391 if peek_token_kind parser = Semicolon then
392 let (parser, semi) = assert_token parser Semicolon in
393 parser, make_declare_directive_statement
394 declare_keyword_token left_paren_token expr_node right_paren_token semi
395 else
396 let (parser, statement_node) = parse_statement parser in
397 parser, make_declare_block_statement
398 declare_keyword_token
399 left_paren_token
400 expr_node
401 right_paren_token
402 statement_node
404 (* SPEC:
405 using-statement:
406 await-opt using expression ;
407 await-opt using ( expression-list ) compound-statement
409 TODO: Update the specification of the grammar
411 and parse_using_statement parser await_kw =
412 let (parser, using_kw) = assert_token parser Using in
413 (* Decision point - Are we at a function scope or a body scope *)
414 let token_kind = peek_token_kind parser in
415 (* if next token is left paren it can be either
416 - parenthesized expression followed by semicolon for function scoped using
417 - comma separated list of expressions wrapped in parens for blocks.
418 To distinguish between then try parse parenthesized expression and then
419 check next token. NOTE: we should not use 'parse_expression' here
420 since it might parse (expr) { smth() } as subscript expression $expr{$index}
422 let (parser1, expr) =
423 if token_kind = LeftParen then
424 with_expr_parser parser
425 ExpressionParser.parse_cast_or_parenthesized_or_lambda_expression
426 else parse_expression parser in
427 let (parser1, token) = next_token parser1 in
428 match Token.kind token with
429 | Semicolon ->
430 let semi = make_token token in
431 parser1, make_using_statement_function_scoped await_kw using_kw expr semi
432 | _ ->
433 let (parser, left_paren) = require_left_paren parser in
434 let (parser, expressions) = parse_comma_list
435 parser RightParen SyntaxError.error1015 parse_expression in
436 let (parser, right_paren) = require_right_paren parser in
437 let (parser, statements) = parse_statement parser in
438 parser, make_using_statement_block_scoped
439 await_kw using_kw left_paren expressions right_paren statements
441 and parse_unset_statement parser =
443 TODO: This is listed as unsupported in Hack in the spec; is that true?
444 TODO: If it is formally supported in Hack then update the spec; if not
445 TODO: then should we make it illegal in strict mode?
446 TODO: Can the list be comma-terminated?
447 TODO: Can the list be empty?
448 TODO: The list has to be expressions which evaluate as variables;
449 add an error checking pass.
450 TODO: Unset is case-insentive. Should non-lowercase be an error?
452 let (parser, keyword) = assert_token parser Unset in
453 let (parser, left_paren, variables, right_paren) =
454 parse_parenthesized_comma_list_opt_allow_trailing
455 parser parse_expression in
456 let (parser, semi) = require_semicolon parser in
457 let result = make_unset_statement
458 keyword left_paren variables right_paren semi in
459 (parser, result)
461 and parse_if_statement parser =
462 (* SPEC:
463 if-statement:
464 if ( expression ) statement elseif-clauses-opt else-clause-opt
466 elseif-clauses:
467 elseif-clause
468 elseif-clauses elseif-clause
470 elseif-clause:
471 elseif ( expression ) statement
473 else-clause:
474 else statement
478 (* parses the "( expr ) statement" segment of If, Elseif or Else clauses.
479 * Return a tuple of 5 elements, the first one being the resultant parser
481 let parse_if_body_helper parser_body =
482 let (parser_body, left_paren_token, expr_node, right_paren_token) =
483 parse_paren_expr parser_body in
484 let (parser_body, statement_node) = parse_statement parser_body in
485 (parser_body, left_paren_token, expr_node, right_paren_token,
486 statement_node)
488 let parse_elseif_opt parser_elseif =
489 if peek_token_kind parser_elseif = Elseif then
490 let (parser_elseif, elseif_token) = assert_token parser_elseif Elseif in
491 let (parser_elseif, elseif_left_paren, elseif_condition_expr,
492 elseif_right_paren, elseif_statement) =
493 parse_if_body_helper parser_elseif in
494 let elseif_syntax = make_elseif_clause elseif_token elseif_left_paren
495 elseif_condition_expr elseif_right_paren elseif_statement in
496 (parser_elseif, Some elseif_syntax)
497 else
498 (parser_elseif, None)
500 (* do not eat token and return Missing if first token is not Else *)
501 let parse_else_opt parser_else =
502 let (parser_else, else_token) = optional_token parser_else Else in
503 if is_missing else_token then
504 (parser_else, else_token)
505 else
506 let (parser_else, else_consequence) = parse_statement parser_else in
507 let else_syntax = make_else_clause else_token else_consequence in
508 (parser_else, else_syntax)
510 let (parser, if_keyword_token) = assert_token parser If in
511 let (parser, if_left_paren, if_expr, if_right_paren, if_consequence) =
512 parse_if_body_helper parser in
513 let (parser, elseif_syntax) =
514 parse_list_until_none parser parse_elseif_opt in
515 let (parser, else_syntax) = parse_else_opt parser in
516 let syntax = make_if_statement if_keyword_token if_left_paren if_expr
517 if_right_paren if_consequence elseif_syntax else_syntax in
518 (parser, syntax)
520 and parse_switch_statement parser =
521 (* SPEC:
523 The spec for switches is very simple:
525 switch-statement:
526 switch ( expression ) compound-statement
527 labeled-statement:
528 case-label
529 default-label
530 case-label:
531 case expression : statement
532 default-label:
533 default : statement
535 where the compound statement, if not empty, must consist of only labeled
536 statements.
538 These rules give a nice simple parse but it has some unfortunate properties.
539 Consider:
541 switch (foo)
543 case 1:
544 case 2:
545 break;
546 default:
547 break;
550 What's the parse of the compound statement contents based on that grammar?
552 case 1:
553 case 2:
554 break;
555 default:
556 break;
558 That is, the second case is a child of the first. That makes it harder
559 to write analyzers, it makes it harder to write pretty printers, and so on.
561 What do we really want here? We want a switch to be a collection of
562 *sections* where each section has one or more *labels* and zero or more
563 *statements*.
565 switch-statement:
566 switch ( expression ) { switch-sections-opt }
568 switch-sections:
569 switch-section
570 switch-sections switch-section
572 switch-section:
573 section-labels
574 section-statements-opt
575 section-fallthrough-opt
577 section-fallthrough:
578 fallthrough ;
580 section-labels:
581 section-label
582 section-labels section-label
584 section-statements:
585 statement
586 section-statements statement
588 The parsing of course has to be greedy; we never want to say that there
589 are zero statements *between* two sections.
591 TODO: Update the specification with these rules.
595 let (parser, switch_keyword_token) = assert_token parser Switch in
596 let (parser, left_paren_token, expr_node, right_paren_token) =
597 parse_paren_expr parser in
598 let (parser, left_brace_token) = require_left_brace parser in
599 (* TODO: I'm not convinced that this always terminates in some cases.
600 Check that. *)
601 let (parser, section_list) =
602 parse_terminated_list parser parse_switch_section RightBrace in
603 let (parser, right_brace_token) = require_right_brace parser in
604 let syntax = make_switch_statement switch_keyword_token left_paren_token
605 expr_node right_paren_token left_brace_token section_list
606 right_brace_token in
607 (parser, syntax)
609 and is_switch_fallthrough parser =
610 peek_token_kind parser = Fallthrough &&
611 peek_token_kind ~lookahead:1 parser = Semicolon
613 and parse_possible_erroneous_fallthrough parser =
614 if is_switch_fallthrough parser then
615 let parser = with_error parser SyntaxError.error1055
616 ~on_whole_token:true in
617 let (parser, result) = parse_switch_fallthrough parser in
618 (parser, result)
619 else
620 parse_expression_statement parser
622 and parse_switch_fallthrough parser =
623 (* We don't get here unless we have fallthrough ; *)
624 let (parser, keyword) = assert_token parser Fallthrough in
625 let (parser, semi) = assert_token parser Semicolon in
626 let result = make_switch_fallthrough keyword semi in
627 (parser, result)
629 and parse_switch_fallthrough_opt parser =
630 if is_switch_fallthrough parser then
631 parse_switch_fallthrough parser
632 else
634 * As long as we have FALLTHROUGH comments, insert a faux-statement as if
635 * there was a fallthrough statement. For example, the code
637 * > case 22:
638 * > $x = 0;
639 * > // FALLTHROUGH because we want all the other functionality as well
640 * > case 42:
641 * > foo($x);
642 * > break;
644 * Should be parsed as if it were
646 * > case 22:
647 * > $x = 0;
648 * > // FALLTHROUGH because we want all the other functionality as well
649 * > fallthrough;
650 * > case 43:
651 * > foo($x);
652 * > break;
654 * But since we have no actual occurrence (i.e. no position, no string) of
655 * that `fallthrough;` statement, we construct a `switch_fallthrough`, but
656 * fill it with `missing`.
658 let next = peek_token parser in
659 let commented_fallthrough =
660 List.exists
661 (fun t -> Trivia.kind t = TriviaKind.FallThrough)
662 (Token.leading next)
664 let missing = make_missing parser in
665 let result =
666 if commented_fallthrough
667 then let missing1 = make_missing parser in
668 make_switch_fallthrough missing missing1
669 else missing
671 parser, result
673 and parse_switch_section parser =
674 (* See parse_switch_statement for grammar *)
675 let (parser, labels) =
676 parse_list_until_none parser parse_switch_section_label in
677 let parser = if is_missing labels then
678 with_error parser SyntaxError.error2008
679 else
680 parser in
681 let (parser, statements) =
682 parse_list_until_none parser parse_switch_section_statement in
683 let (parser, fallthrough) = parse_switch_fallthrough_opt parser in
684 let result = make_switch_section labels statements fallthrough in
685 (parser, result)
687 and parse_switch_section_statement parser =
688 if is_switch_fallthrough parser then (parser, None)
689 else match peek_token_kind parser with
690 | Default
691 | Case
692 | RightBrace
693 | TokenKind.EndOfFile -> (parser, None)
694 | _ ->
695 let (parser, statement) = parse_statement parser in
696 (parser, Some statement)
698 and parse_switch_section_label parser =
699 (* See the grammar under parse_switch_statement *)
700 match peek_token_kind parser with
701 | Case ->
702 let (parser, label) = parse_case_label parser in
703 (parser, Some label)
704 | Default ->
705 let (parser, label) = parse_default_label parser in
706 (parser, Some label)
707 | _ -> (parser, None)
709 and parse_catch_clause_opt parser =
710 (* SPEC
711 catch ( type-specification-opt variable-name ) compound-statement
713 if peek_token_kind parser = Catch then
714 let (parser, catch_token) = assert_token parser Catch in
715 let (parser, left_paren) = require_left_paren parser in
716 let (parser, catch_type) =
717 match peek_token_kind parser with
718 | TokenKind.Variable ->
719 let parser = with_error parser SyntaxError.error1007 in
720 let missing = make_missing parser in
721 (parser, missing)
722 | _ -> parse_type_specifier parser
724 let (parser, catch_var) = require_variable parser in
725 let (parser, right_paren) = require_right_paren parser in
726 let (parser, compound_stmt) = parse_compound_statement parser in
727 let catch_clause = make_catch_clause catch_token left_paren
728 catch_type catch_var right_paren compound_stmt in
729 (parser, Some catch_clause)
730 else
731 (parser, None)
733 and parse_finally_clause_opt parser =
734 (* SPEC
735 finally-clause:
736 finally compound-statement
738 if peek_token_kind parser = Finally then
739 let (parser, finally_token) = assert_token parser Finally in
740 let (parser, compound_stmt) = parse_compound_statement parser in
741 let finally_clause = make_finally_clause finally_token compound_stmt in
742 (parser, finally_clause)
743 else
744 let missing = make_missing parser in
745 (parser, missing)
747 and parse_try_statement parser =
748 (* SPEC:
749 try-statement:
750 try compound-statement catch-clauses
751 try compound-statement finally-clause
752 try compound-statement catch-clauses finally-clause
754 let (parser, try_keyword_token) = assert_token parser Try in
755 let (parser, try_compound_stmt) = parse_compound_statement parser in
756 let (parser, catch_clauses) =
757 parse_list_until_none parser parse_catch_clause_opt in
758 let (parser, finally_clause) = parse_finally_clause_opt parser in
759 (* If the catch and finally are both missing then we give an error in
760 a later pass. *)
761 let syntax = make_try_statement try_keyword_token try_compound_stmt
762 catch_clauses finally_clause in
763 (parser, syntax)
765 and parse_break_statement parser =
766 (* SPEC
767 break-statement:
768 break ;
770 However, PHP allows an optional expression; though Hack does not have
771 this feature, we allow it at parse time and produce an error later.
772 TODO: Implement that error. *)
774 (* We detect if we are not inside a switch or loop in a later pass. *)
775 let (parser, break_token) = assert_token parser Break in
776 let (parser, level) =
777 if peek_token_kind parser = Semicolon then
778 let missing = make_missing parser in
779 (parser, missing)
780 else parse_expression parser in
781 let (parser, semi_token) = require_semicolon parser in
782 let result = make_break_statement break_token level semi_token in
783 (parser, result)
785 and parse_continue_statement parser =
786 (* SPEC
787 continue-statement:
788 continue ;
790 However, PHP allows an optional expression; though Hack does not have
791 this feature, we allow it at parse time and produce an error later.
792 TODO: Implement that error. *)
794 (* We detect if we are not inside a loop in a later pass. *)
795 let (parser, continue_token) = assert_token parser Continue in
796 let (parser, level) =
797 if peek_token_kind parser = Semicolon then
798 let missing = make_missing parser in
799 (parser, missing)
800 else parse_expression parser in
801 let (parser, semi_token) = require_semicolon parser in
802 let result = make_continue_statement continue_token level semi_token in
803 (parser, result)
805 and parse_return_statement parser =
806 let (parser, return_token) = assert_token parser Return in
807 let (parser1, semi_token) = next_token parser in
808 if Token.kind semi_token = Semicolon then
809 let missing = make_missing parser in
810 let semi_token = make_token semi_token in
811 let return_statement = make_return_statement
812 return_token missing semi_token
814 (parser1, return_statement)
815 else
816 let (parser, expr) = parse_expression parser in
817 let (parser, semi_token) = require_semicolon parser in
818 (parser, make_return_statement return_token expr semi_token)
820 and parse_goto_label parser =
821 let parser, goto_label_name = next_token_as_name parser in
822 let goto_label_name = make_token goto_label_name in
823 let parser, colon = assert_token parser Colon in
824 parser, make_goto_label goto_label_name colon
826 and parse_goto_statement parser =
827 let parser, goto = assert_token parser Goto in
828 let parser, goto_label_name = next_token_as_name parser in
829 let goto_label_name = make_token goto_label_name in
830 let parser, semicolon = assert_token parser Semicolon in
831 parser, make_goto_statement goto goto_label_name semicolon
833 and parse_throw_statement parser =
834 let (parser, throw_token) = assert_token parser Throw in
835 let (parser, expr) = parse_expression parser in
836 let (parser, semi_token) = require_semicolon parser in
837 (parser, make_throw_statement throw_token expr semi_token)
839 and parse_default_label parser =
841 See comments under parse_switch_statement for the grammar.
842 TODO: Update the spec.
843 TODO: The spec is wrong; it implies that a statement must always follow
844 the default:, but in fact
845 switch($x) { default: }
846 is legal. Fix the spec.
847 TODO: PHP allows a default to end in a semi; Hack does not. We allow a semi
848 here; add an error in a later pass.
850 let (parser, default_token) = assert_token parser Default in
851 let (parser, semi_token) = optional_token parser Semicolon in
852 let (parser, colon_token) =
853 if is_missing semi_token then
854 require_colon parser
855 else
856 (parser, semi_token) in
857 let result = make_default_label default_token colon_token in
858 (parser, result)
860 and parse_case_label parser =
861 (* SPEC:
862 See comments under parse_switch_statement for the grammar.
863 TODO: The spec is wrong; it implies that a statement must always follow
864 the case, but in fact
865 switch($x) { case 10: }
866 is legal. Fix the spec.
867 TODO: PHP allows a case to end in a semi; Hack does not. We allow a semi
868 here; add an error in a later pass.
871 let (parser, case_token) = assert_token parser Case in
872 let (parser, expr) = parse_expression parser in
873 let (parser, semi_token) = optional_token parser Semicolon in
874 let (parser, colon_token) =
875 if is_missing semi_token then
876 require_colon parser
877 else
878 (parser, semi_token) in
879 let result = make_case_label case_token expr colon_token in
880 (parser, result)
882 and parse_global_statement_or_expression_statement parser =
883 (* PHP has a statement of the form
884 global comma-separated-variable-list ;
885 This is not supported in Hack, but we parse it anyways so as to give
886 a good error message. However we do not want to disallow legal statements
887 like "global(123);" so we use a heuristic to see if this is a likely
888 global statement. If not, we parse it as an expression statement.
889 TODO: Add an error in a later pass if this statement is found in a
890 Hack file.
892 let (parser1, keyword) = assert_token parser Global in
893 let is_global_statement =
894 match peek_token_kind parser1 with
895 | TokenKind.Variable | TokenKind.Dollar -> true
896 | _ -> false
898 if is_global_statement then
899 let parse_simple_variable parser =
900 with_expr_parser parser ExpressionParser.parse_simple_variable
902 let (parser, variables) = parse_comma_list
903 parser1 Semicolon SyntaxError.error1008 parse_simple_variable
905 let (parser, semicolon) = require_semicolon parser in
906 let result = make_global_statement keyword variables semicolon in
907 (parser, result)
908 else
909 parse_expression_statement parser
911 and parse_function_static_declaration_or_expression_statement parser =
912 (* Determine if the current token is a late-bound static scope to be
913 * resolved by the '::' operator. (E.g., "static::foo".)
915 if Token.kind (peek_token ~lookahead:1 parser) == TokenKind.ColonColon then
916 parse_expression_statement parser
917 else
918 parse_function_static_declaration parser
920 and parse_function_static_declaration parser =
921 (* SPEC
923 function-static-declaration:
924 static static-declarator-list ;
926 static-declarator-list:
927 static-declarator
928 static-declarator-list , static-declarator
931 let (parser, static) = assert_token parser Static in
932 let (parser, decls) = parse_comma_list
933 parser Semicolon SyntaxError.error1008 parse_static_declarator in
934 let (parser, semicolon) = require_semicolon parser in
935 let result = make_function_static_statement static decls semicolon in
936 (parser, result)
938 and parse_static_declarator parser =
939 (* SPEC
940 static-declarator:
941 variable-name function-static-initializer-opt
943 (* TODO: ERROR RECOVERY not very sophisticated here *)
944 let (parser, variable_name) = require_variable parser in
945 let (parser, init) = parse_static_initializer_opt parser in
946 let result = make_static_declarator variable_name init in
947 (parser, result)
949 and parse_static_initializer_opt parser =
950 (* SPEC
951 function-static-initializer:
952 = const-expression
954 let (parser1, token) = next_token parser in
955 match (Token.kind token) with
956 | Equal ->
957 (* TODO: Detect if expression is not const *)
958 let equal = make_token token in
959 let (parser, value) = parse_expression parser1 in
960 (parser, make_simple_initializer equal value)
961 | _ ->
962 let missing = make_missing parser in
963 (parser, missing)
965 (* SPEC:
966 TODO: update the spec to reflect that echo and print must be a statement
967 echo-intrinsic:
968 echo expression
969 echo ( expression )
970 echo expression-list-two-or-more
972 expression-list-two-or-more:
973 expression , expression
974 expression-list-two-or-more , expression
976 and parse_echo_statement parser =
977 let parser, token = assert_token parser Echo in
978 let parser, expression_list = parse_comma_list
979 parser Semicolon SyntaxError.error1015 parse_expression
981 let parser, semicolon = require_semicolon parser in
982 let syntax = make_echo_statement token expression_list semicolon in
983 (parser, syntax)
985 and parse_expression_statement parser =
986 let (parser1, token) = next_token parser in
987 match Token.kind token with
988 | Semicolon ->
989 let missing = make_missing parser in
990 let token = make_token token in
991 let expr_stmt = make_expression_statement missing token in
992 (parser1, expr_stmt)
993 | _ ->
994 let parser = Parser.expect_in_new_scope parser [ Semicolon ] in
995 let (parser, expression) = parse_expression parser in
996 let (parser, token) =
997 let (parser, token) = require_semicolon_token parser in
998 match token with
999 | Some t ->
1000 if is_halt_compiler_expression expression then
1001 let (parser, token) = rescan_halt_compiler parser t in
1002 (parser, make_token token)
1003 else
1004 (parser, make_token t)
1005 | None ->
1006 parser, make_missing parser
1008 let parser = Parser.pop_scope parser [ Semicolon ] in
1009 (parser, make_expression_statement expression token)
1011 and parse_compound_statement parser =
1012 let (parser1, token) = next_token parser in
1013 match Token.kind token with
1014 | Semicolon -> (parser1, make_token token)
1015 | _ ->
1016 let (parser, left_brace_token) = require_left_brace parser in
1017 let (parser, statement_list) =
1018 parse_terminated_list parser parse_statement RightBrace in
1019 let (parser, right_brace_token) = require_right_brace parser in
1020 let syntax = make_compound_statement
1021 left_brace_token statement_list right_brace_token in
1022 (parser, syntax)
1024 and parse_alternate_loop_statement parser ~terminator =
1025 let (parser, colon_token) = assert_token parser Colon in
1026 let (parser, statement_list) =
1027 parse_terminated_list parser parse_statement terminator in
1028 let (parser, terminate_token) = assert_token parser terminator in
1029 let (parser, semicolon_token) = assert_token parser Semicolon in
1030 let syntax = make_alternate_loop_statement
1031 colon_token statement_list terminate_token semicolon_token in
1032 (parser, syntax)
1035 end (* WithSmartConstructors *)
1036 end (* WithSyntax *)