2 * Copyright (c) 2016, Facebook, Inc.
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.
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
27 .WithLexer
(Full_fidelity_lexer.WithToken
(Syntax.Token
))
30 module type DeclarationParser_S
= Full_fidelity_declaration_parser_type
32 .WithLexer
(Full_fidelity_lexer.WithToken
(Syntax.Token
))
35 module type TypeParser_S
= Full_fidelity_type_parser_type
37 .WithLexer
(Full_fidelity_lexer.WithToken
(Syntax.Token
))
40 module type StatementParser_S
= Full_fidelity_statement_parser_type
42 .WithLexer
(Full_fidelity_lexer.WithToken
(Syntax.Token
))
48 module WithExpressionAndDeclAndTypeParser
49 (ExpressionParser
: ExpressionParser_S
)
50 (DeclParser
: DeclarationParser_S
)
51 (TypeParser
: TypeParser_S
) :
52 StatementParser_S
= struct
55 include ParserHelper.WithParser
(SimpleParser
)
57 let rec parse_statement parser
=
58 match peek_token_kind parser
with
61 | Function
-> parse_possible_php_function parser
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
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
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
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
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
=
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
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
146 make_token
language, (Token.kind
language = TokenKind.Equal
)
147 | None
-> make_missing
(), false
149 make_markup_suffix
less_than_question_token language,
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
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
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 *)
194 DeclParser.parse_classish_declaration decl_parser
(make_missing
()) in
195 use_decl_parser
f parser
198 (f : DeclParser.t
-> DeclParser.t
* Syntax.t
)
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
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 =
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
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
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
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
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)
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
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
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
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
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
321 let semi = make_token token
in
322 parser1
, make_using_statement_function_scoped await_kw using_kw expr
semi
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
352 and parse_if_statement
parser =
355 if ( expression ) statement elseif-clauses-opt else-clause-opt
359 elseif-clauses elseif-clause
362 elseif ( expression ) 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
,
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)
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
)
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
411 and parse_switch_statement
parser =
414 The spec for switches is very simple:
417 switch ( expression ) compound-statement
422 case expression : statement
426 where the compound statement, if not empty, must consist of only labeled
429 These rules give a nice simple parse but it has some unfortunate properties.
441 What's the parse of the compound statement contents based on that grammar?
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
457 switch ( expression ) { switch-sections-opt }
461 switch-sections switch-section
465 section-statements-opt
466 section-fallthrough-opt
473 section-labels section-label
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.
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
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
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
520 and parse_switch_fallthrough_opt
parser =
521 if is_switch_fallthrough
parser then
522 parse_switch_fallthrough
parser
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
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
540 and parse_switch_section_statement
parser =
541 if is_switch_fallthrough
parser then (parser, None
)
542 else match peek_token_kind
parser with
546 | TokenKind.EndOfFile
-> (parser, None
)
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
555 let (parser, label
) = parse_case_label
parser in
558 let (parser, label
) = parse_default_label
parser in
560 | _
-> (parser, None
)
562 and parse_catch_clause_opt
parser =
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
()
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)
596 and parse_finally_clause_opt
parser =
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)
607 (parser, (make_missing
()))
609 and parse_try_statement
parser =
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
623 let syntax = make_try_statement try_keyword_token try_compound_stmt
624 catch_clauses
finally_clause in
627 and parse_break_statement
parser =
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
645 and parse_continue_statement
parser =
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
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
))
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
710 (parser, semi_token
) in
711 let result = make_default_label default_token colon_token
in
714 and parse_case_label
parser =
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
732 (parser, semi_token
) in
733 let result = make_case_label case_token expr colon_token
in
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
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
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
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
768 parse_function_static_declaration
parser
770 and parse_function_static_declaration
parser =
773 function-static-declaration:
774 static static-declarator-list ;
776 static-declarator-list:
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
788 and parse_static_declarator
parser =
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
799 and parse_static_initializer_opt
parser =
801 function-static-initializer:
804 let (parser1
, token
) = next_token
parser in
805 match (Token.kind token
) with
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
())
814 TODO: update the spec to reflect that echo and print must be a statement
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
833 and parse_expression_statement
parser =
834 let (parser1
, token
) = next_token
parser in
835 match Token.kind token
with
837 (parser1
, make_expression_statement
(make_missing
()) (make_token token
))
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
)
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
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