mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 20:21:21 +01:00
Parsing for if-statements, following #285.
This commit is contained in:
@@ -28,6 +28,10 @@ CARBON_PARSE_NODE_KIND(FileEnd)
|
||||
CARBON_PARSE_NODE_KIND(CodeBlockEnd)
|
||||
CARBON_PARSE_NODE_KIND(CodeBlock)
|
||||
CARBON_PARSE_NODE_KIND(ExpressionStatement)
|
||||
CARBON_PARSE_NODE_KIND(IfStatement)
|
||||
CARBON_PARSE_NODE_KIND(IfStatementElse)
|
||||
CARBON_PARSE_NODE_KIND(Condition)
|
||||
CARBON_PARSE_NODE_KIND(ConditionEnd)
|
||||
|
||||
// Expressions.
|
||||
CARBON_PARSE_NODE_KIND(Literal)
|
||||
|
||||
@@ -516,6 +516,133 @@ TEST_F(ParseTreeTest, VariableDeclarations) {
|
||||
MatchFileEnd()}));
|
||||
}
|
||||
|
||||
TEST_F(ParseTreeTest, IfNoElse) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(
|
||||
"fn F() {\n"
|
||||
" if (a)\n"
|
||||
" if (b)\n"
|
||||
" if (c)\n"
|
||||
" d;\n"
|
||||
"}");
|
||||
ParseTree tree = ParseTree::Parse(tokens, consumer);
|
||||
EXPECT_FALSE(tree.HasErrors());
|
||||
|
||||
EXPECT_THAT(tree,
|
||||
MatchParseTreeNodes(
|
||||
{MatchFunctionDeclaration(
|
||||
MatchDeclaredName("F"),
|
||||
MatchParameterList(MatchParameterListEnd()),
|
||||
MatchCodeBlock(
|
||||
MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("a"),
|
||||
MatchConditionEnd()),
|
||||
MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("b"),
|
||||
MatchConditionEnd()),
|
||||
MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("c"),
|
||||
MatchConditionEnd()),
|
||||
MatchExpressionStatement(
|
||||
MatchNameReference("d"))))),
|
||||
MatchCodeBlockEnd())),
|
||||
MatchFileEnd()}));
|
||||
}
|
||||
|
||||
TEST_F(ParseTreeTest, IfElse) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(
|
||||
"fn F() {\n"
|
||||
" if (a)\n"
|
||||
" if (b)\n"
|
||||
" c;\n"
|
||||
" else\n"
|
||||
" d;\n"
|
||||
" else\n"
|
||||
" e;\n"
|
||||
" if (x) { G(1); }\n"
|
||||
" else if (x) { G(2); }\n"
|
||||
" else { G(3); }\n"
|
||||
"}");
|
||||
ParseTree tree = ParseTree::Parse(tokens, consumer);
|
||||
EXPECT_FALSE(tree.HasErrors());
|
||||
|
||||
EXPECT_THAT(
|
||||
tree,
|
||||
MatchParseTreeNodes(
|
||||
{MatchFunctionDeclaration(
|
||||
MatchDeclaredName("F"),
|
||||
MatchParameterList(MatchParameterListEnd()),
|
||||
MatchCodeBlock(
|
||||
MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("a"),
|
||||
MatchConditionEnd()),
|
||||
MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("b"),
|
||||
MatchConditionEnd()),
|
||||
MatchExpressionStatement(MatchNameReference("c")),
|
||||
MatchIfStatementElse(),
|
||||
MatchExpressionStatement(MatchNameReference("d"))),
|
||||
MatchIfStatementElse(),
|
||||
MatchExpressionStatement(MatchNameReference("e"))),
|
||||
MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("x"),
|
||||
MatchConditionEnd()),
|
||||
MatchCodeBlock(
|
||||
MatchExpressionStatement(MatchCallExpression(
|
||||
MatchNameReference("G"), MatchLiteral("1"),
|
||||
MatchCallExpressionEnd())),
|
||||
MatchCodeBlockEnd()),
|
||||
MatchIfStatementElse(),
|
||||
MatchIfStatement(
|
||||
MatchCondition(MatchNameReference("x"),
|
||||
MatchConditionEnd()),
|
||||
MatchCodeBlock(
|
||||
MatchExpressionStatement(MatchCallExpression(
|
||||
MatchNameReference("G"), MatchLiteral("2"),
|
||||
MatchCallExpressionEnd())),
|
||||
MatchCodeBlockEnd()),
|
||||
MatchIfStatementElse(),
|
||||
MatchCodeBlock(
|
||||
MatchExpressionStatement(MatchCallExpression(
|
||||
MatchNameReference("G"), MatchLiteral("3"),
|
||||
MatchCallExpressionEnd())),
|
||||
MatchCodeBlockEnd()))),
|
||||
MatchCodeBlockEnd())),
|
||||
MatchFileEnd()}));
|
||||
}
|
||||
|
||||
TEST_F(ParseTreeTest, IfError) {
|
||||
TokenizedBuffer tokens = GetTokenizedBuffer(
|
||||
"fn F() {\n"
|
||||
" if a {}\n"
|
||||
" if () {}\n"
|
||||
" if (b c) {}\n"
|
||||
" if (d)\n"
|
||||
"}");
|
||||
ParseTree tree = ParseTree::Parse(tokens, consumer);
|
||||
EXPECT_TRUE(tree.HasErrors());
|
||||
|
||||
EXPECT_THAT(
|
||||
tree, MatchParseTreeNodes(
|
||||
{MatchFunctionDeclaration(
|
||||
MatchDeclaredName("F"),
|
||||
MatchParameterList(MatchParameterListEnd()),
|
||||
MatchCodeBlock(
|
||||
MatchIfStatement(HasError, MatchNameReference("a"),
|
||||
MatchCodeBlock(MatchCodeBlockEnd())),
|
||||
MatchIfStatement(
|
||||
MatchCondition(HasError, MatchConditionEnd()),
|
||||
MatchCodeBlock(MatchCodeBlockEnd())),
|
||||
MatchIfStatement(
|
||||
MatchCondition(HasError, MatchNameReference("b"),
|
||||
MatchConditionEnd()),
|
||||
MatchCodeBlock(MatchCodeBlockEnd())),
|
||||
MatchIfStatement(
|
||||
HasError, MatchCondition(MatchNameReference("d"),
|
||||
MatchConditionEnd())),
|
||||
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);
|
||||
|
||||
+64
-1
@@ -9,6 +9,7 @@
|
||||
#include "lexer/token_kind.h"
|
||||
#include "lexer/tokenized_buffer.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "parser/parse_node_kind.h"
|
||||
#include "parser/parse_tree.h"
|
||||
@@ -65,10 +66,24 @@ struct ExpectedExpression : SimpleDiagnostic<ExpectedExpression> {
|
||||
static constexpr llvm::StringLiteral Message = "Expected expression.";
|
||||
};
|
||||
|
||||
struct ExpectedParenAfter : SimpleDiagnostic<ExpectedParenAfter> {
|
||||
static constexpr llvm::StringLiteral ShortName = "syntax-error";
|
||||
static constexpr const char* Message = "Expected `(` after `{0}`.";
|
||||
|
||||
TokenKind introducer;
|
||||
|
||||
auto Format() -> std::string {
|
||||
return llvm::formatv(Message, introducer.GetFixedSpelling()).str();
|
||||
}
|
||||
};
|
||||
|
||||
struct ExpectedCloseParen : SimpleDiagnostic<ExpectedCloseParen> {
|
||||
static constexpr llvm::StringLiteral ShortName = "syntax-error";
|
||||
static constexpr llvm::StringLiteral Message =
|
||||
"Unexpected tokens before `)`.";
|
||||
|
||||
// TODO: Include the location of the matching open paren in the diagnostic.
|
||||
TokenizedBuffer::Token open_paren;
|
||||
};
|
||||
|
||||
struct ExpectedSemiAfterExpression
|
||||
@@ -495,7 +510,8 @@ auto ParseTree::Parser::ParseParenExpression() -> llvm::Optional<Node> {
|
||||
|
||||
if (tokens.GetKind(*position) != TokenKind::CloseParen()) {
|
||||
if (!has_errors) {
|
||||
emitter.EmitError<ExpectedCloseParen>(*position);
|
||||
emitter.EmitError<ExpectedCloseParen>(*position,
|
||||
{.open_paren = open_paren});
|
||||
has_errors = true;
|
||||
}
|
||||
SkipTo(tokens.GetMatchedClosingToken(open_paren));
|
||||
@@ -712,11 +728,58 @@ auto ParseTree::Parser::ParseExpressionStatement() -> llvm::Optional<Node> {
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
auto ParseTree::Parser::ParseParenCondition(TokenKind introducer)
|
||||
-> llvm::Optional<Node> {
|
||||
// `(` expression `)`
|
||||
auto start = StartSubtree();
|
||||
auto open_paren = ConsumeIf(TokenKind::OpenParen());
|
||||
if (!open_paren) {
|
||||
emitter.EmitError<ExpectedParenAfter>(*position,
|
||||
{.introducer = introducer});
|
||||
}
|
||||
|
||||
auto expr = ParseExpression();
|
||||
|
||||
if (!open_paren) {
|
||||
// Don't expect a matching closing paren if there wasn't an opening paren.
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
bool has_errors = false;
|
||||
if (tokens.GetKind(*position) != TokenKind::CloseParen()) {
|
||||
emitter.EmitError<ExpectedCloseParen>(*position,
|
||||
{.open_paren = *open_paren});
|
||||
SkipTo(tokens.GetMatchedClosingToken(*open_paren));
|
||||
has_errors = true;
|
||||
}
|
||||
AddLeafNode(ParseNodeKind::ConditionEnd(), Consume(TokenKind::CloseParen()));
|
||||
|
||||
return AddNode(ParseNodeKind::Condition(), *open_paren, start,
|
||||
/*has_errors=*/!expr || has_errors);
|
||||
}
|
||||
|
||||
auto ParseTree::Parser::ParseIfStatement() -> llvm::Optional<Node> {
|
||||
auto start = StartSubtree();
|
||||
auto if_token = Consume(TokenKind::IfKeyword());
|
||||
auto cond = ParseParenCondition(TokenKind::IfKeyword());
|
||||
auto then_case = ParseStatement();
|
||||
bool else_has_errors = false;
|
||||
if (ConsumeAndAddLeafNodeIf(TokenKind::ElseKeyword(),
|
||||
ParseNodeKind::IfStatementElse())) {
|
||||
else_has_errors = !ParseStatement();
|
||||
}
|
||||
return AddNode(ParseNodeKind::IfStatement(), if_token, start,
|
||||
/*has_errors=*/!cond || !then_case || else_has_errors);
|
||||
}
|
||||
|
||||
auto ParseTree::Parser::ParseStatement() -> llvm::Optional<Node> {
|
||||
switch (tokens.GetKind(*position)) {
|
||||
case TokenKind::VarKeyword():
|
||||
return ParseVariableDeclaration();
|
||||
|
||||
case TokenKind::IfKeyword():
|
||||
return ParseIfStatement();
|
||||
|
||||
case TokenKind::OpenCurlyBrace():
|
||||
return ParseCodeBlock();
|
||||
|
||||
|
||||
@@ -171,6 +171,12 @@ class ParseTree::Parser {
|
||||
// Parses an expression statement: an expression followed by a semicolon.
|
||||
auto ParseExpressionStatement() -> llvm::Optional<Node>;
|
||||
|
||||
// Parses the parenthesized condition in an if-statement.
|
||||
auto ParseParenCondition(TokenKind introducer) -> llvm::Optional<Node>;
|
||||
|
||||
// Parses an if-statement.
|
||||
auto ParseIfStatement() -> llvm::Optional<Node>;
|
||||
|
||||
// Parses a statement.
|
||||
auto ParseStatement() -> llvm::Optional<Node>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user