[toolchain] Parse postfix operator * as a type operator. (#576)

This commit is contained in:
Richard Smith
2021-06-14 16:12:14 -07:00
committed by GitHub
parent 61b30b1243
commit 1b122924e8
12 changed files with 488 additions and 73 deletions
+107
View File
@@ -530,6 +530,113 @@ TEST_F(ParseTreeTest, Operators) {
MatchFileEnd()}));
}
TEST_F(ParseTreeTest, OperatorFixity) {
TokenizedBuffer tokens = GetTokenizedBuffer(
"fn F(p: Int*, n: Int) {\n"
" var q: Int* = p;\n"
" var t: Type = Int*;\n"
" t = t**;\n"
" n = n * n;\n"
" n = n*n;\n"
" G(Int*, n * n);\n"
"}");
ParseTree tree = ParseTree::Parse(tokens, consumer);
EXPECT_FALSE(tree.HasErrors());
EXPECT_THAT(
tree,
MatchParseTreeNodes(
{MatchFunctionDeclaration(
MatchDeclaredName("F"),
MatchParameters(
MatchPatternBinding(
MatchDeclaredName("p"),
MatchPostfixOperator(MatchNameReference("Int"), "*")),
MatchParameterListComma(),
MatchPatternBinding(MatchDeclaredName("n"),
MatchNameReference("Int"))),
MatchCodeBlock(
MatchVariableDeclaration(
MatchPatternBinding(MatchDeclaredName("q"),
MatchPostfixOperator(
MatchNameReference("Int"), "*")),
MatchVariableInitializer(MatchNameReference("p")),
MatchDeclarationEnd()),
MatchVariableDeclaration(
MatchPatternBinding(MatchDeclaredName("t"),
MatchNameReference("Type")),
MatchVariableInitializer(MatchPostfixOperator(
MatchNameReference("Int"), "*")),
MatchDeclarationEnd()),
MatchExpressionStatement(MatchInfixOperator(
MatchNameReference("t"), "=",
MatchPostfixOperator(
MatchPostfixOperator(MatchNameReference("t"), "*"),
"*"))),
MatchExpressionStatement(MatchInfixOperator(
MatchNameReference("n"), "=",
MatchInfixOperator(MatchNameReference("n"), "*",
MatchNameReference("n")))),
MatchExpressionStatement(MatchInfixOperator(
MatchNameReference("n"), "=",
MatchInfixOperator(MatchNameReference("n"), "*",
MatchNameReference("n")))),
MatchExpressionStatement(MatchCallExpression(
MatchNameReference("G"),
MatchPostfixOperator(MatchNameReference("Int"), "*"),
MatchCallExpressionComma(),
MatchInfixOperator(MatchNameReference("n"), "*",
MatchNameReference("n")),
MatchCallExpressionEnd())),
MatchCodeBlockEnd())),
MatchFileEnd()}));
}
TEST_F(ParseTreeTest, OperatorWhitespaceErrors) {
// Test dispositions: Recovered means we issued an error but recovered a
// proper parse tree; Failed means we didn't fully recover from the error.
enum Kind { Valid, Recovered, Failed };
struct Testcase {
const char* input;
Kind kind;
} testcases[] = {
{"var v: Type = Int*;", Valid},
{"var v: Type = Int *;", Recovered},
{"var v: Type = Int* ;", Valid},
{"var v: Type = Int * ;", Recovered},
{"var n: Int = n * n;", Valid},
{"var n: Int = n*n;", Valid},
{"var n: Int = (n)*3;", Valid},
{"var n: Int = 3*(n);", Valid},
{"var n: Int = n *n;", Recovered},
// FIXME: We could figure out that this first Failed example is infix
// with one-token lookahead.
{"var n: Int = n* n;", Failed},
{"var n: Int = n* -n;", Failed},
{"var n: Int = n* *p;", Failed},
// FIXME: We try to form (n*)*p and reject due to missing parentheses
// before we notice the missing whitespace around the second `*`.
// It'd be better to (somehow) form n*(*p) and reject due to the missing
// whitespace around the first `*`.
{"var n: Int = n**p;", Failed},
{"var n: Int = -n;", Valid},
{"var n: Int = - n;", Recovered},
{"var n: Int =-n;", Valid},
{"var n: Int =- n;", Recovered},
{"var n: Int = F(Int *);", Recovered},
{"var n: Int = F(Int *, 0);", Recovered},
};
for (auto [input, kind] : testcases) {
TokenizedBuffer tokens = GetTokenizedBuffer(input);
ErrorTrackingDiagnosticConsumer error_tracker(consumer);
ParseTree tree = ParseTree::Parse(tokens, error_tracker);
EXPECT_THAT(tree.HasErrors(), Eq(kind == Failed)) << input;
EXPECT_THAT(error_tracker.SeenError(), Eq(kind != Valid)) << input;
}
}
TEST_F(ParseTreeTest, VariableDeclarations) {
TokenizedBuffer tokens = GetTokenizedBuffer(
"var v: Int = 0;\n"