Skip to content

Commit 500152a

Browse files
committed
[parser] Add enum support to parser: Implement parsing for enum specifiers, including enumerator lists, and update ASTPrinter for enum representation. Enhance unit tests to validate enum parsing functionality.
1 parent 352ee48 commit 500152a

5 files changed

Lines changed: 140 additions & 4 deletions

File tree

src/parser/AST.hpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,12 @@ struct DeclarationSpecifiers {
8787
// Aggregate representation for type-specifiers (simple keyword sequences,
8888
// struct/union specifiers, typedef-names, or other combined forms).
8989
struct TypeSpecifier {
90-
enum class Kind { Simple, StructOrUnion, TypedefName, Other } kind{Kind::Other};
90+
enum class Kind { Simple, StructOrUnion, Enum, TypedefName, Other } kind{Kind::Other};
9191
std::vector<SimpleTypeSpecifier> simple; // used when Kind==Simple
9292
std::shared_ptr<StructOrUnionSpecifier> su; // used when Kind==StructOrUnion
93+
// enum specifier (used when Kind==Enum)
94+
struct EnumSpecifier;
95+
std::shared_ptr<EnumSpecifier> en;
9396
std::string text; // textual fallback (typedef-name or other combined form)
9497
};
9598

@@ -133,6 +136,17 @@ struct StructOrUnionSpecifier {
133136
std::vector<StructMember> members;
134137
};
135138

139+
// Minimal holder for enum specifiers (name + whether body present + enumerators).
140+
struct DeclarationSpecifiers::TypeSpecifier::EnumSpecifier {
141+
std::optional<std::string> name;
142+
bool hasBody{false};
143+
struct Enumerator {
144+
std::string name;
145+
std::optional<ExprPtr> value; // optional constant-expression
146+
};
147+
std::vector<Enumerator> enumerators;
148+
};
149+
136150
// ExternalDecl is either a function definition or a declaration
137151
struct ExternalDecl : Node {
138152
std::variant<FunctionDefPtr, DeclarationPtr> decl;

src/parser/ASTPrinter.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,28 @@ void ASTPrinter::emitSpecifiersEntries(const DeclarationSpecifiers &specs) {
181181
}
182182
} else if (!ts.text.empty()) simpleTag("Spec", ts.text);
183183
break;
184+
case DeclarationSpecifiers::TypeSpecifier::Kind::Enum:
185+
if (ts.en) {
186+
std::string name;
187+
if (ts.en->name.has_value()) name = *ts.en->name;
188+
if (!ts.en->hasBody) {
189+
std::string repr = "enum";
190+
if (!name.empty()) { repr += " "; repr += name; }
191+
simpleTag("Spec", repr);
192+
} else {
193+
openTag("enum", (!name.empty() ? std::string("name=\"") + esc(name) + std::string("\"") : std::string()));
194+
openTag("Enumerators");
195+
for (auto &ev : ts.en->enumerators) {
196+
openTag("Enumerator");
197+
simpleTag("Name", ev.name);
198+
if (ev.value) { openTag("Value"); visitExpr(*ev.value); closeTag("Value"); }
199+
closeTag("Enumerator");
200+
}
201+
closeTag("Enumerators");
202+
closeTag("enum");
203+
}
204+
} else if (!ts.text.empty()) simpleTag("Spec", ts.text);
205+
break;
184206
case DeclarationSpecifiers::TypeSpecifier::Kind::TypedefName:
185207
case DeclarationSpecifiers::TypeSpecifier::Kind::Other:
186208
if (!ts.text.empty()) simpleTag("Spec", ts.text);

src/parser/Parser.cpp

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,15 @@ DeclarationSpecifiers Parser::parseDeclarationSpecifiers() {
7575
// Handle compound type-specifiers. Special-case struct/union/enum
7676
// to consume their optional name and optional braced definition.
7777
if (s == "struct" || s == "union" || s == "enum") {
78-
auto ts = parseStructOrUnionSpecifier();
79-
specs.typeSpecifiers.push_back(ts);
80-
continue;
78+
if (s == "enum") {
79+
auto ts = parseEnumSpecifier();
80+
specs.typeSpecifiers.push_back(ts);
81+
continue;
82+
} else {
83+
auto ts = parseStructOrUnionSpecifier();
84+
specs.typeSpecifiers.push_back(ts);
85+
continue;
86+
}
8187
}
8288

8389
// Collect contiguous type-specifier keywords (e.g., "unsigned long int",
@@ -238,6 +244,88 @@ DeclarationSpecifiers::TypeSpecifier Parser::parseStructOrUnionSpecifier() {
238244
return ts;
239245
}
240246

247+
DeclarationSpecifiers::TypeSpecifier Parser::parseEnumSpecifier() {
248+
DeclarationSpecifiers::TypeSpecifier ts;
249+
ts.kind = DeclarationSpecifiers::TypeSpecifier::Kind::Enum;
250+
auto en = std::make_shared<DeclarationSpecifiers::TypeSpecifier::EnumSpecifier>();
251+
252+
// current token should be 'enum'
253+
auto kw = lex.peek();
254+
if (!kw) return ts;
255+
// consume 'enum'
256+
lex.next();
257+
258+
// optional identifier
259+
std::optional<std::string> tagName;
260+
if (lex.peek() && lex.peek()->kind() == TokenKind::Identifier) {
261+
tagName = lex.peek()->lexeme();
262+
en->name = *tagName;
263+
lex.next();
264+
}
265+
266+
// optional body
267+
bool hasBodyNow = false;
268+
if (lex.peek() && lex.peek()->kind() == TokenKind::Punctuator && lex.peek()->lexeme() == "{") {
269+
// consume '{'
270+
lex.next();
271+
hasBodyNow = true;
272+
en->hasBody = true;
273+
274+
// parse enumerator list
275+
while (auto p = lex.peek()) {
276+
if (p->kind() == TokenKind::Punctuator && p->lexeme() == "}") { lex.next(); break; }
277+
278+
// expect identifier
279+
if (p->kind() == TokenKind::Identifier) {
280+
DeclarationSpecifiers::TypeSpecifier::EnumSpecifier::Enumerator ev;
281+
ev.name = p->lexeme();
282+
lex.next();
283+
// optional '=' constant-expression
284+
if (lex.peek() && lex.peek()->kind() == TokenKind::Punctuator && lex.peek()->lexeme() == "=") {
285+
lex.next();
286+
ev.value = parseExpr();
287+
}
288+
en->enumerators.push_back(std::move(ev));
289+
// optional trailing comma
290+
if (lex.peek() && lex.peek()->kind() == TokenKind::Punctuator && lex.peek()->lexeme() == ",") {
291+
lex.next();
292+
// allow trailing comma before '}'
293+
continue;
294+
}
295+
continue;
296+
}
297+
// if something else, attempt to recover by consuming token
298+
lex.next();
299+
}
300+
}
301+
302+
ts.en = en;
303+
// register or merge into enum tag registry if we have a tag name
304+
if (tagName.has_value()) {
305+
auto it = enum_tag_registry.find(*tagName);
306+
if (it == enum_tag_registry.end()) {
307+
enum_tag_registry[*tagName] = en;
308+
} else {
309+
auto existing = it->second;
310+
if (existing && existing->hasBody && hasBodyNow) {
311+
wvmcc::Diagnostic d;
312+
d.severity = wvmcc::Diagnostic::Severity::Error;
313+
d.message = "redefinition of enum tag '" + *tagName + "'";
314+
d.span = kw->span;
315+
diagnostics.push_back(std::move(d));
316+
} else if (existing && !existing->hasBody && hasBodyNow) {
317+
existing->hasBody = true;
318+
existing->enumerators = en->enumerators;
319+
ts.en = existing;
320+
} else {
321+
ts.en = existing;
322+
}
323+
}
324+
}
325+
326+
return ts;
327+
}
328+
241329
StructDeclarator Parser::parseStructDeclarator() {
242330
StructDeclarator sd;
243331
// optional declarator

src/parser/Parser.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ class Parser {
3030
DeclarationSpecifiers parseDeclarationSpecifiers();
3131
// parse a struct/union/enum specifier including member list when present
3232
DeclarationSpecifiers::TypeSpecifier parseStructOrUnionSpecifier();
33+
// parse an enum specifier including enumerator list when present
34+
DeclarationSpecifiers::TypeSpecifier parseEnumSpecifier();
3335
// parse the list of struct-declarations inside a struct/union body
3436
std::vector<StructMember> parseStructDeclarationList();
3537
// parse a single struct-declarator (optional declarator and optional bit-field width)
@@ -53,6 +55,8 @@ class Parser {
5355
std::unordered_set<std::string> typedef_names{};
5456
// tag registry for struct/union names -> specifier (incomplete or complete)
5557
std::unordered_map<std::string, std::shared_ptr<StructOrUnionSpecifier>> tag_registry{};
58+
// tag registry for enum names -> specifier (incomplete or complete)
59+
std::unordered_map<std::string, std::shared_ptr<DeclarationSpecifiers::TypeSpecifier::EnumSpecifier>> enum_tag_registry{};
5660
};
5761

5862
} // namespace wvmcc::parser

tests/unit/parser/type_specifiers_test.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ int main() {
1919
ofs << "long double b;\n";
2020
ofs << "const volatile int c;\n";
2121
ofs << "inline int f() { return 0; }\n";
22+
ofs << "enum E { A, B = 3, C } e1;\n";
23+
ofs << "enum { X = 1, Y, };\n";
24+
ofs << "enum F;\n";
2225
}
2326

2427
Preprocessor pp;
@@ -39,13 +42,18 @@ int main() {
3942
ASTPrinter p(os);
4043
p.print(tu);
4144
std::string out = os.str();
45+
std::cerr << "ASTPrinter output:\n" << out << std::endl;
4246

4347
// Check for presence of expected specifier text
4448
if (out.find("unsigned") == std::string::npos) { std::remove(fname.c_str()); return 5; }
4549
if (out.find("long") == std::string::npos) { std::remove(fname.c_str()); return 6; }
4650
if (out.find("long") == std::string::npos || out.find("double") == std::string::npos) { std::remove(fname.c_str()); return 7; }
4751
if (out.find("const") == std::string::npos || out.find("volatile") == std::string::npos) { std::remove(fname.c_str()); return 8; }
4852
if (out.find("inline") == std::string::npos) { std::remove(fname.c_str()); return 9; }
53+
if (out.find("name=\"E\"") == std::string::npos && out.find("<enum>") == std::string::npos) { std::remove(fname.c_str()); return 10; }
54+
if (out.find("<Name>A</Name>") == std::string::npos || out.find("<Name>B</Name>") == std::string::npos || out.find("<Name>C</Name>") == std::string::npos) { std::remove(fname.c_str()); return 11; }
55+
if (out.find("<Name>X</Name>") == std::string::npos || out.find("<Name>Y</Name>") == std::string::npos) { std::remove(fname.c_str()); return 12; }
56+
if (out.find("enum F") == std::string::npos && out.find("name=\"F\"") == std::string::npos) { std::remove(fname.c_str()); return 13; }
4957

5058
std::cout << "type-specifiers-test: OK" << std::endl;
5159
std::remove(fname.c_str());

0 commit comments

Comments
 (0)