-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
Copy pathhandle_decl_scope_loop.cpp
322 lines (286 loc) · 11.8 KB
/
handle_decl_scope_loop.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
// 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 <array>
#include "toolchain/lex/token_kind.h"
#include "toolchain/parse/context.h"
#include "toolchain/parse/handle.h"
#include "toolchain/parse/node_kind.h"
namespace Carbon::Parse {
// Finishes an invalid declaration, skipping past its end.
static auto FinishAndSkipInvalidDecl(Context& context, int32_t subtree_start)
-> void {
auto cursor = *context.position();
// Output an invalid parse subtree including everything up to the next `;`
// or end of line.
context.ReplacePlaceholderNode(subtree_start, NodeKind::InvalidParseStart,
cursor, /*has_error=*/true);
context.AddNode(NodeKind::InvalidParseSubtree,
context.SkipPastLikelyEnd(cursor), /*has_error=*/true);
}
// Prints a diagnostic and calls FinishAndSkipInvalidDecl.
static auto HandleUnrecognizedDecl(Context& context, int32_t subtree_start)
-> void {
CARBON_DIAGNOSTIC(UnrecognizedDecl, Error,
"unrecognized declaration introducer");
context.emitter().Emit(*context.position(), UnrecognizedDecl);
FinishAndSkipInvalidDecl(context, subtree_start);
}
// Replaces the introducer placeholder node, and pushes the introducer state for
// processing.
static auto ApplyIntroducer(Context& context, Context::State state,
NodeKind introducer_kind, StateKind next_state_kind)
-> void {
context.ReplacePlaceholderNode(state.subtree_start, introducer_kind,
context.Consume());
// Reuse state here to retain its `subtree_start`.
context.PushState(state, next_state_kind);
}
namespace {
// The kind of context in which a declaration appears.
enum DeclContextKind : int8_t {
NonClassContext = 0,
ClassContext = 1,
MaxDeclContextKind = ClassContext,
};
// The kind of declaration introduced by an introducer keyword.
enum class DeclIntroducerKind : int8_t {
Unrecognized,
PackagingDecl,
NonPackagingDecl,
};
// Information about a keyword that might be an introducer keyword.
struct DeclIntroducerInfo {
DeclIntroducerKind introducer_kind;
NodeKind node_kind;
StateKind state_kind;
};
} // namespace
static constexpr auto DeclIntroducers = [] {
std::array<DeclIntroducerInfo, MaxDeclContextKind + 1> introducers[] = {
#define CARBON_TOKEN(Name) \
{{{.introducer_kind = DeclIntroducerKind::Unrecognized, \
.node_kind = NodeKind::InvalidParse, \
.state_kind = StateKind::Invalid}, \
{.introducer_kind = DeclIntroducerKind::Unrecognized, \
.node_kind = NodeKind::InvalidParse, \
.state_kind = StateKind::Invalid}}},
#include "toolchain/lex/token_kind.def"
};
auto set = [&](Lex::TokenKind token_kind, NodeKind node_kind,
StateKind state) {
for (int i = 0; i <= MaxDeclContextKind; ++i) {
introducers[token_kind.AsInt()][i] = {
.introducer_kind = DeclIntroducerKind::NonPackagingDecl,
.node_kind = node_kind,
.state_kind = state};
}
};
auto set_contextual = [&](Lex::TokenKind token_kind,
DeclContextKind context_kind, NodeKind node_kind,
StateKind state) {
introducers[token_kind.AsInt()][context_kind] = {
.introducer_kind = DeclIntroducerKind::NonPackagingDecl,
.node_kind = node_kind,
.state_kind = state};
};
auto set_packaging = [&](Lex::TokenKind token_kind, NodeKind node_kind,
StateKind state) {
for (int i = 0; i <= MaxDeclContextKind; ++i) {
introducers[token_kind.AsInt()][i] = {
.introducer_kind = DeclIntroducerKind::PackagingDecl,
.node_kind = node_kind,
.state_kind = state};
}
};
set(Lex::TokenKind::Adapt, NodeKind::AdaptIntroducer,
StateKind::AdaptAfterIntroducer);
set(Lex::TokenKind::Alias, NodeKind::AliasIntroducer, StateKind::Alias);
set(Lex::TokenKind::Base, NodeKind::BaseIntroducer,
StateKind::BaseAfterIntroducer);
set(Lex::TokenKind::Choice, NodeKind::ChoiceIntroducer,
StateKind::ChoiceIntroducer);
set(Lex::TokenKind::Class, NodeKind::ClassIntroducer,
StateKind::TypeAfterIntroducerAsClass);
set(Lex::TokenKind::Constraint, NodeKind::NamedConstraintIntroducer,
StateKind::TypeAfterIntroducerAsNamedConstraint);
set(Lex::TokenKind::Export, NodeKind::ExportIntroducer,
StateKind::ExportName);
// TODO: Treat `extend` as a declaration introducer.
set(Lex::TokenKind::Fn, NodeKind::FunctionIntroducer,
StateKind::FunctionIntroducer);
set(Lex::TokenKind::Impl, NodeKind::ImplIntroducer,
StateKind::ImplAfterIntroducer);
set(Lex::TokenKind::Interface, NodeKind::InterfaceIntroducer,
StateKind::TypeAfterIntroducerAsInterface);
set(Lex::TokenKind::Namespace, NodeKind::NamespaceStart,
StateKind::Namespace);
set(Lex::TokenKind::Let, NodeKind::LetIntroducer, StateKind::Let);
set_contextual(Lex::TokenKind::Var, NonClassContext,
NodeKind::VariableIntroducer, StateKind::VarAsRegular);
set_contextual(Lex::TokenKind::Var, ClassContext, NodeKind::FieldIntroducer,
StateKind::FieldDecl);
set_packaging(Lex::TokenKind::Package, NodeKind::PackageIntroducer,
StateKind::Package);
set_packaging(Lex::TokenKind::Library, NodeKind::LibraryIntroducer,
StateKind::Library);
set_packaging(Lex::TokenKind::Import, NodeKind::ImportIntroducer,
StateKind::Import);
return std::to_array(introducers);
}();
// Attempts to handle the current token as a declaration introducer.
// Returns true if the current position is a declaration. If we see a
// declaration introducer keyword token, replace the placeholder node and switch
// to a state to parse the rest of the declaration.
static auto TryHandleAsDecl(Context& context, Context::State state,
bool saw_modifier,
DeclContextKind decl_context_kind) -> bool {
const auto& info =
DeclIntroducers[context.PositionKind().AsInt()][decl_context_kind];
switch (info.introducer_kind) {
case DeclIntroducerKind::Unrecognized: {
// A `;` with no modifiers is an empty declaration.
if (!saw_modifier) {
if (auto loc = context.ConsumeIf(Lex::TokenKind::Semi)) {
context.ReplacePlaceholderNode(state.subtree_start,
NodeKind::EmptyDecl, *loc);
return true;
}
}
return false;
}
case DeclIntroducerKind::PackagingDecl: {
// Packaging declarations update the packaging state themselves as needed.
break;
}
case DeclIntroducerKind::NonPackagingDecl: {
// Because a non-packaging keyword was encountered, packaging is complete.
// Misplaced packaging keywords may lead to this being re-triggered.
if (context.packaging_state() !=
Context::PackagingState::AfterNonPackagingDecl) {
if (!context.first_non_packaging_token().has_value()) {
context.set_first_non_packaging_token(*context.position());
}
context.set_packaging_state(
Context::PackagingState::AfterNonPackagingDecl);
}
break;
}
}
ApplyIntroducer(context, state, info.node_kind, info.state_kind);
return true;
}
// Returns true if position_kind could be either an introducer or modifier, and
// should be treated as an introducer.
static auto ResolveAmbiguousTokenAsDeclaration(Context& context,
Lex::TokenKind position_kind)
-> bool {
switch (position_kind) {
case Lex::TokenKind::Base:
case Lex::TokenKind::Export:
case Lex::TokenKind::Extend:
case Lex::TokenKind::Impl:
// This is an ambiguous token, so now we check what the next token is.
// We use the macro for modifiers, including introducers which are
// also modifiers (such as `base`). Other introducer tokens need to be
// added by hand.
switch (context.PositionKind(Lookahead::NextToken)) {
case Lex::TokenKind::Adapt:
case Lex::TokenKind::Alias:
case Lex::TokenKind::Class:
case Lex::TokenKind::Constraint:
case Lex::TokenKind::Extern:
case Lex::TokenKind::Fn:
case Lex::TokenKind::Import:
case Lex::TokenKind::Interface:
case Lex::TokenKind::Let:
case Lex::TokenKind::Library:
case Lex::TokenKind::Namespace:
case Lex::TokenKind::Var:
#define CARBON_PARSE_NODE_KIND(Name)
#define CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Name) case Lex::TokenKind::Name:
#include "toolchain/parse/node_kind.def"
return false;
case Lex::TokenKind::Package:
// `package.foo` is an expression; any other token after `package` is
// a `package` introducer.
return context.PositionKind(static_cast<Lookahead>(2)) ==
Lex::TokenKind::Period;
default:
return true;
}
break;
default:
return false;
}
}
// Returns true if the current position is a modifier, handling it if so.
static auto TryHandleAsModifier(Context& context) -> bool {
auto position_kind = context.PositionKind();
if (ResolveAmbiguousTokenAsDeclaration(context, position_kind)) {
return false;
}
switch (position_kind) {
#define CARBON_PARSE_NODE_KIND(Name)
#define CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Name) \
case Lex::TokenKind::Name: \
context.AddLeafNode(NodeKind::Name##Modifier, context.Consume()); \
return true;
#include "toolchain/parse/node_kind.def"
case Lex::TokenKind::Extern: {
auto extern_token = context.Consume();
if (context.PositionIs(Lex::TokenKind::Library)) {
// `extern library <owning_library>` syntax.
context.ParseLibrarySpecifier(/*accept_default=*/true);
// TODO: Consider error recovery when a non-declaration token is next,
// like a typo of the library name.
context.AddNode(NodeKind::ExternModifierWithLibrary, extern_token,
/*has_error=*/false);
} else {
// `extern` syntax without a library.
context.AddLeafNode(NodeKind::ExternModifier, extern_token);
}
return true;
}
default:
return false;
}
}
static auto HandleDecl(Context& context, DeclContextKind decl_context_kind)
-> void {
auto state = context.PopState();
// Add a placeholder node, to be replaced by the declaration introducer once
// it is found.
context.AddLeafNode(NodeKind::Placeholder, *context.position());
bool saw_modifier = false;
while (TryHandleAsModifier(context)) {
saw_modifier = true;
}
if (!TryHandleAsDecl(context, state, saw_modifier, decl_context_kind)) {
HandleUnrecognizedDecl(context, state.subtree_start);
}
}
auto HandleDeclAsClass(Context& context) -> void {
HandleDecl(context, ClassContext);
}
auto HandleDeclAsNonClass(Context& context) -> void {
HandleDecl(context, NonClassContext);
}
static auto HandleDeclScopeLoop(Context& context, StateKind decl_state_kind)
-> void {
// This maintains the current state unless we're at the end of the scope.
if (context.PositionIs(Lex::TokenKind::CloseCurlyBrace) ||
context.PositionIs(Lex::TokenKind::FileEnd)) {
// This is the end of the scope, so the loop state ends.
context.PopAndDiscardState();
return;
}
context.PushState(decl_state_kind);
}
auto HandleDeclScopeLoopAsClass(Context& context) -> void {
HandleDeclScopeLoop(context, StateKind::DeclAsClass);
}
auto HandleDeclScopeLoopAsNonClass(Context& context) -> void {
HandleDeclScopeLoop(context, StateKind::DeclAsNonClass);
}
} // namespace Carbon::Parse