@@ -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+
241329StructDeclarator Parser::parseStructDeclarator () {
242330 StructDeclarator sd;
243331 // optional declarator
0 commit comments