Parsing for if-statements, following #285.

This commit is contained in:
Richard Smith
2021-04-19 17:09:54 -07:00
parent 8280be1c36
commit 5d6947186d
4 changed files with 201 additions and 1 deletions
+4
View File
@@ -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)
+127
View File
@@ -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
View File
@@ -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();
+6
View File
@@ -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>;