Continue functorizing the parser
[hiphop-php.git] / hphp / hack / src / parser / full_fidelity_statement_parser.ml
blob8f009eeb45f1e7271e2f53a73c36e4d11f4b107e
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))
25 module type ExpressionParser_S = Full_fidelity_expression_parser_type
26 .WithSyntax(Syntax)
27 .WithLexer(Full_fidelity_lexer.WithToken(Syntax.Token))
28 .ExpressionParser_S
30 module type DeclarationParser_S = Full_fidelity_declaration_parser_type
31 .WithSyntax(Syntax)
32 .WithLexer(Full_fidelity_lexer.WithToken(Syntax.Token))
33 .DeclarationParser_S
35 module type TypeParser_S = Full_fidelity_type_parser_type
36 .WithSyntax(Syntax)
37 .WithLexer(Full_fidelity_lexer.WithToken(Syntax.Token))
38 .TypeParser_S
40 module type StatementParser_S = Full_fidelity_statement_parser_type
41 .WithSyntax(Syntax)
42 .WithLexer(Full_fidelity_lexer.WithToken(Syntax.Token))
43 .StatementParser_S
45 open TokenKind
46 open Syntax
48 module WithExpressionAndDeclAndTypeParser
49 (ExpressionParser : ExpressionParser_S)
50 (DeclParser : DeclarationParser_S)
51 (TypeParser : TypeParser_S) :
52 StatementParser_S = struct
54 include SimpleParser
55 include ParserHelper.WithParser(SimpleParser)
57 let rec parse_statement parser =
58 match peek_token_kind parser with
59 | Async
60 | Coroutine
61 | Function -> parse_possible_php_function parser
62 | Abstract
63 | Final
64 | Interface
65 | Trait
66 | Class -> parse_php_class parser
67 | Fallthrough -> parse_possible_erroneous_fallthrough parser
68 | For -> parse_for_statement parser
69 | Foreach -> parse_foreach_statement parser
70 | Do -> parse_do_statement parser
71 | While -> parse_while_statement parser
72 | Using -> parse_using_statement parser (make_missing ())
73 | Await when peek_token_kind ~lookahead:1 parser = Using ->
74 let parser, await_kw = assert_token parser Await in
75 parse_using_statement parser await_kw
76 | If -> parse_if_statement parser
77 | Switch -> parse_switch_statement parser
78 | Try -> parse_try_statement parser
79 | Break -> parse_break_statement parser
80 | Continue -> parse_continue_statement parser
81 | Return -> parse_return_statement parser
82 | Throw -> parse_throw_statement parser
83 | LeftBrace -> parse_compound_statement parser
84 | Static ->
85 parse_function_static_declaration_or_expression_statement parser
86 | Echo -> parse_echo_statement parser
87 | Global -> parse_global_statement_or_expression_statement parser
88 | Unset -> parse_unset_statement parser
89 | Case ->
90 let (parser, result) = parse_case_label parser in
91 (* TODO: This puts the error in the wrong place. We should highlight
92 the entire label, not the trailing colon. *)
93 let parser = with_error parser SyntaxError.error2003 in
94 (parser, result)
95 | Default ->
96 let (parser, result) = parse_default_label parser in
97 (* TODO: This puts the error in the wrong place. We should highlight
98 the entire label, not the trailing colon. *)
99 let parser = with_error parser SyntaxError.error2004 in
100 (parser, result)
101 | Name when peek_token_kind ~lookahead:1 parser = Colon ->
102 parse_goto_label parser
103 | Goto -> parse_goto_statement parser
104 | QuestionGreaterThan ->
105 parse_markup_section parser ~is_leading_section:false
106 | Semicolon -> parse_expression_statement parser
107 (* ERROR RECOVERY: when encountering a token that's invalid now but the
108 * context says is expected later, make the whole statement missing
109 * and continue on, starting at the unexpected token. *)
110 (* TODO T20390825: Make sure this this won't cause premature recovery. *)
111 | kind when SimpleParser.expects parser kind ->
112 (parser, make_missing ())
113 | _ -> parse_expression_statement parser
115 and parse_markup_section parser ~is_leading_section =
116 let parser, prefix =
117 (* for markup section at the beginning of the file
118 treat ?> as a part of markup text *)
119 (* The closing ?> tag is not legal hack, but accept it here and give an
120 error in a later pass *)
121 if not is_leading_section
122 && peek_token_kind parser = TokenKind.QuestionGreaterThan then
123 let (parser, prefix) = next_token parser in
124 parser, make_token prefix
125 else
126 parser, make_missing ()
128 let parser, markup, suffix_opt = scan_markup parser ~is_leading_section in
129 let markup = make_token markup in
130 let suffix, is_echo_tag =
131 match suffix_opt with
132 | Some (less_than_question, language_opt) ->
133 let less_than_question_token = make_token less_than_question in
134 (* if markup section ends with <?= tag
135 then script section embedded between tags should be treated as if it
136 will be an argument to 'echo'. Technically it should be restricted to
137 expression but since it permits trailing semicolons we parse it as
138 expression statement.
139 TODO: consider making it even more loose and parse it as declaration
140 for better error recovery in cases when user
141 accidentally type '<?=' instead of '<?php' so declaration in script
142 section won't throw parser off the rails. *)
143 let language, is_echo_tag =
144 match language_opt with
145 | Some language ->
146 make_token language, (Token.kind language = TokenKind.Equal)
147 | None -> make_missing (), false
149 make_markup_suffix less_than_question_token language,
150 is_echo_tag
151 | None -> make_missing (), false
153 let parser, expression =
154 if is_echo_tag then parse_statement parser else parser, make_missing()
156 let s = make_markup_section prefix markup suffix expression in
157 parser, s
159 and parse_php_function parser =
160 use_decl_parser DeclParser.parse_function parser
162 and parse_possible_php_function parser =
163 (* ERROR RECOVERY: PHP supports nested named functions, but Hack does not.
164 (Hack only supports anonymous nested functions as expressions.)
166 If we have a statement beginning with function left-paren, then parse it
167 as a statement expression beginning with an anonymous function; it will
168 then have to end with a semicolon.
170 If it starts with something else, parse it as a function.
172 TODO: Give an error for nested nominal functions in a later pass.
175 let kind0 = peek_token_kind ~lookahead:0 parser in
176 let kind1 = peek_token_kind ~lookahead:1 parser in
177 match kind0, kind1 with
178 | Async, Function
179 when peek_token_kind ~lookahead:2 parser = LeftParen ->
180 parse_expression_statement parser
181 | Coroutine, Function
182 when peek_token_kind ~lookahead:2 parser = LeftParen ->
183 parse_expression_statement parser
184 | Function, LeftParen (* Verbose-style lambda *)
185 | (Async | Coroutine), LeftParen (* Async / coroutine, compact-style lambda *)
186 | Async, LeftBrace (* Async block *)
187 -> parse_expression_statement parser
188 | _ -> parse_php_function parser
190 and parse_php_class parser =
191 (* PHP allows classes nested inside of functions, but hack does not *)
192 (* TODO check for hack error: no classish declarations inside functions *)
193 let f decl_parser =
194 DeclParser.parse_classish_declaration decl_parser (make_missing ()) in
195 use_decl_parser f parser
197 and use_decl_parser
198 (f : DeclParser.t -> DeclParser.t * Syntax.t)
199 parser =
200 let decl_parser = DeclParser.make
201 ~hhvm_compat_mode: parser.hhvm_compat_mode
202 parser.lexer parser.errors parser.context in
203 let decl_parser, node = f decl_parser in
204 let lexer = DeclParser.lexer decl_parser in
205 let errors = DeclParser.errors decl_parser in
206 let parser = { parser with lexer; errors } in
207 parser, node
209 (* Helper: parses ( expr ) *)
210 and parse_paren_expr parser =
211 let (parser, left_paren) = require_left_paren parser in
212 let (parser, expr_syntax) = parse_expression parser in
213 let (parser, right_paren) = require_right_paren parser in
214 (parser, left_paren, expr_syntax, right_paren)
216 and parse_for_statement parser =
217 (* SPEC
218 for-statement:
219 for ( for-initializer-opt ; for-control-opt ; \
220 for-end-of-loop-opt ) statement
222 Each clause is an optional, comma-separated list of expressions.
223 Note that unlike most such lists in Hack, it may *not* have a trailing
224 comma.
225 TODO: There is no compelling reason to not allow a trailing comma
226 from the grammatical point of view. Each clause unambiguously ends in
227 either a semi or a paren, so we can allow a trailing comma without
228 difficulty.
231 let parser, for_keyword_token = assert_token parser For in
232 let parser, for_left_paren = require_left_paren parser in
233 let parser, for_initializer_expr = parse_comma_list_opt
234 parser Semicolon SyntaxError.error1015 parse_expression in
235 let parser, for_first_semicolon = require_semicolon parser in
236 let parser, for_control_expr = parse_comma_list_opt
237 parser Semicolon SyntaxError.error1015 parse_expression in
238 let parser, for_second_semicolon = require_semicolon parser in
239 let parser, for_end_of_loop_expr = parse_comma_list_opt
240 parser RightParen SyntaxError.error1015 parse_expression in
241 let parser, for_right_paren = require_right_paren parser in
242 let parser, for_statement = parse_statement parser in
243 let syntax = make_for_statement for_keyword_token for_left_paren
244 for_initializer_expr for_first_semicolon for_control_expr
245 for_second_semicolon for_end_of_loop_expr for_right_paren for_statement
247 (parser, syntax)
249 and parse_foreach_statement parser =
250 let parser, foreach_keyword_token = assert_token parser Foreach in
251 let parser, foreach_left_paren = require_left_paren parser in
252 let parser, foreach_collection_name = parse_expression parser in
253 let parser, await_token = optional_token parser Await in
254 let parser, as_token = require_as parser in
255 (* let (parser1, token) = next_token parser in *)
256 let (parser, after_as) = parse_expression parser in
257 let parser = SimpleParser.expect_in_new_scope parser [ RightParen ] in
258 let (parser, foreach_key, foreach_arrow, foreach_value) =
259 match Token.kind (peek_token parser) with
260 | RightParen ->
261 (parser, make_missing (), make_missing (), after_as)
262 | EqualGreaterThan ->
263 let parser, arrow = assert_token parser EqualGreaterThan in
264 let parser, value = parse_expression parser in
265 (parser, after_as, arrow, value)
266 | _ ->
267 (* TODO ERROR RECOVERY. Now assumed that the arrow is missing
268 * and goes on to parse the next expression *)
269 let parser, token = next_token parser in
270 let parser, foreach_value = parse_expression parser in
271 (parser, after_as, make_error (make_token token), foreach_value)
273 let parser, right_paren_token = require_right_paren parser in
274 let parser = SimpleParser.pop_scope parser [ RightParen ] in
275 let parser, foreach_statement = parse_statement parser in
276 let syntax =
277 make_foreach_statement foreach_keyword_token foreach_left_paren
278 foreach_collection_name await_token as_token foreach_key foreach_arrow
279 foreach_value right_paren_token foreach_statement in
280 (parser, syntax)
282 and parse_do_statement parser =
283 let (parser, do_keyword_token) =
284 assert_token parser Do in
285 let (parser, statement_node) =
286 parse_statement parser in
287 let (parser, do_while_keyword_token) = require_while parser in
288 let (parser, left_paren_token, expr_node, right_paren_token) =
289 parse_paren_expr parser in
290 let (parser, do_semicolon_token) = require_semicolon parser in
291 let syntax = make_do_statement do_keyword_token statement_node
292 do_while_keyword_token left_paren_token expr_node right_paren_token
293 do_semicolon_token in
294 (parser, syntax)
296 and parse_while_statement parser =
297 let (parser, while_keyword_token) =
298 assert_token parser While in
299 let (parser, left_paren_token, expr_node, right_paren_token) =
300 parse_paren_expr parser in
301 let (parser, statement_node) =
302 parse_statement parser in
303 let syntax = make_while_statement while_keyword_token left_paren_token
304 expr_node right_paren_token statement_node in
305 (parser, syntax)
307 (* SPEC:
308 using-statement:
309 await-opt using expression ;
310 await-opt using ( expression-list ) compound-statement
312 TODO: Update the specification of the grammar
314 and parse_using_statement parser await_kw =
315 let (parser, using_kw) = assert_token parser Using in
316 (* Decision point - Are we at a function scope or a body scope *)
317 let (parser1, expr) = parse_expression parser in
318 let (parser1, token) = next_token parser1 in
319 match Token.kind token with
320 | Semicolon ->
321 let semi = make_token token in
322 parser1, make_using_statement_function_scoped await_kw using_kw expr semi
323 | _ ->
324 let (parser, left_paren) = require_left_paren parser in
325 let (parser, expressions) = parse_comma_list
326 parser RightParen SyntaxError.error1015 parse_expression in
327 let (parser, right_paren) = require_right_paren parser in
328 let (parser, statements) = parse_statement parser in
329 parser, make_using_statement_block_scoped
330 await_kw using_kw left_paren expressions right_paren statements
332 and parse_unset_statement parser =
334 TODO: This is listed as unsupported in Hack in the spec; is that true?
335 TODO: If it is formally supported in Hack then update the spec; if not
336 TODO: then should we make it illegal in strict mode?
337 TODO: Can the list be comma-terminated?
338 TODO: Can the list be empty?
339 TODO: The list has to be expressions which evaluate as variables;
340 add an error checking pass.
341 TODO: Unset is case-insentive. Should non-lowercase be an error?
343 let (parser, keyword) = assert_token parser Unset in
344 let (parser, left_paren, variables, right_paren) =
345 parse_parenthesized_comma_list_opt_allow_trailing
346 parser parse_expression in
347 let (parser, semi) = require_semicolon parser in
348 let result = make_unset_statement
349 keyword left_paren variables right_paren semi in
350 (parser, result)
352 and parse_if_statement parser =
353 (* SPEC:
354 if-statement:
355 if ( expression ) statement elseif-clauses-opt else-clause-opt
357 elseif-clauses:
358 elseif-clause
359 elseif-clauses elseif-clause
361 elseif-clause:
362 elseif ( expression ) statement
364 else-clause:
365 else statement
369 (* parses the "( expr ) statement" segment of If, Elseif or Else clauses.
370 * Return a tuple of 5 elements, the first one being the resultant parser
372 let parse_if_body_helper parser_body =
373 let (parser_body, left_paren_token, expr_node, right_paren_token) =
374 parse_paren_expr parser_body in
375 let (parser_body, statement_node) = parse_statement parser_body in
376 (parser_body, left_paren_token, expr_node, right_paren_token,
377 statement_node)
379 let parse_elseif_opt parser_elseif =
380 if peek_token_kind parser_elseif = Elseif then
381 let (parser_elseif, elseif_token) = assert_token parser_elseif Elseif in
382 let (parser_elseif, elseif_left_paren, elseif_condition_expr,
383 elseif_right_paren, elseif_statement) =
384 parse_if_body_helper parser_elseif in
385 let elseif_syntax = make_elseif_clause elseif_token elseif_left_paren
386 elseif_condition_expr elseif_right_paren elseif_statement in
387 (parser_elseif, Some elseif_syntax)
388 else
389 (parser_elseif, None)
391 (* do not eat token and return Missing if first token is not Else *)
392 let parse_else_opt parser_else =
393 let (parser_else, else_token) = optional_token parser_else Else in
394 match syntax else_token with
395 | Missing -> (parser_else, else_token)
396 | _ ->
397 let (parser_else, else_consequence) = parse_statement parser_else in
398 let else_syntax = make_else_clause else_token else_consequence in
399 (parser_else, else_syntax)
401 let (parser, if_keyword_token) = assert_token parser If in
402 let (parser, if_left_paren, if_expr, if_right_paren, if_consequence) =
403 parse_if_body_helper parser in
404 let (parser, elseif_syntax) =
405 parse_list_until_none parser parse_elseif_opt in
406 let (parser, else_syntax) = parse_else_opt parser in
407 let syntax = make_if_statement if_keyword_token if_left_paren if_expr
408 if_right_paren if_consequence elseif_syntax else_syntax in
409 (parser, syntax)
411 and parse_switch_statement parser =
412 (* SPEC:
414 The spec for switches is very simple:
416 switch-statement:
417 switch ( expression ) compound-statement
418 labeled-statement:
419 case-label
420 default-label
421 case-label:
422 case expression : statement
423 default-label:
424 default : statement
426 where the compound statement, if not empty, must consist of only labeled
427 statements.
429 These rules give a nice simple parse but it has some unfortunate properties.
430 Consider:
432 switch (foo)
434 case 1:
435 case 2:
436 break;
437 default:
438 break;
441 What's the parse of the compound statement contents based on that grammar?
443 case 1:
444 case 2:
445 break;
446 default:
447 break;
449 That is, the second case is a child of the first. That makes it harder
450 to write analyzers, it makes it harder to write pretty printers, and so on.
452 What do we really want here? We want a switch to be a collection of
453 *sections* where each section has one or more *labels* and zero or more
454 *statements*.
456 switch-statement:
457 switch ( expression ) { switch-sections-opt }
459 switch-sections:
460 switch-section
461 switch-sections switch-section
463 switch-section:
464 section-labels
465 section-statements-opt
466 section-fallthrough-opt
468 section-fallthrough:
469 fallthrough ;
471 section-labels:
472 section-label
473 section-labels section-label
475 section-statements:
476 statement
477 section-statements statement
479 The parsing of course has to be greedy; we never want to say that there
480 are zero statements *between* two sections.
482 TODO: Update the specification with these rules.
486 let (parser, switch_keyword_token) = assert_token parser Switch in
487 let (parser, left_paren_token, expr_node, right_paren_token) =
488 parse_paren_expr parser in
489 let (parser, left_brace_token) = require_left_brace parser in
490 (* TODO: I'm not convinced that this always terminates in some cases.
491 Check that. *)
492 let (parser, section_list) =
493 parse_terminated_list parser parse_switch_section RightBrace in
494 let (parser, right_brace_token) = require_right_brace parser in
495 let syntax = make_switch_statement switch_keyword_token left_paren_token
496 expr_node right_paren_token left_brace_token section_list
497 right_brace_token in
498 (parser, syntax)
500 and is_switch_fallthrough parser =
501 peek_token_kind parser = Fallthrough &&
502 peek_token_kind ~lookahead:1 parser = Semicolon
504 and parse_possible_erroneous_fallthrough parser =
505 if is_switch_fallthrough parser then
506 let parser = with_error parser SyntaxError.error1055
507 ~on_whole_token:true in
508 let (parser, result) = parse_switch_fallthrough parser in
509 (parser, result)
510 else
511 parse_expression_statement parser
513 and parse_switch_fallthrough parser =
514 (* We don't get here unless we have fallthrough ; *)
515 let (parser, keyword) = assert_token parser Fallthrough in
516 let (parser, semi) = assert_token parser Semicolon in
517 let result = make_switch_fallthrough keyword semi in
518 (parser, result)
520 and parse_switch_fallthrough_opt parser =
521 if is_switch_fallthrough parser then
522 parse_switch_fallthrough parser
523 else
524 (parser, (make_missing()))
526 and parse_switch_section parser =
527 (* See parse_switch_statement for grammar *)
528 let (parser, labels) =
529 parse_list_until_none parser parse_switch_section_label in
530 let parser = if is_missing labels then
531 with_error parser SyntaxError.error2008
532 else
533 parser in
534 let (parser, statements) =
535 parse_list_until_none parser parse_switch_section_statement in
536 let (parser, fallthrough) = parse_switch_fallthrough_opt parser in
537 let result = make_switch_section labels statements fallthrough in
538 (parser, result)
540 and parse_switch_section_statement parser =
541 if is_switch_fallthrough parser then (parser, None)
542 else match peek_token_kind parser with
543 | Default
544 | Case
545 | RightBrace
546 | TokenKind.EndOfFile -> (parser, None)
547 | _ ->
548 let (parser, statement) = parse_statement parser in
549 (parser, Some statement)
551 and parse_switch_section_label parser =
552 (* See the grammar under parse_switch_statement *)
553 match peek_token_kind parser with
554 | Case ->
555 let (parser, label) = parse_case_label parser in
556 (parser, Some label)
557 | Default ->
558 let (parser, label) = parse_default_label parser in
559 (parser, Some label)
560 | _ -> (parser, None)
562 and parse_catch_clause_opt parser =
563 (* SPEC
564 catch ( type-specification-opt variable-name ) compound-statement
566 if peek_token_kind parser = Catch then
567 let (parser, catch_token) = assert_token parser Catch in
568 let (parser, left_paren) = require_left_paren parser in
569 let (parser, catch_type) =
570 match peek_token_kind parser with
571 | TokenKind.Variable ->
572 let parser = with_error parser SyntaxError.error1007 in
573 parser, make_missing ()
574 | _ ->
575 let type_parser =
576 TypeParser.make
577 ~hhvm_compat_mode:parser.hhvm_compat_mode
578 parser.lexer parser.errors parser.context
580 let (type_parser, node) =
581 TypeParser.parse_type_specifier type_parser
583 let lexer = TypeParser.lexer type_parser in
584 let errors = TypeParser.errors type_parser in
585 { parser with lexer; errors }, node
587 let (parser, catch_var) = require_variable parser in
588 let (parser, right_paren) = require_right_paren parser in
589 let (parser, compound_stmt) = parse_compound_statement parser in
590 let catch_clause = make_catch_clause catch_token left_paren
591 catch_type catch_var right_paren compound_stmt in
592 (parser, Some catch_clause)
593 else
594 (parser, None)
596 and parse_finally_clause_opt parser =
597 (* SPEC
598 finally-clause:
599 finally compound-statement
601 if peek_token_kind parser = Finally then
602 let (parser, finally_token) = assert_token parser Finally in
603 let (parser, compound_stmt) = parse_compound_statement parser in
604 let finally_clause = make_finally_clause finally_token compound_stmt in
605 (parser, finally_clause)
606 else
607 (parser, (make_missing()))
609 and parse_try_statement parser =
610 (* SPEC:
611 try-statement:
612 try compound-statement catch-clauses
613 try compound-statement finally-clause
614 try compound-statement catch-clauses finally-clause
616 let (parser, try_keyword_token) = assert_token parser Try in
617 let (parser, try_compound_stmt) = parse_compound_statement parser in
618 let (parser, catch_clauses) =
619 parse_list_until_none parser parse_catch_clause_opt in
620 let (parser, finally_clause) = parse_finally_clause_opt parser in
621 (* If the catch and finally are both missing then we give an error in
622 a later pass. *)
623 let syntax = make_try_statement try_keyword_token try_compound_stmt
624 catch_clauses finally_clause in
625 (parser, syntax)
627 and parse_break_statement parser =
628 (* SPEC
629 break-statement:
630 break ;
632 However, PHP allows an optional expression; though Hack does not have
633 this feature, we allow it at parse time and produce an error later.
634 TODO: Implement that error. *)
636 (* We detect if we are not inside a switch or loop in a later pass. *)
637 let (parser, break_token) = assert_token parser Break in
638 let (parser, level) =
639 if peek_token_kind parser = Semicolon then (parser, (make_missing()))
640 else parse_expression parser in
641 let (parser, semi_token) = require_semicolon parser in
642 let result = make_break_statement break_token level semi_token in
643 (parser, result)
645 and parse_continue_statement parser =
646 (* SPEC
647 continue-statement:
648 continue ;
650 However, PHP allows an optional expression; though Hack does not have
651 this feature, we allow it at parse time and produce an error later.
652 TODO: Implement that error. *)
654 (* We detect if we are not inside a loop in a later pass. *)
655 let (parser, continue_token) = assert_token parser Continue in
656 let (parser, level) =
657 if peek_token_kind parser = Semicolon then (parser, (make_missing()))
658 else parse_expression parser in
659 let (parser, semi_token) = require_semicolon parser in
660 let result = make_continue_statement continue_token level semi_token in
661 (parser, result)
663 and parse_return_statement parser =
664 let (parser, return_token) = assert_token parser Return in
665 let (parser1, semi_token) = next_token parser in
666 if Token.kind semi_token = Semicolon then
667 (parser1, make_return_statement
668 return_token (make_missing()) (make_token semi_token))
669 else
670 let (parser, expr) = parse_expression parser in
671 let (parser, semi_token) = require_semicolon parser in
672 (parser, make_return_statement return_token expr semi_token)
674 and parse_goto_label parser =
675 let parser, goto_label_name = next_token_as_name parser in
676 let goto_label_name = make_token goto_label_name in
677 let parser, colon = assert_token parser Colon in
678 parser, make_goto_label goto_label_name colon
680 and parse_goto_statement parser =
681 let parser, goto = assert_token parser Goto in
682 let parser, goto_label_name = next_token_as_name parser in
683 let goto_label_name = make_token goto_label_name in
684 let parser, semicolon = assert_token parser Semicolon in
685 parser, make_goto_statement goto goto_label_name semicolon
687 and parse_throw_statement parser =
688 let (parser, throw_token) = assert_token parser Throw in
689 let (parser, expr) = parse_expression parser in
690 let (parser, semi_token) = require_semicolon parser in
691 (parser, make_throw_statement throw_token expr semi_token)
693 and parse_default_label parser =
695 See comments under parse_switch_statement for the grammar.
696 TODO: Update the spec.
697 TODO: The spec is wrong; it implies that a statement must always follow
698 the default:, but in fact
699 switch($x) { default: }
700 is legal. Fix the spec.
701 TODO: PHP allows a default to end in a semi; Hack does not. We allow a semi
702 here; add an error in a later pass.
704 let (parser, default_token) = assert_token parser Default in
705 let (parser, semi_token) = optional_token parser Semicolon in
706 let (parser, colon_token) =
707 if is_missing semi_token then
708 require_colon parser
709 else
710 (parser, semi_token) in
711 let result = make_default_label default_token colon_token in
712 (parser, result)
714 and parse_case_label parser =
715 (* SPEC:
716 See comments under parse_switch_statement for the grammar.
717 TODO: The spec is wrong; it implies that a statement must always follow
718 the case, but in fact
719 switch($x) { case 10: }
720 is legal. Fix the spec.
721 TODO: PHP allows a case to end in a semi; Hack does not. We allow a semi
722 here; add an error in a later pass.
725 let (parser, case_token) = assert_token parser Case in
726 let (parser, expr) = parse_expression parser in
727 let (parser, semi_token) = optional_token parser Semicolon in
728 let (parser, colon_token) =
729 if is_missing semi_token then
730 require_colon parser
731 else
732 (parser, semi_token) in
733 let result = make_case_label case_token expr colon_token in
734 (parser, result)
736 and parse_global_statement_or_expression_statement parser =
737 (* PHP has a statement of the form
738 global comma-separated-variable-list ;
739 This is not supported in Hack, but we parse it anyways so as to give
740 a good error message. However we do not want to disallow legal statements
741 like "global(123);" so we use a heuristic to see if this is a likely
742 global statement. If not, we parse it as an expression statement.
743 TODO: Add an error in a later pass if this statement is found in a
744 Hack file.
746 let (parser1, keyword) = assert_token parser Global in
747 let is_global_statement =
748 match peek_token_kind parser1 with
749 | TokenKind.Variable | TokenKind.Dollar -> true
750 | _ -> false
752 if is_global_statement then
753 let (parser, variables) = parse_comma_list
754 parser1 Semicolon SyntaxError.error1008 parse_simple_variable in
755 let (parser, semicolon) = require_semicolon parser in
756 let result = make_global_statement keyword variables semicolon in
757 (parser, result)
758 else
759 parse_expression_statement parser
761 and parse_function_static_declaration_or_expression_statement parser =
762 (* Determine if the current token is a late-bound static scope to be
763 * resolved by the '::' operator. (E.g., "static::foo".)
765 if Token.kind (peek_token ~lookahead:1 parser) == TokenKind.ColonColon then
766 parse_expression_statement parser
767 else
768 parse_function_static_declaration parser
770 and parse_function_static_declaration parser =
771 (* SPEC
773 function-static-declaration:
774 static static-declarator-list ;
776 static-declarator-list:
777 static-declarator
778 static-declarator-list , static-declarator
781 let (parser, static) = assert_token parser Static in
782 let (parser, decls) = parse_comma_list
783 parser Semicolon SyntaxError.error1008 parse_static_declarator in
784 let (parser, semicolon) = require_semicolon parser in
785 let result = make_function_static_statement static decls semicolon in
786 (parser, result)
788 and parse_static_declarator parser =
789 (* SPEC
790 static-declarator:
791 variable-name function-static-initializer-opt
793 (* TODO: ERROR RECOVERY not very sophisticated here *)
794 let (parser, variable_name) = require_variable parser in
795 let (parser, init) = parse_static_initializer_opt parser in
796 let result = make_static_declarator variable_name init in
797 (parser, result)
799 and parse_static_initializer_opt parser =
800 (* SPEC
801 function-static-initializer:
802 = const-expression
804 let (parser1, token) = next_token parser in
805 match (Token.kind token) with
806 | Equal ->
807 (* TODO: Detect if expression is not const *)
808 let equal = make_token token in
809 let (parser, value) = parse_expression parser1 in
810 (parser, make_simple_initializer equal value)
811 | _ -> (parser, make_missing())
813 (* SPEC:
814 TODO: update the spec to reflect that echo and print must be a statement
815 echo-intrinsic:
816 echo expression
817 echo ( expression )
818 echo expression-list-two-or-more
820 expression-list-two-or-more:
821 expression , expression
822 expression-list-two-or-more , expression
824 and parse_echo_statement parser =
825 let parser, token = assert_token parser Echo in
826 let parser, expression_list = parse_comma_list
827 parser Semicolon SyntaxError.error1015 parse_expression
829 let parser, semicolon = require_semicolon parser in
830 let syntax = make_echo_statement token expression_list semicolon in
831 (parser, syntax)
833 and parse_expression_statement parser =
834 let (parser1, token) = next_token parser in
835 match Token.kind token with
836 | Semicolon ->
837 (parser1, make_expression_statement (make_missing ()) (make_token token))
838 | _ ->
839 let parser = SimpleParser.expect_in_new_scope parser [ Semicolon ] in
840 let (parser, expression) = parse_expression parser in
841 let (parser, token) = require_semicolon parser in
842 let parser = SimpleParser.pop_scope parser [ Semicolon ] in
843 (parser, make_expression_statement expression token)
845 and parse_compound_statement parser =
846 let (parser1, token) = next_token parser in
847 match Token.kind token with
848 | Semicolon -> (parser1, make_token token)
849 | _ ->
850 let (parser, left_brace_token) = require_left_brace parser in
851 let (parser, statement_list) =
852 parse_terminated_list parser parse_statement RightBrace in
853 let (parser, right_brace_token) = require_right_brace parser in
854 let syntax = make_compound_statement
855 left_brace_token statement_list right_brace_token in
856 (parser, syntax)
858 and parse_expression parser =
859 with_expression_parser parser ExpressionParser.parse_expression
861 and parse_simple_variable parser =
862 with_expression_parser parser ExpressionParser.parse_simple_variable
864 and with_expression_parser parser f =
865 let expression_parser = ExpressionParser.make
866 ~hhvm_compat_mode:parser.hhvm_compat_mode
867 parser.lexer parser.errors parser.context in
868 let (expression_parser, node) = f expression_parser in
869 let lexer = ExpressionParser.lexer expression_parser in
870 let errors = ExpressionParser.errors expression_parser in
871 let parser = { parser with lexer; errors } in
872 (parser, node)
875 end (* WithSyntax *)