mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
[parse] Implement initial parsing support for Lambda expressions (#6583)
This adds the necessary parser infrastructure to recognize and parse lambda expressions in Carbon. Key changes: - Added and Parse Node Kinds. - Updated to use to accommodate the growing number of node kinds. - Implemented parser states and handlers for lambda syntax ( or ). - Added structure to . - Added diagnostics for missing lambda bodies. - Added a stub in phase to defer semantic analysis using . - Added parser tests for lambdas.
This commit is contained in:
@@ -688,4 +688,9 @@ auto HandleParseNode(Context& context,
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::FunctionTerseDefinitionId node_id)
|
||||
-> bool {
|
||||
return context.TODO(node_id, "HandleFunctionTerseDefinition");
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/handle.h"
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::LambdaIntroducerId node_id)
|
||||
-> bool {
|
||||
return context.TODO(node_id, "HandleLambdaIntroducer");
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::LambdaId node_id) -> bool {
|
||||
return context.TODO(node_id, "HandleLambda");
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::TerseBodyArrowId node_id)
|
||||
-> bool {
|
||||
return context.TODO(node_id, "HandleTerseBodyArrow");
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
@@ -462,6 +462,7 @@ class NodeStack {
|
||||
case Parse::NodeKind::ImplicitParamListStart:
|
||||
case Parse::NodeKind::ImplIntroducer:
|
||||
case Parse::NodeKind::InterfaceIntroducer:
|
||||
case Parse::NodeKind::LambdaIntroducer:
|
||||
case Parse::NodeKind::LetInitializer:
|
||||
case Parse::NodeKind::LetIntroducer:
|
||||
case Parse::NodeKind::NamedConstraintIntroducer:
|
||||
@@ -534,6 +535,7 @@ class NodeStack {
|
||||
case Parse::NodeKind::StructLiteralComma:
|
||||
case Parse::NodeKind::StructFieldDesignator:
|
||||
case Parse::NodeKind::StructTypeLiteralComma:
|
||||
case Parse::NodeKind::TerseBodyArrow:
|
||||
case Parse::NodeKind::TupleLiteralComma:
|
||||
case Parse::NodeKind::WhileCondition:
|
||||
return Id::Kind::Invalid;
|
||||
|
||||
@@ -53,14 +53,14 @@ fn A[] -> ();
|
||||
|
||||
// TODO: We don't have parsing support for this yet.
|
||||
library "[[@TEST_NAME]]";
|
||||
// CHECK:STDERR: fail_todo_arrow_body.carbon:[[@LINE+8]]:6: error: `fn` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
|
||||
// CHECK:STDERR: fail_todo_arrow_body.carbon:[[@LINE+8]]:1: error: semantics TODO: `function with positional parameters` [SemanticsTodo]
|
||||
// CHECK:STDERR: fn A => 0;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_arrow_body.carbon:[[@LINE+4]]:6: error: semantics TODO: `HandleTerseBodyArrow` [SemanticsTodo]
|
||||
// CHECK:STDERR: fn A => 0;
|
||||
// CHECK:STDERR: ^~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_arrow_body.carbon:[[@LINE+4]]:1: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR: fn A => 0;
|
||||
// CHECK:STDERR: ^~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn A => 0;
|
||||
|
||||
// --- fail_invalid_file_generic_regression_test.carbon
|
||||
@@ -176,6 +176,11 @@ fn A {
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_todo_arrow_body.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @A;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_invalid_file_generic_regression_test.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
|
||||
@@ -95,6 +95,8 @@ CARBON_DIAGNOSTIC_KIND(BinaryOperatorRequiresWhitespace)
|
||||
CARBON_DIAGNOSTIC_KIND(ExpectedArrayComma)
|
||||
CARBON_DIAGNOSTIC_KIND(ExpectedCloseSymbol)
|
||||
CARBON_DIAGNOSTIC_KIND(ExpectedCodeBlock)
|
||||
CARBON_DIAGNOSTIC_KIND(ExpectedLambdaBody)
|
||||
CARBON_DIAGNOSTIC_KIND(ExpectedLambdaBodyAfterReturnType)
|
||||
CARBON_DIAGNOSTIC_KIND(ExpectedExpr)
|
||||
CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierAfterPeriodOrArrow)
|
||||
CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierOrSelfAfterPeriod)
|
||||
|
||||
@@ -155,7 +155,8 @@ CARBON_DECL_INTRODUCER_TOKEN(Choice, "choice")
|
||||
CARBON_DECL_INTRODUCER_TOKEN(Class, "class")
|
||||
CARBON_DECL_INTRODUCER_TOKEN(Constraint, "constraint")
|
||||
CARBON_DECL_INTRODUCER_TOKEN(Export, "export")
|
||||
CARBON_DECL_INTRODUCER_TOKEN(Fn, "fn")
|
||||
CARBON_TOKEN_WITH_VIRTUAL_NODE(
|
||||
CARBON_DECL_INTRODUCER_TOKEN(Fn, "fn"))
|
||||
CARBON_DECL_INTRODUCER_TOKEN(Impl, "impl")
|
||||
CARBON_DECL_INTRODUCER_TOKEN(Import, "import")
|
||||
CARBON_DECL_INTRODUCER_TOKEN(Interface, "interface")
|
||||
|
||||
@@ -470,6 +470,19 @@ auto Context::AddFunctionDefinition(Lex::TokenIndex token, bool has_error)
|
||||
}
|
||||
}
|
||||
|
||||
auto Context::AddFunctionTerseDefinition(Lex::TokenIndex token, bool has_error)
|
||||
-> void {
|
||||
auto definition_id =
|
||||
AddNode<NodeKind::FunctionTerseDefinition>(token, has_error);
|
||||
if (ParsingInDeferredDefinitionScope(*this)) {
|
||||
auto definition_index = deferred_definition_stack_.pop_back_val();
|
||||
auto& definition = tree_->deferred_definitions_.Get(definition_index);
|
||||
definition.definition_id = definition_id;
|
||||
definition.next_definition_index =
|
||||
DeferredDefinitionIndex(tree_->deferred_definitions().size());
|
||||
}
|
||||
}
|
||||
|
||||
auto Context::PrintForStackDump(llvm::raw_ostream& output) const -> void {
|
||||
output << "Parser stack:\n";
|
||||
for (auto [i, entry] : llvm::enumerate(state_stack_)) {
|
||||
|
||||
@@ -406,6 +406,10 @@ class Context {
|
||||
// Adds a function definition node, and ends tracking a deferred definition if
|
||||
// necessary.
|
||||
auto AddFunctionDefinition(Lex::TokenIndex token, bool has_error) -> void;
|
||||
// Adds a function terse definition node, and ends tracking a deferred
|
||||
// definition if necessary.
|
||||
auto AddFunctionTerseDefinition(Lex::TokenIndex token, bool has_error)
|
||||
-> void;
|
||||
|
||||
// Prints information for a stack dump.
|
||||
auto PrintForStackDump(llvm::raw_ostream& output) const -> void;
|
||||
|
||||
@@ -79,6 +79,11 @@ auto HandleExprInPostfix(Context& context) -> void {
|
||||
context.PushState(state);
|
||||
break;
|
||||
}
|
||||
case Lex::TokenKind::Fn: {
|
||||
context.PushState(state);
|
||||
context.PushState(StateKind::LambdaIntroducer);
|
||||
break;
|
||||
}
|
||||
case Lex::TokenKind::False: {
|
||||
context.AddLeafNode(NodeKind::BoolLiteralFalse, context.Consume());
|
||||
context.PushState(state);
|
||||
|
||||
@@ -51,6 +51,13 @@ auto HandleFunctionSignatureFinish(Context& context) -> void {
|
||||
context.PushState(StateKind::StatementScopeLoop);
|
||||
break;
|
||||
}
|
||||
case Lex::TokenKind::EqualGreater: {
|
||||
context.AddFunctionDefinitionStart(context.Consume(), state.has_error);
|
||||
context.AddLeafNode(NodeKind::TerseBodyArrow, *(context.position() - 1));
|
||||
context.PushState(state, StateKind::FunctionTerseBodyFinish);
|
||||
context.PushStateForExpr(PrecedenceGroup::ForTopLevelExpr());
|
||||
break;
|
||||
}
|
||||
case Lex::TokenKind::Equal: {
|
||||
context.AddNode(NodeKind::BuiltinFunctionDefinitionStart,
|
||||
context.Consume(), state.has_error);
|
||||
@@ -95,4 +102,16 @@ auto HandleFunctionDefinitionFinish(Context& context) -> void {
|
||||
context.AddFunctionDefinition(context.Consume(), state.has_error);
|
||||
}
|
||||
|
||||
auto HandleFunctionTerseBodyFinish(Context& context) -> void {
|
||||
auto state = context.PopState();
|
||||
|
||||
auto semi = context.ConsumeIf(Lex::TokenKind::Semi);
|
||||
if (!semi && !state.has_error) {
|
||||
context.DiagnoseExpectedDeclSemi(Lex::TokenKind::Fn);
|
||||
state.has_error = true;
|
||||
}
|
||||
context.AddFunctionTerseDefinition(semi ? *semi : *(context.position() - 1),
|
||||
state.has_error);
|
||||
}
|
||||
|
||||
} // namespace Carbon::Parse
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/parse/context.h"
|
||||
#include "toolchain/parse/handle.h"
|
||||
|
||||
namespace Carbon::Parse {
|
||||
|
||||
auto HandleLambdaIntroducer(Context& context) -> void {
|
||||
auto state = context.PopState();
|
||||
context.AddLeafNode(NodeKind::LambdaIntroducer, context.Consume());
|
||||
context.PushState(state, StateKind::LambdaAfterIntroducer);
|
||||
}
|
||||
|
||||
auto HandleLambdaAfterIntroducer(Context& context) -> void {
|
||||
auto state = context.PopState();
|
||||
|
||||
if (context.PositionIs(Lex::TokenKind::OpenSquareBracket)) {
|
||||
context.PushState(state, StateKind::LambdaAfterImplicitParams);
|
||||
context.PushState(StateKind::PatternListAsImplicit);
|
||||
} else if (context.PositionIs(Lex::TokenKind::OpenParen)) {
|
||||
context.PushState(state, StateKind::LambdaAfterParams);
|
||||
context.PushState(StateKind::PatternListAsExplicit);
|
||||
} else {
|
||||
// No implicit or explicit params.
|
||||
context.PushState(state, StateKind::LambdaAfterParams);
|
||||
}
|
||||
}
|
||||
|
||||
auto HandleLambdaAfterImplicitParams(Context& context) -> void {
|
||||
auto state = context.PopState();
|
||||
|
||||
if (context.PositionIs(Lex::TokenKind::OpenParen)) {
|
||||
context.PushState(state, StateKind::LambdaAfterParams);
|
||||
context.PushState(StateKind::PatternListAsExplicit);
|
||||
} else {
|
||||
// No explicit params after implicit params.
|
||||
context.PushState(state, StateKind::LambdaAfterParams);
|
||||
}
|
||||
}
|
||||
|
||||
auto HandleLambdaAfterParams(Context& context) -> void {
|
||||
auto state = context.PopState();
|
||||
|
||||
if (context.PositionIs(Lex::TokenKind::MinusGreater)) {
|
||||
// Has return type.
|
||||
context.PushState(state, StateKind::LambdaBody);
|
||||
context.PushState(StateKind::FunctionReturnTypeFinish);
|
||||
context.ConsumeAndDiscard();
|
||||
context.PushStateForExpr(PrecedenceGroup::ForType());
|
||||
} else if (context.PositionIs(Lex::TokenKind::EqualGreater)) {
|
||||
// Terse body `=> expr`
|
||||
context.AddLeafNode(NodeKind::TerseBodyArrow, context.Consume());
|
||||
context.PushState(state, StateKind::LambdaBodyFinish);
|
||||
context.PushStateForExpr(PrecedenceGroup::ForTopLevelExpr());
|
||||
} else if (context.PositionIs(Lex::TokenKind::OpenCurlyBrace)) {
|
||||
// Block body `{ ... }`
|
||||
context.PushState(state, StateKind::LambdaBodyFinish);
|
||||
context.PushState(StateKind::CodeBlock);
|
||||
} else {
|
||||
CARBON_DIAGNOSTIC(ExpectedLambdaBody, Error,
|
||||
"expected `->`, `=>`, or `{{`");
|
||||
context.emitter().Emit(*context.position(), ExpectedLambdaBody);
|
||||
state.has_error = true;
|
||||
context.ReturnErrorOnState();
|
||||
}
|
||||
}
|
||||
|
||||
auto HandleLambdaBody(Context& context) -> void {
|
||||
auto state = context.PopState();
|
||||
|
||||
// We arrive here after parsing return type.
|
||||
// So we look for `=>` or `{`.
|
||||
|
||||
if (context.PositionIs(Lex::TokenKind::EqualGreater)) {
|
||||
// Terse body `=> expr`
|
||||
context.AddLeafNode(NodeKind::TerseBodyArrow, context.Consume());
|
||||
context.PushState(state, StateKind::LambdaBodyFinish);
|
||||
context.PushStateForExpr(PrecedenceGroup::ForTopLevelExpr());
|
||||
} else if (context.PositionIs(Lex::TokenKind::OpenCurlyBrace)) {
|
||||
// Block body `{ ... }`
|
||||
context.PushState(state, StateKind::LambdaBodyFinish);
|
||||
context.PushState(StateKind::CodeBlock);
|
||||
} else {
|
||||
CARBON_DIAGNOSTIC(ExpectedLambdaBodyAfterReturnType, Error,
|
||||
"expected `=>` or `{{` after return type");
|
||||
context.emitter().Emit(*context.position(),
|
||||
ExpectedLambdaBodyAfterReturnType);
|
||||
state.has_error = true;
|
||||
context.ReturnErrorOnState();
|
||||
}
|
||||
}
|
||||
|
||||
auto HandleLambdaBodyFinish(Context& context) -> void {
|
||||
auto state = context.PopState();
|
||||
context.AddNode(NodeKind::Lambda, state.token, state.has_error);
|
||||
}
|
||||
|
||||
} // namespace Carbon::Parse
|
||||
@@ -160,6 +160,9 @@ using AnyClassDeclId =
|
||||
ChoiceDefinitionStartId>;
|
||||
using AnyFunctionDeclId = NodeIdOneOf<FunctionDeclId, FunctionDefinitionStartId,
|
||||
BuiltinFunctionDefinitionStartId>;
|
||||
using AnyFunctionDefinitionId =
|
||||
NodeIdOneOf<FunctionDefinitionId, FunctionTerseDefinitionId,
|
||||
BuiltinFunctionDefinitionId>;
|
||||
using AnyImplDeclId = NodeIdOneOf<ImplDeclId, ImplDefinitionStartId>;
|
||||
using AnyInterfaceDeclId =
|
||||
NodeIdOneOf<InterfaceDeclId, InterfaceDefinitionStartId>;
|
||||
|
||||
@@ -143,10 +143,14 @@ CARBON_PARSE_NODE_KIND(CodeBlockStart)
|
||||
CARBON_PARSE_NODE_KIND(CodeBlock)
|
||||
|
||||
CARBON_PARSE_NODE_KIND(FunctionIntroducer)
|
||||
CARBON_PARSE_NODE_KIND(LambdaIntroducer)
|
||||
CARBON_PARSE_NODE_KIND(ReturnType)
|
||||
CARBON_PARSE_NODE_KIND(FunctionDefinitionStart)
|
||||
CARBON_PARSE_NODE_KIND(FunctionDefinition)
|
||||
CARBON_PARSE_NODE_KIND(FunctionTerseDefinition)
|
||||
CARBON_PARSE_NODE_KIND(FunctionDecl)
|
||||
CARBON_PARSE_NODE_KIND(Lambda)
|
||||
CARBON_PARSE_NODE_KIND(TerseBodyArrow)
|
||||
CARBON_PARSE_NODE_KIND(BuiltinFunctionDefinitionStart)
|
||||
CARBON_PARSE_NODE_KIND(BuiltinName)
|
||||
CARBON_PARSE_NODE_KIND(BuiltinFunctionDefinition)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
namespace Carbon::Parse {
|
||||
|
||||
CARBON_DEFINE_RAW_ENUM_CLASS(NodeKind, uint8_t) {
|
||||
CARBON_DEFINE_RAW_ENUM_CLASS(NodeKind, uint16_t) {
|
||||
#define CARBON_PARSE_NODE_KIND(Name) CARBON_RAW_ENUM_ENUMERATOR(Name)
|
||||
#include "toolchain/parse/node_kind.def"
|
||||
};
|
||||
@@ -91,8 +91,8 @@ static_assert(
|
||||
"a `constexpr` context despite being declared as merely `const`. We use it "
|
||||
"in a static assert here to ensure that.");
|
||||
|
||||
// We expect the parse node kind to fit compactly into 8 bits.
|
||||
static_assert(sizeof(NodeKind) == 1, "Kind objects include padding!");
|
||||
// We expect the parse node kind to fit compactly into 16 bits.
|
||||
static_assert(sizeof(NodeKind) == 2, "Kind objects include padding!");
|
||||
|
||||
// Optional arguments that can be supplied when defining a node kind. At least
|
||||
// one of `bracketed_by` and `child_count` is required.
|
||||
|
||||
@@ -678,6 +678,83 @@ CARBON_PARSE_STATE(ExprStatementFinish)
|
||||
// 2. FunctionAfterParams
|
||||
CARBON_PARSE_STATE(FunctionIntroducer)
|
||||
|
||||
// Handles a lambda's introducer.
|
||||
//
|
||||
// fn ...
|
||||
// ^
|
||||
// 1. LambdaIntroducer
|
||||
// 2. LambdaAfterIntroducer
|
||||
CARBON_PARSE_STATE(LambdaIntroducer)
|
||||
|
||||
// Handles processing of a lambda after the introducer.
|
||||
//
|
||||
// fn [ ... ] ...
|
||||
// ^
|
||||
// 1. PatternListAsImplicit
|
||||
// 2. LambdaAfterImplicitParams
|
||||
//
|
||||
// fn ( ... ) ...
|
||||
// ^
|
||||
// 1. PatternListAsExplicit
|
||||
// 2. LambdaAfterParams
|
||||
//
|
||||
// fn ...
|
||||
// ^
|
||||
// 1. LambdaAfterParams
|
||||
CARBON_PARSE_STATE(LambdaAfterIntroducer)
|
||||
|
||||
// Handles processing of a lambda after implicit parameters.
|
||||
//
|
||||
// fn [ ... ] ( ... ) ...
|
||||
// ^
|
||||
// 1. PatternListAsExplicit
|
||||
// 2. LambdaAfterParams
|
||||
//
|
||||
// fn [ ... ] ...
|
||||
// ^
|
||||
// 1. LambdaAfterParams
|
||||
CARBON_PARSE_STATE(LambdaAfterImplicitParams)
|
||||
|
||||
// Handles processing of a lambda after optional parameters.
|
||||
//
|
||||
// fn ... -> ...
|
||||
// ^
|
||||
// 1. FunctionReturnTypeFinish (Reused)
|
||||
// 2. LambdaBody
|
||||
//
|
||||
// fn ... => ...
|
||||
// ^
|
||||
// 1. Expr
|
||||
// 2. LambdaBodyFinish
|
||||
//
|
||||
// fn ... { ... }
|
||||
// ^
|
||||
// 1. StatementScopeLoop
|
||||
// 2. LambdaBodyFinish
|
||||
CARBON_PARSE_STATE(LambdaAfterParams)
|
||||
|
||||
// Handles processing of a lambda body.
|
||||
//
|
||||
// fn ... -> ... { ... }
|
||||
// ^
|
||||
// 1. StatementScopeLoop
|
||||
// 2. LambdaBodyFinish
|
||||
//
|
||||
// fn ... -> ... => ...
|
||||
// ^
|
||||
// 1. Expr
|
||||
// 2. LambdaBodyFinish
|
||||
CARBON_PARSE_STATE(LambdaBody)
|
||||
|
||||
// Finishes a lambda expression.
|
||||
//
|
||||
// fn ... }
|
||||
// ^
|
||||
// fn ... => expr
|
||||
// ^
|
||||
// (state done)
|
||||
CARBON_PARSE_STATE(LambdaBodyFinish)
|
||||
|
||||
// Handles processing of a function's syntax after `)`, primarily the
|
||||
// possibility a `->` return type is there. Always enqueues signature finish
|
||||
// handling.
|
||||
@@ -726,6 +803,13 @@ CARBON_PARSE_STATE(FunctionSignatureFinish)
|
||||
// (state done)
|
||||
CARBON_PARSE_STATE(FunctionDefinitionFinish)
|
||||
|
||||
// Finishes a function with a terse body.
|
||||
//
|
||||
// fn ... => expr ;
|
||||
// ^
|
||||
// (state done)
|
||||
CARBON_PARSE_STATE(FunctionTerseBodyFinish)
|
||||
|
||||
// Handles `export <name>`.
|
||||
//
|
||||
// export Name;
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/function/terse.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/function/terse.carbon
|
||||
|
||||
fn F(x: i32) => x;
|
||||
|
||||
fn G(x: i32) -> i32 => x;
|
||||
|
||||
// CHECK:STDOUT: - filename: terse.carbon
|
||||
// CHECK:STDOUT: parse_tree: [
|
||||
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
|
||||
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'F'},
|
||||
// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'},
|
||||
// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '=>', subtree_size: 8},
|
||||
// CHECK:STDOUT: {kind: 'TerseBodyArrow', text: '=>'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'FunctionTerseDefinition', text: ';', subtree_size: 11},
|
||||
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'G'},
|
||||
// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'},
|
||||
// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'},
|
||||
// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '=>', subtree_size: 10},
|
||||
// CHECK:STDOUT: {kind: 'TerseBodyArrow', text: '=>'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'FunctionTerseDefinition', text: ';', subtree_size: 13},
|
||||
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
|
||||
// CHECK:STDOUT: ]
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/lambda/lambda.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/lambda/lambda.carbon
|
||||
|
||||
// --- basic.carbon
|
||||
|
||||
var a: auto = fn [T:! type] (x: T) -> T { return x; };
|
||||
var b: auto = fn (x: i32) => x;
|
||||
var c: auto = fn { };
|
||||
|
||||
// --- fail_expected_body.carbon
|
||||
|
||||
fn F() {
|
||||
// CHECK:STDERR: fail_expected_body.carbon:[[@LINE+4]]:20: error: expected `->`, `=>`, or `{` [ExpectedLambdaBody]
|
||||
// CHECK:STDERR: var x: auto = fn ;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
var x: auto = fn ;
|
||||
}
|
||||
|
||||
// --- fail_expected_body_after_return_type.carbon
|
||||
|
||||
fn G() {
|
||||
// CHECK:STDERR: fail_expected_body_after_return_type.carbon:[[@LINE+4]]:27: error: expected `=>` or `{` after return type [ExpectedLambdaBodyAfterReturnType]
|
||||
// CHECK:STDERR: var x: auto = fn -> i32 ;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
var x: auto = fn -> i32 ;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: - filename: basic.carbon
|
||||
// CHECK:STDOUT: parse_tree: [
|
||||
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
|
||||
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
|
||||
// CHECK:STDOUT: {kind: 'AutoTypeLiteral', text: 'auto'},
|
||||
// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
|
||||
// CHECK:STDOUT: {kind: 'LambdaIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'ImplicitParamListStart', text: '['},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'T'},
|
||||
// CHECK:STDOUT: {kind: 'CompileTimeBindingPatternStart', text: ':!', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'},
|
||||
// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'ImplicitParamList', text: ']', subtree_size: 6},
|
||||
// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'},
|
||||
// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'},
|
||||
// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'},
|
||||
// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'Lambda', text: 'fn', subtree_size: 20},
|
||||
// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 27},
|
||||
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
|
||||
// CHECK:STDOUT: {kind: 'AutoTypeLiteral', text: 'auto'},
|
||||
// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
|
||||
// CHECK:STDOUT: {kind: 'LambdaIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'},
|
||||
// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'TerseBodyArrow', text: '=>'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'Lambda', text: 'fn', subtree_size: 9},
|
||||
// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 16},
|
||||
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'c'},
|
||||
// CHECK:STDOUT: {kind: 'AutoTypeLiteral', text: 'auto'},
|
||||
// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
|
||||
// CHECK:STDOUT: {kind: 'LambdaIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'},
|
||||
// CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'Lambda', text: 'fn', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 11},
|
||||
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
|
||||
// CHECK:STDOUT: ]
|
||||
// CHECK:STDOUT: - filename: fail_expected_body.carbon
|
||||
// CHECK:STDOUT: parse_tree: [
|
||||
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
|
||||
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'F'},
|
||||
// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'AutoTypeLiteral', text: 'auto'},
|
||||
// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
|
||||
// CHECK:STDOUT: {kind: 'LambdaIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', has_error: yes, subtree_size: 8},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 14},
|
||||
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
|
||||
// CHECK:STDOUT: ]
|
||||
// CHECK:STDOUT: - filename: fail_expected_body_after_return_type.carbon
|
||||
// CHECK:STDOUT: parse_tree: [
|
||||
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
|
||||
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'G'},
|
||||
// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('},
|
||||
// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
|
||||
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'},
|
||||
// CHECK:STDOUT: {kind: 'AutoTypeLiteral', text: 'auto'},
|
||||
// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
|
||||
// CHECK:STDOUT: {kind: 'LambdaIntroducer', text: 'fn'},
|
||||
// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'},
|
||||
// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2},
|
||||
// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', has_error: yes, subtree_size: 10},
|
||||
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 16},
|
||||
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
|
||||
// CHECK:STDOUT: ]
|
||||
@@ -40,7 +40,7 @@ struct DeferredDefinition {
|
||||
// The node that starts the function definition.
|
||||
FunctionDefinitionStartId start_id;
|
||||
// The function definition node.
|
||||
FunctionDefinitionId definition_id = NodeId::None;
|
||||
AnyFunctionDefinitionId definition_id = NodeId::None;
|
||||
// The index of the next method that is not nested within this one.
|
||||
DeferredDefinitionIndex next_definition_index = DeferredDefinitionIndex::None;
|
||||
};
|
||||
@@ -211,9 +211,8 @@ class Tree : public Printable<Tree> {
|
||||
}
|
||||
|
||||
private:
|
||||
// The kind of this node. Note that this is only a single byte.
|
||||
// The kind of this node.
|
||||
NodeKind kind_;
|
||||
static_assert(sizeof(kind_) == 1, "TokenKind must pack to 8 bits");
|
||||
|
||||
// Whether this node is or contains a parse error.
|
||||
//
|
||||
@@ -234,7 +233,7 @@ class Tree : public Printable<Tree> {
|
||||
unsigned token_index_ : Lex::TokenIndex::Bits;
|
||||
};
|
||||
|
||||
static_assert(sizeof(NodeImpl) == 4,
|
||||
static_assert(sizeof(NodeImpl) == 8,
|
||||
"Unexpected size of node implementation!");
|
||||
|
||||
// Sets the kind of a node. This is intended to allow putting the tree into a
|
||||
|
||||
@@ -469,8 +469,8 @@ struct FunctionSignature {
|
||||
using FunctionDecl = FunctionSignature<NodeKind::FunctionDecl,
|
||||
Lex::SemiTokenIndex, NodeCategory::Decl>;
|
||||
using FunctionDefinitionStart =
|
||||
FunctionSignature<NodeKind::FunctionDefinitionStart,
|
||||
Lex::OpenCurlyBraceTokenIndex, NodeCategory::None>;
|
||||
FunctionSignature<NodeKind::FunctionDefinitionStart, Lex::TokenIndex,
|
||||
NodeCategory::None>;
|
||||
|
||||
// A function definition: `fn F() -> i32 { ... }`.
|
||||
struct FunctionDefinition {
|
||||
@@ -483,6 +483,18 @@ struct FunctionDefinition {
|
||||
Lex::CloseCurlyBraceTokenIndex token;
|
||||
};
|
||||
|
||||
// A terse function definition: `fn F() -> i32 => expr;`.
|
||||
struct FunctionTerseDefinition {
|
||||
static constexpr auto Kind = NodeKind::FunctionTerseDefinition.Define(
|
||||
{.category = NodeCategory::Decl,
|
||||
.bracketed_by = FunctionDefinitionStart::Kind});
|
||||
|
||||
FunctionDefinitionStartId signature;
|
||||
TerseBodyArrowId arrow;
|
||||
AnyExprId body;
|
||||
Lex::SemiTokenIndex token;
|
||||
};
|
||||
|
||||
using BuiltinFunctionDefinitionStart =
|
||||
FunctionSignature<NodeKind::BuiltinFunctionDefinitionStart,
|
||||
Lex::EqualTokenIndex, NodeCategory::None>;
|
||||
@@ -651,6 +663,9 @@ struct VariablePattern {
|
||||
using CodeBlockStart =
|
||||
LeafNode<NodeKind::CodeBlockStart, Lex::OpenCurlyBraceTokenIndex>;
|
||||
|
||||
using TerseBodyArrow =
|
||||
LeafNode<NodeKind::TerseBodyArrow, Lex::EqualGreaterTokenIndex>;
|
||||
|
||||
// A code block: `{ statement; statement; ... }`.
|
||||
struct CodeBlock {
|
||||
static constexpr auto Kind =
|
||||
@@ -661,6 +676,24 @@ struct CodeBlock {
|
||||
Lex::CloseCurlyBraceTokenIndex token;
|
||||
};
|
||||
|
||||
using LambdaIntroducer =
|
||||
LeafNode<NodeKind::LambdaIntroducer, Lex::FnTokenIndex>;
|
||||
|
||||
struct Lambda {
|
||||
static constexpr auto Kind = NodeKind::Lambda.Define(
|
||||
{.category = NodeCategory::Expr, .bracketed_by = LambdaIntroducer::Kind});
|
||||
|
||||
LambdaIntroducerId introducer;
|
||||
std::optional<ImplicitParamListId> implicit_params;
|
||||
std::optional<ExplicitParamListId> explicit_params;
|
||||
std::optional<ReturnTypeId> return_type;
|
||||
std::optional<TerseBodyArrowId> arrow;
|
||||
NodeId body;
|
||||
// Use a generic token index because the token might be `}` or part of an
|
||||
// expression.
|
||||
Lex::TokenIndex token;
|
||||
};
|
||||
|
||||
// An expression statement: `F(x);`.
|
||||
struct ExprStatement {
|
||||
static constexpr auto Kind = NodeKind::ExprStatement.Define(
|
||||
|
||||
Reference in New Issue
Block a user