Finish Parser2 support and switch. (#2381)

Adds remaining expression support, and switches the default to Parser2.

Note, this doesn't delete the current Parser yet. I'll just do that in its own PR.
This commit is contained in:
Jon Ross-Perkins
2022-11-11 08:36:17 -08:00
committed by GitHub
parent d3be5c2827
commit cd93ae6618
5 changed files with 1307 additions and 259 deletions
+1 -3
View File
@@ -28,9 +28,7 @@ auto ParseTree::Parse(TokenizedBuffer& tokens, DiagnosticConsumer& consumer)
TokenDiagnosticEmitter emitter(translator, consumer);
// Delegate to the parser.
// TODO: Edit this to swap between Parser and Parser2. This is manual in order
// to avoid test duplication.
return Parser::Parse(tokens, emitter);
return Parser2::Parse(tokens, emitter);
}
auto ParseTree::postorder() const -> llvm::iterator_range<PostorderIterator> {
+2 -25
View File
@@ -209,7 +209,7 @@ TEST_F(ParseTreeTest, PrintPreorderAsYAML) {
EXPECT_THAT(Yaml::Value::FromText(print_output), ElementsAre(file));
}
TEST_F(ParseTreeTest, RecursionLimit) {
TEST_F(ParseTreeTest, HighRecursion) {
std::string code = "fn Foo() { return ";
code.append(10000, '(');
code.append(10000, ')');
@@ -217,31 +217,8 @@ TEST_F(ParseTreeTest, RecursionLimit) {
TokenizedBuffer tokens = GetTokenizedBuffer(code);
ASSERT_FALSE(tokens.has_errors());
Testing::MockDiagnosticConsumer consumer;
// Recursion might be exceeded multiple times due to quirks in parse tree
// handling; we only need to be sure it's hit at least once for test
// correctness.
EXPECT_CALL(consumer, HandleDiagnostic(IsDiagnosticMessage(
llvm::formatv("Exceeded recursion limit ({0})",
ParseTree::StackDepthLimit)
.str())))
.Times(AtLeast(1));
ParseTree tree = ParseTree::Parse(tokens, consumer);
EXPECT_TRUE(tree.has_errors());
}
TEST_F(ParseTreeTest, ParsePostfixExpressionRegression) {
// Stack depth errors could cause ParsePostfixExpression to infinitely loop
// when calling children and those children error. Because of the fragility of
// stack depth, this tries a few different values.
for (int n = 0; n <= 10; ++n) {
std::string code = "var x: auto = ";
code.append(ParseTree::StackDepthLimit - n, '*');
code += "(z);";
TokenizedBuffer tokens = GetTokenizedBuffer(code);
ASSERT_FALSE(tokens.has_errors());
ParseTree tree = ParseTree::Parse(tokens, consumer);
EXPECT_TRUE(tree.has_errors());
}
EXPECT_FALSE(tree.has_errors());
}
TEST_F(ParseTreeTest, PackageErrors) {
File diff suppressed because it is too large Load Diff
+63 -20
View File
@@ -5,6 +5,7 @@
#ifndef CARBON_TOOLCHAIN_PARSER_PARSER2_H_
#define CARBON_TOOLCHAIN_PARSER_PARSER2_H_
#include "common/check.h"
#include "llvm/ADT/Optional.h"
#include "toolchain/lexer/token_kind.h"
#include "toolchain/lexer/tokenized_buffer.h"
@@ -88,22 +89,32 @@ class Parser2 {
static_assert(sizeof(StateStackEntry) == 12,
"StateStackEntry has unexpected size!");
// Possible return values for FindListToken.
enum class ListTokenKind {
Comma,
Close,
CommaClose,
};
// The kind of brace expression being evaluated.
enum class BraceExpressionKind { Unknown, Value, Type };
Parser2(ParseTree& tree, TokenizedBuffer& tokens,
TokenDiagnosticEmitter& emitter);
auto Parse() -> void;
// Adds a node to the parse tree that is fully parsed, has no children
// ("leaf"), and has a subsequent sibling.
//
// This sets up the next sibling of the node to be the next node in the parse
// tree's preorder sequence.
// Adds a node to the parse tree that has no children (a leaf).
auto AddLeafNode(ParseNodeKind kind, TokenizedBuffer::Token token,
bool has_error = false) -> void;
// Adds a node to the parse tree that has children.
auto AddNode(ParseNodeKind kind, TokenizedBuffer::Token token,
int subtree_start, bool has_error) -> void;
// Returns the current position and moves past it.
auto Consume() -> TokenizedBuffer::Token { return *(position_++); }
// Parses a close paren token corresponding to the given open paren token,
// possibly skipping forward and diagnosing if necessary. Creates a parse node
// of the specified kind if successful.
@@ -163,6 +174,15 @@ class Parser2 {
// whether there's an error, it's expected that parsing continues.
auto DiagnoseOperatorFixity(OperatorFixity fixity) -> void;
// If the current position is a `,`, consumes it, adds the provided token, and
// returns `Comma`. Returns `Close` if the current position is close_token
// (for example, `)`). `CommaClose` indicates it found both (for example,
// `,)`). Handles cases where invalid tokens are present by advancing the
// position, and may emit errors. Pass already_has_error in order to suppress
// duplicate errors.
auto ConsumeListToken(ParseNodeKind comma_kind, TokenKind close_kind,
bool already_has_error) -> ListTokenKind;
// Gets the kind of the next token to be consumed.
auto PositionKind() const -> TokenKind { return tokens_.GetKind(*position_); }
@@ -205,43 +225,66 @@ class Parser2 {
// Pushes a constructed state onto the stack.
auto PushState(StateStackEntry state) -> void {
state_stack_.push_back(state);
// Verify the stack doesn't grow unbounded by programming error.
CARBON_CHECK(state_stack_.size() < (1 << 20));
}
// Propagates an error up the state stack, to the parent state.
auto ReturnErrorOnState() -> void { state_stack_.back().has_error = true; }
// Returns the appropriate ParserState for the input kind.
static auto BraceExpressionKindToParserState(BraceExpressionKind kind,
ParserState type,
ParserState value,
ParserState unknown)
-> ParserState;
// Prints a diagnostic for brace expression syntax errors.
auto HandleBraceExpressionParameterError(StateStackEntry state,
BraceExpressionKind kind) -> void;
// Handles BraceExpressionParameterAs(Type|Value|Unknown).
auto HandleBraceExpressionParameter(BraceExpressionKind kind) -> void;
// Handles BraceExpressionParameterAfterDesignatorAs(Type|Value|Unknown).
auto HandleBraceExpressionParameterAfterDesignator(BraceExpressionKind kind)
-> void;
// Handles BraceExpressionParameterFinishAs(Type|Value|Unknown).
auto HandleBraceExpressionParameterFinish(BraceExpressionKind kind) -> void;
// Handles BraceExpressionFinishAs(Type|Value|Unknown).
auto HandleBraceExpressionFinish(BraceExpressionKind kind) -> void;
// Handles DesignatorAs.
auto HandleDesignator(bool as_struct) -> void;
// When handling errors before the start of the definition, treat it as a
// declaration. Recover to a semicolon when it makes sense as a possible
// function end, otherwise use the fn token for the error.
auto HandleFunctionError(StateStackEntry state, bool skip_past_likely_end)
-> void;
// Handles a code block in the context of a statement scope.
auto HandleCodeBlock() -> void;
// Handles parsing of a function parameter list, including commas and the
// close paren.
auto HandleFunctionParameterList(bool is_start) -> void;
// Handles ParenExpressionParameterFinish(AsUnknown|AsTuple)
auto HandleParenExpressionParameterFinish(bool as_tuple) -> void;
// Handles the start of a pattern.
// If the start of the pattern is invalid, it's the responsibility of the
// outside context to advance past the pattern.
auto HandlePatternStart(PatternKind pattern_kind) -> void;
// Handles a single statement. While typically within a statement block, this
// can also be used for error recovery where we expect a statement block and
// are missing braces.
auto HandleStatement(TokenKind token_kind) -> void;
// Handles a `if` statement at the start `if` token.
auto HandleStatementIf() -> void;
// Handles the end of a pattern.
auto HandlePatternFinish() -> bool;
// Handles the `;` after a keyword statement.
auto HandleStatementKeywordFinish(TokenKind token_kind,
ParseNodeKind node_kind) -> void;
// Handles a `while` statement at the start `while` token.
auto HandleStatementWhile() -> void;
// Handles VarAs(RequireSemicolon|NoSemicolon).
auto HandleVar(bool require_semicolon) -> void;
// Handles VarFinishAs(RequireSemicolon|NoSemicolon).
auto HandleVarFinish(bool require_semicolon) -> void;
// `clang-format` has a bug with spacing around `->` returns in macros. See
// https://bugs.llvm.org/show_bug.cgi?id=48320 for details.
+437 -16
View File
@@ -13,100 +13,521 @@
#error "Must define the x-macro to use this file."
#endif
// Handles the `{` of a brace expression.
//
// If `CloseCurlyBrace`:
// 1. BraceExpressionFinishAsUnknown
// Else:
// 1. BraceExpressionParameterAsUnknown
// 2. BraceExpressionFinishAsUnknown
CARBON_PARSER_STATE(BraceExpression)
// Handles a brace expression parameter. Note this will always start as unknown,
// but should be known after the first valid parameter. All later inconsistent
// parameters are invalid.
//
// If valid:
// 1. DesignatorExpressionAsStruct
// 2. BraceExpressionParameterAfterDesignatorAs(Type|Value|Unknown)
// Else:
// 1. BraceExpressionParameterFinishAs(Type|Value|Unknown)
CARBON_PARSER_STATE(BraceExpressionParameterAsType)
CARBON_PARSER_STATE(BraceExpressionParameterAsValue)
CARBON_PARSER_STATE(BraceExpressionParameterAsUnknown)
// Handles a brace expression parameter after the initial designator. This
// should be at a `:` or `=`, depending on whether it's a type or value literal.
//
// If valid:
// 1. Expression
// 2. BraceExpressionParameterFinishAs(Type|Value|Unknown)
// Else:
// 1. BraceExpressionParameterFinishAs(Type|Value|Unknown)
CARBON_PARSER_STATE(BraceExpressionParameterAfterDesignatorAsType)
CARBON_PARSER_STATE(BraceExpressionParameterAfterDesignatorAsValue)
CARBON_PARSER_STATE(BraceExpressionParameterAfterDesignatorAsUnknown)
// Handles the end of a brace expression parameter.
//
// If `Comma`:
// 1. BraceExpressionParameterAsUnknown
// Else:
// (state done)
CARBON_PARSER_STATE(BraceExpressionParameterFinishAsType)
CARBON_PARSER_STATE(BraceExpressionParameterFinishAsValue)
CARBON_PARSER_STATE(BraceExpressionParameterFinishAsUnknown)
// Handles the `}` of a brace expression.
//
// Always:
// (state done)
CARBON_PARSER_STATE(BraceExpressionFinishAsType)
CARBON_PARSER_STATE(BraceExpressionFinishAsValue)
CARBON_PARSER_STATE(BraceExpressionFinishAsUnknown)
// Handles a call expression `(...)`.
//
// If `CloseParen`:
// 1. CallExpressionFinish
// Else:
// 1. Expression
// 2. CallExpressionParameterFinish
// 3. CallExpressionFinish
CARBON_PARSER_STATE(CallExpression)
// Handles the `,` or `)` after a call parameter.
//
// If `Comma`:
// 1. Expression
// 2. CallExpressionParameterFinish
// Else:
// (state done)
CARBON_PARSER_STATE(CallExpressionParameterFinish)
// Handles finishing the call expression.
//
// Always:
// (state done)
CARBON_PARSER_STATE(CallExpressionFinish)
// Handles processing at the `{` on a typical code block.
//
// If `OpenCurlyBrace`:
// 1. StatementScopeLoop
// 2. CodeBlockFinish
// Else:
// 1. Statement
// 2. CodeBlockFinish
CARBON_PARSER_STATE(CodeBlock)
// Handles processing at the `}` on a typical code block, after a statement
// scope is done.
//
// Always:
// (state done)
CARBON_PARSER_STATE(CodeBlockFinish)
// Handles processing of a declaration scope. Things like fn, class, interface,
// and so on.
CARBON_PARSER_STATE(Declaration)
//
// If `EndOfFile`:
// (state done)
// If `Fn`:
// 1. FunctionIntroducer
// 2. DeclarationLoop
// If `Package`:
// 1. Package
// 2. DeclarationLoop
// If `Semi`:
// 1. DeclarationLoop
// If `Var`:
// 1. Var
// 2. DeclarationLoop
// Else:
// 1. DeclarationLoop
CARBON_PARSER_STATE(DeclarationLoop)
// Handles a designator expression, such as `.z` in `x.(y.z)`.
CARBON_PARSER_STATE(DesignatorExpression)
//
// Always:
// (state done)
CARBON_PARSER_STATE(DesignatorAsExpression)
CARBON_PARSER_STATE(DesignatorAsStruct)
// Handles processing of an expression.
//
// If valid prefix operator:
// 1. Expression
// 2. ExpressionLoopForPrefix
// Else:
// 1. ExpressionInPostfix
// 2. ExpressionLoop
CARBON_PARSER_STATE(Expression)
// Handles processing of an expression.
CARBON_PARSER_STATE(ExpressionLoop)
CARBON_PARSER_STATE(ExpressionLoopForBinary)
CARBON_PARSER_STATE(ExpressionLoopForPrefix)
// Handles the initial part of postfix expressions, such as an identifier or
// literal value, then proceeds to the loop.
//
// If `Identifier` or literal (including type literals):
// 1. ExpressionInPostfixLoop
// If `OpenCurlyBrace`:
// 1. BraceExpression
// 2. ExpressionInPostfixLoop
// If `OpenParen`:
// 1. ParenExpression
// 2. ExpressionInPostfixLoop
// Else:
// (state done)
CARBON_PARSER_STATE(ExpressionInPostfix)
// Handles looping through elements following the initial postfix expression,
// such as designators or parenthesized parameters.
//
// If `Period`:
// 1. DesignatorAsExpression
// 2. ExpressionInPostfixLoop
// If `OpenParen`:
// 1. CallExpression
// 2. ExpressionInPostfixLoop
// Else:
// (state done)
CARBON_PARSER_STATE(ExpressionInPostfixLoop)
// Handles processing of an expression.
//
// If binary operator:
// 1. Expression
// 2. ExpressionLoopForBinary
// If postfix operator:
// 1. ExpressionLoop
// Else:
// (state done)
CARBON_PARSER_STATE(ExpressionLoop)
// Completes an ExpressionLoop pass by adding an infix operator, then goes back
// to ExpressionLoop.
//
// Always:
// 1. ExpressionLoop
CARBON_PARSER_STATE(ExpressionLoopForBinary)
// Completes an ExpressionLoop pass by adding a prefix operator, then goes back
// to ExpressionLoop.
//
// Always:
// 1. ExpressionLoop
CARBON_PARSER_STATE(ExpressionLoopForPrefix)
// Handles the `;` for an expression statement, which is different from most
// keyword statements.
//
// Always:
// (state done)
CARBON_PARSER_STATE(ExpressionStatementFinish)
// Handles processing of a function's `fn <name>(`, and enqueues parameter list
// handling.
//
// If invalid:
// (state done)
// If parenthesized parameters:
// 1. PatternAsFunctionParameter
// 2. FunctionParameterListFinish
// 3. FunctionAfterParameterList
// Else:
// 1. FunctionParameterListFinish
// 2. FunctionAfterParameterList
CARBON_PARSER_STATE(FunctionIntroducer)
// Handles processing of a function's parameter list `)`.
//
// Always:
// (state done)
CARBON_PARSER_STATE(FunctionParameterListFinish)
// Handles processing of a function's syntax after `)`, primarily the
// possibility a `->` return type is there. Always enqueues signature finish
// handling.
//
// If `MinusGreater`:
// 1. Expression
// 2. FunctionReturnTypeFinish
// 3. FunctionSignatureFinish
// Else:
// 1. FunctionSignatureFinish
CARBON_PARSER_STATE(FunctionAfterParameterList)
// Finishes a function return type.
//
// Always:
// (state done)
CARBON_PARSER_STATE(FunctionReturnTypeFinish)
// Finishes a function signature. If it's a declaration, the function is done;
// otherwise, this also starts definition processing.
//
// If `Semi`:
// (state done)
// If `OpenCurlyBrace`:
// 1. StatementScopeLoop
// 2. FunctionDefinitionFinish
// Else:
// (state done)
CARBON_PARSER_STATE(FunctionSignatureFinish)
// Finishes a function definition.
//
// Always:
// (state done)
CARBON_PARSER_STATE(FunctionDefinitionFinish)
// Handles `package`.
//
// Always:
// (state done)
CARBON_PARSER_STATE(Package)
// Handles the processing of a `(condition)` up through the expression.
//
// Always:
// 1. Expression
// 2. ParenConditionFinish
CARBON_PARSER_STATE(ParenCondition)
// Finishes the processing of a `(condition)` after the expression.
//
// Always:
// (state done)
CARBON_PARSER_STATE(ParenConditionFinish)
// Handles the `(` of a parenthesized expression.
//
// If `CloseParen`:
// 1. ParenExpressionFinishAsTuple
// Else:
// 1. Expression
// 2. ParenExpressionParameterFinishAsUnknown
// 3. ParenExpressionFinish
CARBON_PARSER_STATE(ParenExpression)
// Handles the end of a parenthesized expression's parameter. This will start as
// AsUnknown on the first parameter; if there are more, it switches to AsTuple
// processing.
//
// If `Comma` without `CloseParen`:
// 1. Expression
// 2. ParenExpressionParameterFinishAsTuple
// SPECIAL: Parent becomes ParenExpressionFinishAsTuple
// If `Comma` with `CloseParen`:
// (state done)
// SPECIAL: Parent becomes ParenExpressionFinishAsTuple
// Else `CloseParen`:
// (state done)
CARBON_PARSER_STATE(ParenExpressionParameterFinishAsUnknown)
CARBON_PARSER_STATE(ParenExpressionParameterFinishAsTuple)
// Handles the `)` of a parenthesized expression.
//
// Always:
// (state done)
CARBON_PARSER_STATE(ParenExpressionFinish)
CARBON_PARSER_STATE(ParenExpressionFinishAsTuple)
// Handles pattern parsing for a function parameter, enqueuing type expression
// processing. Proceeds to the matching Finish state when done.
//
// If valid:
// 1. Expression
// 2. PatternAsFunctionParameterFinish
// Else:
// 1. PatternAsFunctionParameterFinish
CARBON_PARSER_STATE(PatternAsFunctionParameter)
// Finishes function parameter processing, including `,`. If there are more
// parameters, enqueues another parameter processing state.
//
// If `Comma` without `CloseParen`:
// 1. PatternAsFunctionParameter
// Else:
// (state done)
CARBON_PARSER_STATE(PatternAsFunctionParameterFinish)
// Handles pattern parsing for a `var` statement, enqueuing type expression
// processing. Proceeds to the matching Finish state when done.
//
//
// If valid:
// 1. Expression
// 2. PatternAsVariableFinish
// Else:
// 1. PatternAsVariableFinish
CARBON_PARSER_STATE(PatternAsVariable)
// Finishes `var` pattern processing.
//
// Always:
// (state done)
CARBON_PARSER_STATE(PatternAsVariableFinish)
// Handles a single statement. While typically within a statement block, this
// can also be used for error recovery where we expect a statement block and
// are missing braces.
//
// If `Break`:
// 1. StatementBreakFinish
// (state done)
// If `Continue`:
// 1. StatementContinueFinish
// (state done)
// If `For`:
// 1. StatementForHeader
// 2. StatementForFinish
// If `If`:
// 1. StatementIf
// If `Return`:
// 1. StatementReturn
// If `Var`:
// 1. VarAsRequireSemicolon
// If `While`:
// 1. StatementWhile
// Else:
// 1. Expression
// 2. ExpressionStatementFinish
CARBON_PARSER_STATE(Statement)
// Handles `break` processing at the `;`.
//
// Always:
// (state done)
CARBON_PARSER_STATE(StatementBreakFinish)
// Handles `continue` processing at the `;`.
//
// Always:
// (state done)
CARBON_PARSER_STATE(StatementContinueFinish)
// Handles `for` processing of `(var`, proceeding to a pattern before
// continuing.
//
// If no `OpenParen`:
// 1. CodeBlock
// If `Var`:
// 1. VarAsNoSemicolon
// 2. StatementForHeaderIn
// Else:
// 1. StatementForHeaderIn
CARBON_PARSER_STATE(StatementForHeader)
// Handles `for` procesisng of `in`, proceeding to an expression before
// continuing.
//
// If `In` or `Colon`:
// 1. Expression
// 2. StatementForHeaderFinish
// Else:
// 1. StatementForHeaderFinish
CARBON_PARSER_STATE(StatementForHeaderIn)
// Handles `for` processing of `)`, proceeding to the statement block.
//
// Always:
// 1. CodeBlock
CARBON_PARSER_STATE(StatementForHeaderFinish)
// Handles `for` processing at the final `}`.
//
// Always:
// (state done)
CARBON_PARSER_STATE(StatementForFinish)
// Handles `if` processing at the start.
//
// Always:
// 1. ParenCondition
// 2. StatementIfConditionFinish
CARBON_PARSER_STATE(StatementIf)
// Handles `if` processing between the condition and start of the first code
// block.
//
// Always:
// 1. CodeBlock
// 2. StatementIfThenBlockFinish
CARBON_PARSER_STATE(StatementIfConditionFinish)
// Handles `if` processing after the end of the first code block, with the
// optional `else`.
//
// If `Else` then `If`:
// 1. CodeBlock
// 2. StatementIfElseBlockFinish
// If `Else`:
// 1. StatementIf
// 2. StatementIfElseBlockFinish
// Else:
// (state done)
CARBON_PARSER_STATE(StatementIfThenBlockFinish)
// Handles `if` processing after a provided `else` code block.
//
// Always:
// (state done)
CARBON_PARSER_STATE(StatementIfElseBlockFinish)
// Handles `return` processing at the `;`.
// Handles `return` processing.
//
// If `Semi`:
// 1. StatementReturnFinish
// Else:
// 1. Expression
// 2. StatementReturnFinish
CARBON_PARSER_STATE(StatementReturn)
// Handles `return` processing at the `;` when there's an expression.
//
// Always:
// (state done)
CARBON_PARSER_STATE(StatementReturnFinish)
// Handles processing of statements within a scope.
//
// If `CloseCurlyBrace`:
// (state done)
// Else:
// 1. Statement
// 2. StatementScopeLoop
CARBON_PARSER_STATE(StatementScopeLoop)
// Handles `while` processing.
//
// Always:
// 1. ParenCondition
// 2. StatementWhileConditionFinish
CARBON_PARSER_STATE(StatementWhile)
// Handles `while` processing between the condition and start of the code block.
//
// Always:
// 1. CodeBlock
// 2. StatementWhileBlockFinish
CARBON_PARSER_STATE(StatementWhileConditionFinish)
// Handles `while` processing after the end of the code block.
//
// Always:
// (state done)
CARBON_PARSER_STATE(StatementWhileBlockFinish)
// Handles pattern parsing for a function parameter, enqueuing type expression
// processing. Proceeds to the matching Finish state when done.
CARBON_PARSER_STATE(PatternForFunctionParameter)
// Handles the start of a `var`.
//
// Always:
// 1. PatternAsVariable
// 2. VarAfterPattern
// 3. VarFinishAs(RequireSemicolon|NoSemicolon)
CARBON_PARSER_STATE(VarAsRequireSemicolon)
CARBON_PARSER_STATE(VarAsNoSemicolon)
// Finishes function parameter processing, including `,`. If there are more
// parameters, enqueues another parameter processing state.
CARBON_PARSER_STATE(PatternForFunctionParameterFinish)
// Handles `var` after the pattern, either followed by an initializer or the
// semicolon.
//
// If `Equal`:
// 1. Expression
// 2. VarAfterInitializer
// Else:
// (state done)
CARBON_PARSER_STATE(VarAfterPattern)
// Handles processing of statements within a scope.
CARBON_PARSER_STATE(StatementScopeLoop)
// Handles `var` after the initializer, wrapping up its subtree.
//
// Always:
// (state done)
CARBON_PARSER_STATE(VarAfterInitializer)
// Handles `var` parsing at the end.
//
// Always:
// (state done)
CARBON_PARSER_STATE(VarFinishAsRequireSemicolon)
CARBON_PARSER_STATE(VarFinishAsNoSemicolon)
#undef CARBON_PARSER_STATE