Skip to content

Commit

Permalink
Parsing for while, break, and continue statements, following #340.
Browse files Browse the repository at this point in the history
  • Loading branch information
zygoloid committed Apr 20, 2021
1 parent aa3f1b2 commit 2cf976f
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 0 deletions.
4 changes: 4 additions & 0 deletions parser/parse_node_kind.def
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,12 @@ CARBON_PARSE_NODE_KIND(CodeBlock)
CARBON_PARSE_NODE_KIND(ExpressionStatement)
CARBON_PARSE_NODE_KIND(IfStatement)
CARBON_PARSE_NODE_KIND(IfStatementElse)
CARBON_PARSE_NODE_KIND(WhileStatement)
CARBON_PARSE_NODE_KIND(Condition)
CARBON_PARSE_NODE_KIND(ConditionEnd)
CARBON_PARSE_NODE_KIND(ContinueStatement)
CARBON_PARSE_NODE_KIND(BreakStatement)
CARBON_PARSE_NODE_KIND(StatementEnd)

// Expressions.
CARBON_PARSE_NODE_KIND(Literal)
Expand Down
36 changes: 36 additions & 0 deletions parser/parse_tree_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,42 @@ TEST_F(ParseTreeTest, IfError) {
MatchFileEnd()}));
}

TEST_F(ParseTreeTest, WhileBreakContinue) {
TokenizedBuffer tokens = GetTokenizedBuffer(
"fn F() {\n"
" while (a) {\n"
" if (b)\n"
" break;\n"
" if (c)\n"
" continue;\n"
"}");
ParseTree tree = ParseTree::Parse(tokens, consumer);
EXPECT_FALSE(tree.HasErrors());

EXPECT_THAT(
tree,
MatchParseTreeNodes(
{MatchFunctionDeclaration(
MatchDeclaredName("F"),
MatchParameterList(MatchParameterListEnd()),
MatchCodeBlock(
MatchWhileStatement(
MatchCondition(MatchNameReference("a"),
MatchConditionEnd()),
MatchCodeBlock(
MatchIfStatement(
MatchCondition(MatchNameReference("b"),
MatchConditionEnd()),
MatchBreakStatement(MatchStatementEnd())),
MatchIfStatement(
MatchCondition(MatchNameReference("c"),
MatchConditionEnd()),
MatchContinueStatement(MatchStatementEnd())),
MatchCodeBlockEnd())),
MatchCodeBlockEnd())),
MatchFileEnd()}));
}

auto GetAndDropLine(llvm::StringRef& s) -> std::string {
auto newline_offset = s.find_first_of('\n');
llvm::StringRef line = s.slice(0, newline_offset);
Expand Down
45 changes: 45 additions & 0 deletions parser/parser_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@ struct ExpectedSemiAfterExpression
"Expected `;` after expression.";
};

struct ExpectedSemiAfter : SimpleDiagnostic<ExpectedSemiAfter> {
static constexpr llvm::StringLiteral ShortName = "syntax-error";
static constexpr const char* Message = "Expected `;` after `{0}`.";

TokenKind preceding;

auto Format() -> std::string {
return llvm::formatv(Message, preceding.GetFixedSpelling()).str();
}
};

struct ExpectedIdentifierAfterDot
: SimpleDiagnostic<ExpectedIdentifierAfterDot> {
static constexpr llvm::StringLiteral ShortName = "syntax-error";
Expand Down Expand Up @@ -758,6 +769,31 @@ auto ParseTree::Parser::ParseIfStatement() -> llvm::Optional<Node> {
/*has_errors=*/!cond || !then_case || else_has_errors);
}

auto ParseTree::Parser::ParseWhileStatement() -> llvm::Optional<Node> {
auto start = StartSubtree();
auto while_token = Consume(TokenKind::WhileKeyword());
auto cond = ParseParenCondition(TokenKind::WhileKeyword());
auto body = ParseStatement();
return AddNode(ParseNodeKind::WhileStatement(), while_token, start,
/*has_errors=*/!cond || !body);
}

auto ParseTree::Parser::ParseKeywordStatement(ParseNodeKind kind)
-> llvm::Optional<Node> {
auto keyword_kind = tokens.GetKind(*position);
assert(keyword_kind.IsKeyword());

auto start = StartSubtree();
auto keyword = Consume(keyword_kind);
auto semi =
ConsumeAndAddLeafNodeIf(TokenKind::Semi(), ParseNodeKind::StatementEnd());
if (!semi) {
emitter.EmitError<ExpectedSemiAfter>(*position,
{.preceding = keyword_kind});
}
return AddNode(kind, keyword, start, /*has_errors=*/!semi);
}

auto ParseTree::Parser::ParseStatement() -> llvm::Optional<Node> {
switch (tokens.GetKind(*position)) {
case TokenKind::VarKeyword():
Expand All @@ -766,6 +802,15 @@ auto ParseTree::Parser::ParseStatement() -> llvm::Optional<Node> {
case TokenKind::IfKeyword():
return ParseIfStatement();

case TokenKind::WhileKeyword():
return ParseWhileStatement();

case TokenKind::ContinueKeyword():
return ParseKeywordStatement(ParseNodeKind::ContinueStatement());

case TokenKind::BreakKeyword():
return ParseKeywordStatement(ParseNodeKind::BreakStatement());

case TokenKind::OpenCurlyBrace():
return ParseCodeBlock();

Expand Down
6 changes: 6 additions & 0 deletions parser/parser_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ class ParseTree::Parser {
// Parses an if-statement.
auto ParseIfStatement() -> llvm::Optional<Node>;

// Parses a while-statement.
auto ParseWhileStatement() -> llvm::Optional<Node>;

// Parses a statement of the form `keyword;` such as `break;` or `continue;`.
auto ParseKeywordStatement(ParseNodeKind kind) -> llvm::Optional<Node>;

// Parses a statement.
auto ParseStatement() -> llvm::Optional<Node>;

Expand Down

0 comments on commit 2cf976f

Please sign in to comment.