Skip to content

[Syntax] Parse 'switch' statement #15537

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 24 additions & 10 deletions lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1966,6 +1966,7 @@ ParserResult<Stmt> Parser::parseStmtForEach(LabeledStmtInfo LabelInfo) {
/// stmt-switch:
/// (identifier ':')? 'switch' expr-basic '{' stmt-case+ '}'
ParserResult<Stmt> Parser::parseStmtSwitch(LabeledStmtInfo LabelInfo) {
SyntaxContext->setCreateSyntax(SyntaxKind::SwitchStmt);
SourceLoc SwitchLoc = consumeToken(tok::kw_switch);

ParserStatus Status;
Expand Down Expand Up @@ -2020,6 +2021,7 @@ ParserResult<Stmt> Parser::parseStmtSwitch(LabeledStmtInfo LabelInfo) {

ParserStatus
Parser::parseStmtCases(SmallVectorImpl<ASTNode> &cases, bool IsActive) {
SyntaxParsingContext CasesContext(SyntaxContext, SyntaxKind::SwitchCaseList);
ParserStatus Status;
while (Tok.isNot(tok::r_brace, tok::eof,
tok::pound_endif, tok::pound_elseif, tok::pound_else)) {
Expand Down Expand Up @@ -2076,21 +2078,30 @@ static ParserStatus parseStmtCase(Parser &P, SourceLoc &CaseLoc,
SmallVectorImpl<CaseLabelItem> &LabelItems,
SmallVectorImpl<VarDecl *> &BoundDecls,
SourceLoc &ColonLoc) {
SyntaxParsingContext CaseContext(P.SyntaxContext,
SyntaxKind::SwitchCaseLabel);
ParserStatus Status;
bool isFirst = true;

CaseLoc = P.consumeToken(tok::kw_case);

do {
GuardedPattern PatternResult;
parseGuardedPattern(P, PatternResult, Status, BoundDecls,
GuardedPatternContext::Case, isFirst);
LabelItems.push_back(CaseLabelItem(/*IsDefault=*/false,
PatternResult.ThePattern,
PatternResult.WhereLoc,
PatternResult.Guard));
isFirst = false;
} while (P.consumeIf(tok::comma));
{
SyntaxParsingContext ListContext(P.SyntaxContext, SyntaxKind::CaseItemList);

while (true) {
SyntaxParsingContext ItemContext(P.SyntaxContext, SyntaxKind::CaseItem);
GuardedPattern PatternResult;
parseGuardedPattern(P, PatternResult, Status, BoundDecls,
GuardedPatternContext::Case, isFirst);
LabelItems.push_back(
CaseLabelItem(/*IsDefault=*/false, PatternResult.ThePattern,
PatternResult.WhereLoc, PatternResult.Guard));
isFirst = false;
if (P.consumeIf(tok::comma))
continue;
break;
}
}

ColonLoc = P.Tok.getLoc();
if (!P.Tok.is(tok::colon)) {
Expand All @@ -2106,6 +2117,8 @@ static ParserStatus
parseStmtCaseDefault(Parser &P, SourceLoc &CaseLoc,
SmallVectorImpl<CaseLabelItem> &LabelItems,
SourceLoc &ColonLoc) {
SyntaxParsingContext CaseContext(P.SyntaxContext,
SyntaxKind::SwitchDefaultLabel);
ParserStatus Status;

CaseLoc = P.consumeToken(tok::kw_default);
Expand Down Expand Up @@ -2137,6 +2150,7 @@ parseStmtCaseDefault(Parser &P, SourceLoc &CaseLoc,
}

ParserResult<CaseStmt> Parser::parseStmtCase(bool IsActive) {
SyntaxParsingContext CaseContext(SyntaxContext, SyntaxKind::SwitchCase);
// A case block has its own scope for variables bound out of the pattern.
Scope S(this, ScopeKind::CaseVars, !IsActive);

Expand Down
2 changes: 0 additions & 2 deletions lib/Syntax/Status.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@
* GuardStmt
* WhileStmt
* ForInStmt

### Not-started (UnknownStmt):
* SwitchStmt

## Pattern
Expand Down
57 changes: 35 additions & 22 deletions test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds
Original file line number Diff line number Diff line change
Expand Up @@ -302,18 +302,18 @@ class C <MemberDeclBlock>{<VariableDecl><Attribute>
}</CodeBlock></FunctionDecl>
}</MemberDeclBlock></ClassDecl><DoStmt>

do <CodeBlock>{
switch <IdentifierExpr>foo </IdentifierExpr>{
case <ValueBindingPattern>let <IdentifierPattern>a</IdentifierPattern></ValueBindingPattern>: <BreakStmt>break</BreakStmt>
case <ValueBindingPattern>let <ExpressionPattern><SequenceExpr><UnresolvedPatternExpr><IdentifierPattern>a </IdentifierPattern></UnresolvedPatternExpr><AsExpr>as <SimpleTypeIdentifier>Int</SimpleTypeIdentifier></AsExpr></SequenceExpr></ExpressionPattern></ValueBindingPattern>: <BreakStmt>break</BreakStmt>
case <ValueBindingPattern>let <ExpressionPattern><TupleExpr>(<TupleElement><UnresolvedPatternExpr><IdentifierPattern>a</IdentifierPattern></UnresolvedPatternExpr>, </TupleElement><TupleElement><UnresolvedPatternExpr><IdentifierPattern>b</IdentifierPattern></UnresolvedPatternExpr></TupleElement>)</TupleExpr></ExpressionPattern></ValueBindingPattern>: <BreakStmt>break</BreakStmt>
case <ExpressionPattern><TupleExpr>(<TupleElement><UnresolvedPatternExpr><ValueBindingPattern>let <IdentifierPattern>a</IdentifierPattern></ValueBindingPattern></UnresolvedPatternExpr>, </TupleElement><TupleElement><UnresolvedPatternExpr><ValueBindingPattern>var <IdentifierPattern>b</IdentifierPattern></ValueBindingPattern></UnresolvedPatternExpr></TupleElement>)</TupleExpr></ExpressionPattern>: <BreakStmt>break</BreakStmt>
case <IsTypePattern>is <SimpleTypeIdentifier>Int</SimpleTypeIdentifier></IsTypePattern>: <BreakStmt>break</BreakStmt>
case <ValueBindingPattern>let <ExpressionPattern><FunctionCallExpr><ImplicitMemberExpr>.bar</ImplicitMemberExpr>(<FunctionCallArgument><UnresolvedPatternExpr><IdentifierPattern>x</IdentifierPattern></UnresolvedPatternExpr></FunctionCallArgument>)</FunctionCallExpr></ExpressionPattern></ValueBindingPattern>: <BreakStmt>break</BreakStmt>
case <ExpressionPattern><MemberAccessExpr><IdentifierExpr>MyEnum</IdentifierExpr>.foo</MemberAccessExpr></ExpressionPattern>: <BreakStmt>break</BreakStmt>
case <ValueBindingPattern>let <ExpressionPattern><SequenceExpr><UnresolvedPatternExpr><IdentifierPattern>a </IdentifierPattern></UnresolvedPatternExpr><AsExpr>as <SimpleTypeIdentifier>Int</SimpleTypeIdentifier></AsExpr></SequenceExpr></ExpressionPattern></ValueBindingPattern>: <BreakStmt>break</BreakStmt>
case <ValueBindingPattern>let <ExpressionPattern><OptionalChainingExpr><UnresolvedPatternExpr><IdentifierPattern>a</IdentifierPattern></UnresolvedPatternExpr>?</OptionalChainingExpr></ExpressionPattern></ValueBindingPattern>: <BreakStmt>break</BreakStmt>
}
do <CodeBlock>{<SwitchStmt>
switch <IdentifierExpr>foo </IdentifierExpr>{<SwitchCase><SwitchCaseLabel>
case <CaseItem><ValueBindingPattern>let <IdentifierPattern>a</IdentifierPattern></ValueBindingPattern></CaseItem>: </SwitchCaseLabel><BreakStmt>break</BreakStmt></SwitchCase><SwitchCase><SwitchCaseLabel>
case <CaseItem><ValueBindingPattern>let <ExpressionPattern><SequenceExpr><UnresolvedPatternExpr><IdentifierPattern>a </IdentifierPattern></UnresolvedPatternExpr><AsExpr>as <SimpleTypeIdentifier>Int</SimpleTypeIdentifier></AsExpr></SequenceExpr></ExpressionPattern></ValueBindingPattern></CaseItem>: </SwitchCaseLabel><BreakStmt>break</BreakStmt></SwitchCase><SwitchCase><SwitchCaseLabel>
case <CaseItem><ValueBindingPattern>let <ExpressionPattern><TupleExpr>(<TupleElement><UnresolvedPatternExpr><IdentifierPattern>a</IdentifierPattern></UnresolvedPatternExpr>, </TupleElement><TupleElement><UnresolvedPatternExpr><IdentifierPattern>b</IdentifierPattern></UnresolvedPatternExpr></TupleElement>)</TupleExpr></ExpressionPattern></ValueBindingPattern></CaseItem>: </SwitchCaseLabel><BreakStmt>break</BreakStmt></SwitchCase><SwitchCase><SwitchCaseLabel>
case <CaseItem><ExpressionPattern><TupleExpr>(<TupleElement><UnresolvedPatternExpr><ValueBindingPattern>let <IdentifierPattern>a</IdentifierPattern></ValueBindingPattern></UnresolvedPatternExpr>, </TupleElement><TupleElement><UnresolvedPatternExpr><ValueBindingPattern>var <IdentifierPattern>b</IdentifierPattern></ValueBindingPattern></UnresolvedPatternExpr></TupleElement>)</TupleExpr></ExpressionPattern></CaseItem>: </SwitchCaseLabel><BreakStmt>break</BreakStmt></SwitchCase><SwitchCase><SwitchCaseLabel>
case <CaseItem><IsTypePattern>is <SimpleTypeIdentifier>Int</SimpleTypeIdentifier></IsTypePattern></CaseItem>: </SwitchCaseLabel><BreakStmt>break</BreakStmt></SwitchCase><SwitchCase><SwitchCaseLabel>
case <CaseItem><ValueBindingPattern>let <ExpressionPattern><FunctionCallExpr><ImplicitMemberExpr>.bar</ImplicitMemberExpr>(<FunctionCallArgument><UnresolvedPatternExpr><IdentifierPattern>x</IdentifierPattern></UnresolvedPatternExpr></FunctionCallArgument>)</FunctionCallExpr></ExpressionPattern></ValueBindingPattern></CaseItem>: </SwitchCaseLabel><BreakStmt>break</BreakStmt></SwitchCase><SwitchCase><SwitchCaseLabel>
case <CaseItem><ExpressionPattern><MemberAccessExpr><IdentifierExpr>MyEnum</IdentifierExpr>.foo</MemberAccessExpr></ExpressionPattern></CaseItem>: </SwitchCaseLabel><BreakStmt>break</BreakStmt></SwitchCase><SwitchCase><SwitchCaseLabel>
case <CaseItem><ValueBindingPattern>let <ExpressionPattern><SequenceExpr><UnresolvedPatternExpr><IdentifierPattern>a </IdentifierPattern></UnresolvedPatternExpr><AsExpr>as <SimpleTypeIdentifier>Int</SimpleTypeIdentifier></AsExpr></SequenceExpr></ExpressionPattern></ValueBindingPattern></CaseItem>: </SwitchCaseLabel><BreakStmt>break</BreakStmt></SwitchCase><SwitchCase><SwitchCaseLabel>
case <CaseItem><ValueBindingPattern>let <ExpressionPattern><OptionalChainingExpr><UnresolvedPatternExpr><IdentifierPattern>a</IdentifierPattern></UnresolvedPatternExpr>?</OptionalChainingExpr></ExpressionPattern></ValueBindingPattern></CaseItem>: </SwitchCaseLabel><BreakStmt>break</BreakStmt></SwitchCase>
}</SwitchStmt>
}</CodeBlock></DoStmt><FunctionDecl>

func statementTests<FunctionSignature><ParameterClause>() </ParameterClause></FunctionSignature><CodeBlock>{<DoStmt>
Expand All @@ -327,15 +327,15 @@ func statementTests<FunctionSignature><ParameterClause>() </ParameterClause></Fu
LABEL: repeat <CodeBlock>{ } </CodeBlock>while <BooleanLiteralExpr>false</BooleanLiteralExpr></RepeatWhileStmt><WhileStmt>
while <ConditionElement><BooleanLiteralExpr>true </BooleanLiteralExpr></ConditionElement><CodeBlock>{ }</CodeBlock></WhileStmt><WhileStmt>
LABEL: while <ConditionElement><BooleanLiteralExpr>true </BooleanLiteralExpr></ConditionElement><CodeBlock>{ }</CodeBlock></WhileStmt><DoStmt>
LABEL: do <CodeBlock>{}</CodeBlock></DoStmt>
LABEL: switch <IdentifierExpr>foo </IdentifierExpr>{
case <ExpressionPattern><IntegerLiteralExpr>1</IntegerLiteralExpr></ExpressionPattern>:<FallthroughStmt>
fallthrough</FallthroughStmt>
case <ExpressionPattern><IntegerLiteralExpr>2</IntegerLiteralExpr></ExpressionPattern>:<BreakStmt>
break LABEL</BreakStmt>
case <ExpressionPattern><IntegerLiteralExpr>3</IntegerLiteralExpr></ExpressionPattern>:<BreakStmt>
break</BreakStmt>
}<ForInStmt>
LABEL: do <CodeBlock>{}</CodeBlock></DoStmt><SwitchStmt>
LABEL: switch <IdentifierExpr>foo </IdentifierExpr>{<SwitchCase><SwitchCaseLabel>
case <CaseItem><ExpressionPattern><IntegerLiteralExpr>1</IntegerLiteralExpr></ExpressionPattern></CaseItem>:</SwitchCaseLabel><FallthroughStmt>
fallthrough</FallthroughStmt></SwitchCase><SwitchCase><SwitchCaseLabel>
case <CaseItem><ExpressionPattern><IntegerLiteralExpr>2</IntegerLiteralExpr></ExpressionPattern></CaseItem>:</SwitchCaseLabel><BreakStmt>
break LABEL</BreakStmt></SwitchCase><SwitchCase><SwitchCaseLabel>
case <CaseItem><ExpressionPattern><IntegerLiteralExpr>3</IntegerLiteralExpr></ExpressionPattern></CaseItem>:</SwitchCaseLabel><BreakStmt>
break</BreakStmt></SwitchCase>
}</SwitchStmt><ForInStmt>

for <IdentifierPattern>a </IdentifierPattern>in <IdentifierExpr>b </IdentifierExpr><CodeBlock>{<DeferStmt>
defer <CodeBlock>{ <TupleExpr>() </TupleExpr>}</CodeBlock></DeferStmt><IfStmt>
Expand All @@ -361,7 +361,20 @@ func statementTests<FunctionSignature><ParameterClause>() </ParameterClause></Fu
guard <ConditionElement><OptionalBindingCondition>let <IdentifierPattern>a </IdentifierPattern><InitializerClause>= <IdentifierExpr>b </IdentifierExpr></InitializerClause></OptionalBindingCondition></ConditionElement>else <CodeBlock>{}</CodeBlock></GuardStmt><ForInStmt>

for <ValueBindingPattern>var <IdentifierPattern>i </IdentifierPattern></ValueBindingPattern>in <IdentifierExpr>foo </IdentifierExpr><WhereClause>where <MemberAccessExpr><IdentifierExpr>i</IdentifierExpr>.foo </MemberAccessExpr></WhereClause><CodeBlock>{}</CodeBlock></ForInStmt><ForInStmt>
for case <IsTypePattern>is <SimpleTypeIdentifier>Int </SimpleTypeIdentifier></IsTypePattern>in <IdentifierExpr>foo </IdentifierExpr><CodeBlock>{}</CodeBlock></ForInStmt>
for case <IsTypePattern>is <SimpleTypeIdentifier>Int </SimpleTypeIdentifier></IsTypePattern>in <IdentifierExpr>foo </IdentifierExpr><CodeBlock>{}</CodeBlock></ForInStmt><SwitchStmt>

switch <IdentifierExpr>Foo </IdentifierExpr>{<SwitchCase><SwitchCaseLabel>
case <CaseItem><ExpressionPattern><IdentifierExpr>n1</IdentifierExpr></ExpressionPattern></CaseItem>:</SwitchCaseLabel><BreakStmt>
break</BreakStmt></SwitchCase><SwitchCase><SwitchCaseLabel>
case <CaseItem><ExpressionPattern><IdentifierExpr>n2</IdentifierExpr></ExpressionPattern>, </CaseItem><CaseItem><ExpressionPattern><IdentifierExpr>n3l</IdentifierExpr></ExpressionPattern></CaseItem>:</SwitchCaseLabel><BreakStmt>
break</BreakStmt></SwitchCase><IfConfigDecl>
#if <IdentifierExpr>FOO</IdentifierExpr><SwitchCase><SwitchCaseLabel>
case <CaseItem><ValueBindingPattern>let <ExpressionPattern><TupleExpr>(<TupleElement><UnresolvedPatternExpr><IdentifierPattern>x</IdentifierPattern></UnresolvedPatternExpr>, </TupleElement><TupleElement><UnresolvedPatternExpr><IdentifierPattern>y</IdentifierPattern></UnresolvedPatternExpr></TupleElement>) </TupleExpr></ExpressionPattern></ValueBindingPattern><WhereClause>where <PrefixOperatorExpr>!<IdentifierExpr>x</IdentifierExpr></PrefixOperatorExpr></WhereClause>, </CaseItem><CaseItem><ExpressionPattern><IdentifierExpr>n3l </IdentifierExpr></ExpressionPattern><WhereClause>where <BooleanLiteralExpr>false</BooleanLiteralExpr></WhereClause></CaseItem>:</SwitchCaseLabel><BreakStmt>
break</BreakStmt></SwitchCase>
#endif</IfConfigDecl><SwitchCase><SwitchDefaultLabel>
default:</SwitchDefaultLabel><BreakStmt>
break</BreakStmt></SwitchCase>
}</SwitchStmt>
}</CodeBlock></FunctionDecl><ExtensionDecl>

// MARK: - ExtensionDecl
Expand Down
13 changes: 13 additions & 0 deletions test/Syntax/round_trip_parse_gen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,19 @@ func statementTests() {

for var i in foo where i.foo {}
for case is Int in foo {}

switch Foo {
case n1:
break
case n2, n3l:
break
#if FOO
case let (x, y) where !x, n3l where false:
break
#endif
default:
break
}
}

// MARK: - ExtensionDecl
Expand Down
6 changes: 4 additions & 2 deletions utils/gyb_syntax_support/DeclNodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,13 @@
# if-config-decl -> '#if' expr stmt-list else-if-directive-clause-list
# else-clause? '#endif'
Node('IfConfigDecl', kind='Decl',
traits=['WithStatements'],
children=[
Child('PoundIf', kind='PoundIfToken'),
Child('Condition', kind='Expr'),
Child('Statements', kind='CodeBlockItemList'),
Child('Elements', kind='Syntax',
node_choices=[
Child('Statements', kind='CodeBlockItemList'),
Child('SwitchCases', kind='SwitchCaseList')]),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if there’s anything we could do to try to merge these

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropping WithStatements trait from IfConfigDecl.
But separating IfConfigDecl only for switch-cases (i.e. SwitchCaseIfConfig) doesn't seem the right choice.

Child('ElseifDirectiveClauses', kind='ElseifDirectiveClauseList',
is_optional=True),
Child('ElseClause', kind='ElseDirectiveClause',
Expand Down
9 changes: 7 additions & 2 deletions utils/gyb_syntax_support/StmtNodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@

# switch-case-list -> switch-case switch-case-list?
Node('SwitchCaseList', kind='SyntaxCollection',
element='SwitchCase'),
element='Syntax',
element_choices=['SwitchCase', 'IfConfigDecl']),

# repeat-while-stmt -> label? ':'? 'repeat' code-block 'while' expr ';'?
Node('RepeatWhileStmt', kind='Stmt',
Expand Down Expand Up @@ -257,7 +258,11 @@
Node('SwitchCase', kind='Syntax',
traits=['WithStatements'],
children=[
Child('Label', kind='Syntax'),
Child('Label', kind='Syntax',
node_choices=[
Child('Default', kind='SwitchDefaultLabel'),
Child('Case', kind='SwitchCaseLabel'),
]),
Child('Statements', kind='CodeBlockItemList'),
]),

Expand Down