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