Permalink
Browse files

Alternate if syntax support

Summary:
Support for statements of the style
```
if ($v < 0):
  ++$v;
elseif ($v < 1):
  ++$v;
else:
  ++$v;
endif;
```
in the full fidelity parser.

Reviewed By: jamesjwu

Differential Revision: D6965574

fbshipit-source-id: ea416b5835bbca40cdde7de5ab57b530ec378e54
  • Loading branch information...
vassilmladenov authored and hhvm-bot committed Feb 23, 2018
1 parent e75aa91 commit dcc7796449e8b48add9b8b1797593968bc14f2b4
@@ -816,17 +816,17 @@ let rec t (env: Env.t) (node: Syntax.t) : Doc.t =
]
| _ -> handle_possible_compound_statement env x.else_statement
]
| Syntax.IfEndIfStatement {
if_endif_keyword = kw;
if_endif_left_paren = left_p;
if_endif_condition = condition;
if_endif_right_paren = right_p;
if_endif_colon = colon;
if_endif_statement = if_body;
if_endif_elseif_colon_clauses = elseif_clauses;
if_endif_else_colon_clause = else_clause;
if_endif_endif_keyword = endif_kw;
if_endif_semicolon = semicolon; } ->
| Syntax.AlternateIfStatement {
alternate_if_keyword = kw;
alternate_if_left_paren = left_p;
alternate_if_condition = condition;
alternate_if_right_paren = right_p;
alternate_if_colon = colon;
alternate_if_statement = if_body;
alternate_if_elseif_clauses = elseif_clauses;
alternate_if_else_clause = else_clause;
alternate_if_endif_keyword = endif_kw;
alternate_if_semicolon = semicolon; } ->
Concat [
t env kw;
Space;
@@ -839,30 +839,30 @@ let rec t (env: Env.t) (node: Syntax.t) : Doc.t =
t env semicolon;
Newline;
]
| Syntax.ElseifColonClause {
elseif_colon_keyword = kw;
elseif_colon_left_paren = left_p;
elseif_colon_condition = condition;
elseif_colon_right_paren = right_p;
elseif_colon_colon = colon;
elseif_colon_statement = body; } ->
| Syntax.AlternateElseifClause {
alternate_elseif_keyword = kw;
alternate_elseif_left_paren = left_p;
alternate_elseif_condition = condition;
alternate_elseif_right_paren = right_p;
alternate_elseif_colon = colon;
alternate_elseif_statement = body; } ->
Concat [
t env kw;
Space;
transform_condition env left_p condition right_p;
t env colon;
handle_possible_compound_statement env body;
]
| Syntax.ElseColonClause x ->
| Syntax.AlternateElseClause x ->
Concat [
t env x.else_colon_keyword;
match Syntax.syntax x.else_colon_statement with
t env x.alternate_else_keyword;
match Syntax.syntax x.alternate_else_statement with
| Syntax.IfStatement _ -> Concat [
Space;
t env x.else_colon_statement;
t env x.alternate_else_statement;
Space;
]
| _ -> handle_possible_compound_statement env x.else_colon_statement
| _ -> handle_possible_compound_statement env x.alternate_else_statement
]
| Syntax.TryStatement {
try_keyword = kw;
@@ -1502,33 +1502,44 @@ and pStmt : stmt parser = fun node env ->
, List.concat @@ couldMap ~f:pSwitchSection sections env
)
| IfStatement
{ if_condition; if_statement; if_elseif_clauses; if_else_clause; _ } ->
{ if_condition=cond;
if_statement=stmt;
if_elseif_clauses=elseif_clause;
if_else_clause=else_clause; _ }
| AlternateIfStatement
{ alternate_if_condition=cond;
alternate_if_statement=stmt;
alternate_if_elseif_clauses=elseif_clause;
alternate_if_else_clause=else_clause; _ } ->
(* Because consistency is for the weak-willed, Parser_hack does *not*
* produce `Noop`s for compound statements **in if statements**
*)
let de_noop = function
| [p, Block [_, Noop]] -> [p, Block []]
| stmts -> stmts
in
let if_condition = pExpr if_condition env in
let if_statement = de_noop (pStmtUnsafe if_statement env) in
let if_condition = pExpr cond env in
let if_statement = de_noop (pStmtUnsafe stmt env) in
let if_elseif_statement =
let pElseIf : (block -> block) parser = fun node env ->
match syntax node with
| ElseifClause { elseif_condition; elseif_statement; _ } ->
| ElseifClause { elseif_condition=ei_cond; elseif_statement=ei_stmt; _ }
| AlternateElseifClause { alternate_elseif_condition=ei_cond;
alternate_elseif_statement=ei_stmt; _ } ->
fun next_clause ->
let elseif_condition = pExpr elseif_condition env in
let elseif_statement = de_noop (pStmtUnsafe elseif_statement env) in
let elseif_condition = pExpr ei_cond env in
let elseif_statement = de_noop (pStmtUnsafe ei_stmt env) in
[ pos, If (elseif_condition, elseif_statement, next_clause) ]
| _ -> missing_syntax "elseif clause" node env
in
List.fold_right ~f:(@@)
(couldMap ~f:pElseIf if_elseif_clauses env)
~init:( match syntax if_else_clause with
| ElseClause { else_statement; _ } ->
de_noop (pStmtUnsafe else_statement env)
(couldMap ~f:pElseIf elseif_clause env)
~init:( match syntax else_clause with
| ElseClause { else_statement=e_stmt; _ }
| AlternateElseClause { alternate_else_statement=e_stmt; _ } ->
de_noop (pStmtUnsafe e_stmt env)
| Missing -> [Pos.none, Noop]
| _ -> missing_syntax "else clause" if_else_clause env
| _ -> missing_syntax "else clause" else_clause env
)
in
pos, If (if_condition, if_statement, if_elseif_statement)
@@ -1545,6 +1556,8 @@ and pStmt : stmt parser = fun node env ->
handle_loop_body pos compound_statements tail env
| AlternateLoopStatement { alternate_loop_statements; _ } ->
handle_loop_body pos alternate_loop_statements [] env
| SyntaxList _ ->
handle_loop_body pos node [] env
| ThrowStatement { throw_expression; _ } ->
pos, Throw (pExpr throw_expression env)
| DoStatement { do_body; do_condition; _ } ->
@@ -856,6 +856,12 @@ module WithParser(Parser : Parser_S) = struct
let predicate parser = peek_token_kind parser != terminator in
parse_list_while parser parse_item predicate
let parse_alternate_if_block parser parse_item =
parse_list_while parser parse_item (fun parser ->
match peek_token_kind parser with
| TokenKind.Elseif | TokenKind.Else | TokenKind.Endif -> false
| _ -> true)
let parse_list_until_none parser parse_item =
let rec aux parser acc =
let (parser, maybe_item) = parse_item parser in
@@ -732,20 +732,21 @@ let rec get_doc node =
let statement = x.else_statement in
handle_compound_brace_prefix_indent keyword statement indt
|> add_break
| IfEndIfStatement
{ if_endif_keyword; if_endif_colon; if_endif_left_paren; if_endif_condition;
if_endif_right_paren; if_endif_statement; if_endif_elseif_colon_clauses;
if_endif_else_colon_clause; if_endif_endif_keyword; if_endif_semicolon }->
let keyword = get_doc if_endif_keyword in
let left = get_doc if_endif_left_paren in
let condition = get_doc if_endif_condition in
let right = get_doc if_endif_right_paren in
let colon = get_doc if_endif_colon in
let if_stmt = if_endif_statement in
let elseif_clause = get_doc if_endif_elseif_colon_clauses in
let else_clause = get_doc if_endif_else_colon_clause in
let end_kw = get_doc if_endif_endif_keyword in
let semicolon = get_doc if_endif_semicolon in
| AlternateIfStatement
{ alternate_if_keyword; alternate_if_colon; alternate_if_left_paren;
alternate_if_condition; alternate_if_right_paren; alternate_if_statement;
alternate_if_elseif_clauses; alternate_if_else_clause;
alternate_if_endif_keyword; alternate_if_semicolon }->
let keyword = get_doc alternate_if_keyword in
let left = get_doc alternate_if_left_paren in
let condition = get_doc alternate_if_condition in
let right = get_doc alternate_if_right_paren in
let colon = get_doc alternate_if_colon in
let if_stmt = alternate_if_statement in
let elseif_clause = get_doc alternate_if_elseif_clauses in
let else_clause = get_doc alternate_if_else_clause in
let end_kw = get_doc alternate_if_endif_keyword in
let semicolon = get_doc alternate_if_semicolon in
let left_part = group_doc (keyword ^^| left) in
let right_part = group_doc (right ^^^ colon) in
let end_block = group_doc (end_kw ^^^ semicolon) in
@@ -754,23 +755,23 @@ let rec get_doc node =
handle_compound_brace_prefix_indent start_block if_stmt indt in
let if_statement = add_break if_statement in
group_doc (if_statement ^| elseif_clause ^| else_clause ^| end_block)
| ElseifColonClause
{ elseif_colon_keyword; elseif_colon_left_paren; elseif_colon_condition;
elseif_colon_right_paren; elseif_colon_colon; elseif_colon_statement } ->
let keyword = get_doc elseif_colon_keyword in
let left = get_doc elseif_colon_left_paren in
let condition = get_doc elseif_colon_condition in
let right = get_doc elseif_colon_right_paren in
let colon = get_doc elseif_colon_colon in
let elif_statement_syntax = elseif_colon_statement in
| AlternateElseifClause
{ alternate_elseif_keyword; alternate_elseif_left_paren; alternate_elseif_condition;
alternate_elseif_right_paren; alternate_elseif_colon; alternate_elseif_statement } ->
let keyword = get_doc alternate_elseif_keyword in
let left = get_doc alternate_elseif_left_paren in
let condition = get_doc alternate_elseif_condition in
let right = get_doc alternate_elseif_right_paren in
let colon = get_doc alternate_elseif_colon in
let elif_statement_syntax = alternate_elseif_statement in
let left_part = group_doc (keyword ^^| left) in
let right_part = group_doc (right ^^^ colon) in
let start_block = indent_block_no_space left_part condition right_part indt in
handle_compound_brace_prefix_indent start_block elif_statement_syntax indt
|> add_break
| ElseColonClause x ->
let keyword = get_doc x.else_colon_keyword in
let statement = x.else_colon_statement in
| AlternateElseClause x ->
let keyword = get_doc x.alternate_else_keyword in
let statement = x.alternate_else_statement in
handle_compound_brace_prefix_indent keyword statement indt
|> add_break
| TryStatement
@@ -469,18 +469,29 @@ module WithExpressionAndDeclAndTypeParser
and parse_if_statement parser =
(* SPEC:
if-statement:
if ( expression ) statement elseif-clauses-opt else-clause-opt
if ( expression ) statement elseif-clauses-opt else-clause-opt
if ( expression ): statement alt-elif-clauses-opt alt-else-clause-opt endif;
elseif-clauses:
elseif-clause
elseif-clauses elseif-clause
alt-elif-clauses:
alt-elif-clause
alt-elif-clauses alt-elif-clause
elseif-clause:
elseif ( expression ) statement
alt-elif-clause:
elseif ( expression ): statement
else-clause:
else statement
alt-else-clause:
else: statement
*)
(* parses the "( expr ) statement" segment of If, Elseif or Else clauses.
@@ -489,18 +500,48 @@ module WithExpressionAndDeclAndTypeParser
let parse_if_body_helper parser_body =
let (parser_body, left_paren_token, expr_node, right_paren_token) =
parse_paren_expr parser_body in
let (parser_body, statement_node) = parse_statement parser_body in
(parser_body, left_paren_token, expr_node, right_paren_token,
statement_node)
let parser1, opening_token = next_token parser_body in
let opening_token_syntax = make_token opening_token in
let (parser_body, statement_node) = match Token.kind opening_token with
| Colon -> parse_alternate_if_block parser1 parse_statement
| _ -> parse_statement parser_body in
( parser_body
, left_paren_token
, expr_node
, right_paren_token
, opening_token
, opening_token_syntax
, statement_node
)
in
let parse_elseif_opt parser_elseif =
if peek_token_kind parser_elseif = Elseif then
let (parser_elseif, elseif_token) = assert_token parser_elseif Elseif in
let (parser_elseif, elseif_left_paren, elseif_condition_expr,
elseif_right_paren, elseif_statement) =
let ( parser_elseif
, elseif_left_paren
, elseif_condition_expr
, elseif_right_paren
, elseif_opening_token
, elseif_opening_token_syntax
, elseif_statement
) =
parse_if_body_helper parser_elseif in
let elseif_syntax = make_elseif_clause elseif_token elseif_left_paren
elseif_condition_expr elseif_right_paren elseif_statement in
let elseif_syntax = match Token.kind elseif_opening_token with
| Colon ->
make_alternate_elseif_clause
elseif_token
elseif_left_paren
elseif_condition_expr
elseif_right_paren
elseif_opening_token_syntax
elseif_statement
| _ ->
make_elseif_clause
elseif_token
elseif_left_paren
elseif_condition_expr
elseif_right_paren
elseif_statement in
(parser_elseif, Some elseif_syntax)
else
(parser_elseif, None)
@@ -511,19 +552,59 @@ module WithExpressionAndDeclAndTypeParser
if is_missing else_token then
(parser_else, else_token)
else
let (parser_else, else_consequence) = parse_statement parser_else in
let else_syntax = make_else_clause else_token else_consequence in
(parser_else, else_syntax)
let parser1, opening_token = next_token parser_else in
match Token.kind opening_token with
| Colon ->
let opening_token_syntax = make_token opening_token in
let (parser_else, else_consequence) =
parse_alternate_if_block parser1 parse_statement in
(parser_else, make_alternate_else_clause else_token opening_token_syntax else_consequence)
| _ ->
let (parser_else, else_consequence) = parse_statement parser_else in
(parser_else, make_else_clause else_token else_consequence)
in
let (parser, if_keyword_token) = assert_token parser If in
let (parser, if_left_paren, if_expr, if_right_paren, if_consequence) =
let ( parser
, if_left_paren
, if_expr
, if_right_paren
, if_opening_token
, if_opening_token_syntax
, if_consequence
) =
parse_if_body_helper parser in
let (parser, elseif_syntax) =
parse_list_until_none parser parse_elseif_opt in
let (parser, else_syntax) = parse_else_opt parser in
let syntax = make_if_statement if_keyword_token if_left_paren if_expr
if_right_paren if_consequence elseif_syntax else_syntax in
(parser, syntax)
match Token.kind if_opening_token with
| Colon ->
let (parser, closing_token) = require_token parser Endif
(SyntaxError.error1059 Endif) in
let (parser, semicolon_token) = require_semicolon parser in
( parser
, make_alternate_if_statement
if_keyword_token
if_left_paren
if_expr
if_right_paren
if_opening_token_syntax
if_consequence
elseif_syntax
else_syntax
closing_token
semicolon_token
)
| _ ->
( parser
, make_if_statement
if_keyword_token
if_left_paren
if_expr
if_right_paren
if_consequence
elseif_syntax
else_syntax
)
and parse_switch_statement parser =
(* SPEC:
Oops, something went wrong.

0 comments on commit dcc7796

Please sign in to comment.