enum, union

This commit is contained in:
agra
2026-02-14 13:17:22 +02:00
parent 4ff828fd1a
commit 025b790411
14 changed files with 537 additions and 245 deletions

View File

@@ -160,7 +160,7 @@ pub const Parser = struct {
return self.parseStructDecl(name, start_pos);
}
// Union declaration
// C-style union declaration
if (self.current.tag == .kw_union) {
return self.parseUnionDecl(name, start_pos);
}
@@ -368,7 +368,7 @@ pub const Parser = struct {
if (self.current.tag == .kw_struct) {
return try self.parseStructDecl("__anon", start);
}
// Inline union type in type position: union { ... }
// Inline C-style union in type position: union { ... }
if (self.current.tag == .kw_union) {
return try self.parseUnionDecl("__anon", start);
}
@@ -382,30 +382,13 @@ pub const Parser = struct {
fn parseEnumDecl(self: *Parser, name: []const u8, start_pos: u32) anyerror!*Node {
self.advance(); // skip 'enum'
try self.expect(.l_brace);
var variants = std.ArrayList([]const u8).empty;
var variant_names = std.ArrayList([]const u8).empty;
var variant_types = std.ArrayList(?*Node).empty;
var has_any_type = false;
while (self.current.tag != .r_brace and self.current.tag != .eof) {
if (self.current.tag != .identifier) {
return self.fail("expected variant name");
}
try variants.append(self.allocator, self.tokenSlice(self.current));
self.advance();
if (self.current.tag == .semicolon) {
self.advance();
}
}
try self.expect(.r_brace);
return try self.createNode(start_pos, .{ .enum_decl = .{ .name = name, .variants = try variants.toOwnedSlice(self.allocator) } });
}
fn parseUnionDecl(self: *Parser, name: []const u8, start_pos: u32) anyerror!*Node {
self.advance(); // skip 'union'
try self.expect(.l_brace);
var variant_names = std.ArrayList([]const u8).empty;
var variant_types = std.ArrayList(?*Node).empty;
while (self.current.tag != .r_brace and self.current.tag != .eof) {
if (self.current.tag != .identifier) {
return self.fail("expected variant name in union");
}
try variant_names.append(self.allocator, self.tokenSlice(self.current));
self.advance();
if (self.current.tag == .colon) {
@@ -413,6 +396,7 @@ pub const Parser = struct {
self.advance();
const vtype = try self.parseTypeExpr();
try variant_types.append(self.allocator, vtype);
has_any_type = true;
} else {
// Void variant: name;
try variant_types.append(self.allocator, null);
@@ -422,10 +406,54 @@ pub const Parser = struct {
}
}
try self.expect(.r_brace);
return try self.createNode(start_pos, .{ .union_decl = .{
// Always produce enum_decl; variant_types distinguishes payload-less from tagged
return try self.createNode(start_pos, .{ .enum_decl = .{
.name = name,
.variant_names = try variant_names.toOwnedSlice(self.allocator),
.variant_types = try variant_types.toOwnedSlice(self.allocator),
.variant_types = if (has_any_type) try variant_types.toOwnedSlice(self.allocator) else &.{},
} });
}
fn parseUnionDecl(self: *Parser, name: []const u8, start_pos: u32) anyerror!*Node {
self.advance(); // skip 'union'
try self.expect(.l_brace);
var field_names = std.ArrayList([]const u8).empty;
var field_types = std.ArrayList(*Node).empty;
var anon_idx: u32 = 0;
while (self.current.tag != .r_brace and self.current.tag != .eof) {
// Anonymous struct field: struct { x, y: f32; };
if (self.current.tag == .kw_struct) {
const anon_field = try std.fmt.allocPrint(self.allocator, "__anon_{d}", .{anon_idx});
anon_idx += 1;
const anon_struct_name = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ name, anon_field });
const struct_node = try self.parseStructDecl(anon_struct_name, self.current.loc.start);
try field_names.append(self.allocator, anon_field);
try field_types.append(self.allocator, struct_node);
if (self.current.tag == .semicolon) {
self.advance();
}
continue;
}
if (self.current.tag != .identifier) {
return self.fail("expected field name or 'struct'");
}
try field_names.append(self.allocator, self.tokenSlice(self.current));
self.advance();
if (self.current.tag != .colon) {
return self.fail("union fields must have a type");
}
self.advance();
const ftype = try self.parseTypeExpr();
try field_types.append(self.allocator, ftype);
if (self.current.tag == .semicolon) {
self.advance();
}
}
try self.expect(.r_brace);
return try self.createNode(start_pos, .{ .union_decl = .{
.name = name,
.field_names = try field_names.toOwnedSlice(self.allocator),
.field_types = try field_types.toOwnedSlice(self.allocator),
} });
}
@@ -1141,14 +1169,13 @@ pub const Parser = struct {
}
const name = self.tokenSlice(self.current);
self.advance();
// Union literal: .variant(payload)
// Enum literal with payload: .variant(payload) — tagged enum (formerly union literal)
if (self.current.tag == .l_paren) {
self.advance(); // skip '('
const payload = try self.parseExpr();
try self.expect(.r_paren);
return try self.createNode(start, .{ .union_literal = .{
.union_name = null,
.variant_name = name,
return try self.createNode(start, .{ .enum_literal = .{
.name = name,
.payload = payload,
} });
}
@@ -1175,8 +1202,12 @@ pub const Parser = struct {
// Anonymous struct expression: struct { value: T; count: u32; }
return try self.parseStructDecl("__anon", start);
},
.kw_enum => {
// Anonymous enum expression: enum { variant: T; other: u32; }
return try self.parseEnumDecl("__anon", start);
},
.kw_union => {
// Anonymous union expression: union { variant: T; other: u32; }
// Anonymous C-style union expression: union { f: f32; i: s32; }
return try self.parseUnionDecl("__anon", start);
},
.kw_if => {