diff --git a/generator/registry-new.zig b/generator/registry-new.zig
index 32363ac..c443247 100644
--- a/generator/registry-new.zig
+++ b/generator/registry-new.zig
@@ -39,7 +39,13 @@ pub const TypeInfo = union(enum) {
};
pub const Container = struct {
- fields: []Declaration,
+ pub const Field = struct {
+ name: []const u8,
+ field_type: TypeInfo,
+ bits: ?usize,
+ };
+
+ fields: []Field,
is_union: bool,
};
diff --git a/generator/spec-c-parse.zig b/generator/spec-c-parse.zig
index 7b1b98c..be55aad 100644
--- a/generator/spec-c-parse.zig
+++ b/generator/spec-c-parse.zig
@@ -19,6 +19,7 @@ const Token = struct {
star,
comma,
semicolon,
+ colon,
lparen,
rparen,
lbracket,
@@ -26,6 +27,7 @@ const Token = struct {
kw_typedef,
kw_const,
kw_vkapi_ptr,
+ kw_struct,
};
};
@@ -70,6 +72,8 @@ const CTokenizer = struct {
Token.Id.kw_const
else if (mem.eql(u8, token_text, "VKAPI_PTR"))
Token.Id.kw_vkapi_ptr
+ else if (mem.eql(u8, token_text, "struct"))
+ Token.Id.kw_struct
else
Token.Id.id;
@@ -111,6 +115,7 @@ const CTokenizer = struct {
'*' => id = .star,
',' => id = .comma,
';' => id = .semicolon,
+ ':' => id = .colon,
'[' => id = .lbracket,
']' => id = .rbracket,
'(' => id = .lparen,
@@ -150,6 +155,8 @@ pub const XmlCTokenizer = struct {
return Token{.id = .enum_name, .text = text};
} else if (mem.eql(u8, elem.tag, "name")) {
return Token{.id = .name, .text = text};
+ } else if (mem.eql(u8, elem.tag, "comment")) {
+ return null;
} else {
return error.InvalidTag;
}
@@ -173,16 +180,13 @@ pub const XmlCTokenizer = struct {
if (self.it.next()) |child| {
switch (child.*) {
- .CharData => |cdata| {
- self.ctok = CTokenizer{.source = cdata};
- continue;
- },
- .Comment => continue,
- .Element => |elem| return try elemToToken(elem)
+ .CharData => |cdata| self.ctok = CTokenizer{.source = cdata},
+ .Comment => {},
+ .Element => |elem| if (try elemToToken(elem)) |tok| return tok,
}
+ } else {
+ return null;
}
-
- return null;
}
}
@@ -222,14 +226,45 @@ pub fn parseTypedef(allocator: *Allocator, xctok: *XmlCTokenizer) !registry.Decl
_ = try xctok.expect(.kw_typedef);
const decl = try parseDeclaration(allocator, xctok);
_ = try xctok.expect(.semicolon);
+ if (try xctok.peek()) |_| {
+ return error.InvalidSyntax;
+ }
+
return decl;
}
+pub fn parseMember(allocator: *Allocator, xctok: *XmlCTokenizer) !registry.Container.Field {
+ const decl = try parseDeclaration(allocator, xctok);
+ var field = registry.Container.Field {
+ .name = decl.name,
+ .field_type = decl.decl_type,
+ .bits = null,
+ };
+
+ if (try xctok.peek()) |tok| {
+ if (tok.id != .colon) {
+ return error.InvalidSyntax;
+ }
+
+ _ = try xctok.nextNoEof();
+ const bits = try xctok.expect(.int);
+ field.bits = try std.fmt.parseInt(usize, bits.text, 10);
+
+ // Assume for now that there won't be any invalid C types like `char char* x : 4`.
+
+ if (try xctok.peek()) |_| {
+ return error.InvalidSyntax;
+ }
+ }
+
+ return field;
+}
+
// DECLARATION = kw_const? type_name DECLARATOR
-// DECLARATOR = POINTERS? (id | name) ('[' ARRAY_EXPR ']')
+// DECLARATOR = POINTERS? (id | name) ('[' ARRAY_EXPR ']')*
// | POINTERS? '(' kw_vkapi_ptr '*' name' ')' // TODO
// POINTERS = (kw_const? '*')+
-pub fn parseDeclaration(allocator: *Allocator, xctok: *XmlCTokenizer) !registry.Declaration {
+fn parseDeclaration(allocator: *Allocator, xctok: *XmlCTokenizer) !registry.Declaration {
// Parse declaration constness
var tok = try xctok.nextNoEof();
const inner_is_const = tok.id == .kw_const;
@@ -237,6 +272,10 @@ pub fn parseDeclaration(allocator: *Allocator, xctok: *XmlCTokenizer) !registry.
tok = try xctok.nextNoEof();
}
+ if (tok.id == .kw_struct) {
+ tok = try xctok.nextNoEof();
+ }
+
// Parse type name
if (tok.id != .type_name) return error.InvalidSyntax;
const type_name = tok.text;
@@ -255,16 +294,24 @@ pub fn parseDeclaration(allocator: *Allocator, xctok: *XmlCTokenizer) !registry.
}
const name = tok.text;
+ var inner_type = &type_info;
- if (try parseArrayDeclarator(xctok)) |array_size| {
+ while (try parseArrayDeclarator(xctok)) |array_size| {
+ // Move the current inner type to a new node on the heap
const child = try allocator.create(registry.TypeInfo);
- child.* = type_info;
- type_info = .{
+ child.* = inner_type.*;
+
+ // Re-assign the previous inner type for the array type info node
+ inner_type.* = .{
.array = .{
.size = array_size,
.child = child,
}
};
+
+ // update the inner_type pointer so it points to the proper
+ // inner type again
+ inner_type = child;
}
return registry.Declaration {
@@ -307,16 +354,16 @@ fn parsePointers(
type_info = .{
.pointer = .{
.size = .one, // set elsewhere
- .is_const = is_const or (first_const),
+ .is_const = is_const or first_const,
.child = child,
- }
+ },
};
}
}
fn parseArrayDeclarator(xctok: *XmlCTokenizer) !?ArraySize {
- const lbracket = try xctok.peekNoEof();
- if (lbracket.id != .lbracket) {
+ const lbracket = try xctok.peek();
+ if (lbracket == null or lbracket.?.id != .lbracket) {
return null;
}
@@ -398,7 +445,7 @@ test "XmlCTokenizer" {
test "parseTypedef" {
const document = try xml.parse(
testing.allocator,
- "typedef const char* pMessage[4];"
+ "typedef const struct Python* pythons[4];"
);
defer document.deinit();
@@ -407,5 +454,14 @@ test "parseTypedef" {
var xctok = XmlCTokenizer.init(document.root);
const decl = try parseTypedef(&arena.allocator, &xctok);
- std.debug.warn("{}\n", .{decl.decl_type.Array.child.Pointer});
+
+ testing.expectEqualSlices(u8, "pythons", decl.name);
+ testing.expectEqual(registry.TypeInfo.array, decl.decl_type);
+ testing.expectEqual(ArraySize{.int = 4}, decl.decl_type.array.size);
+ const array_child = decl.decl_type.array.child.*;
+ testing.expectEqual(registry.TypeInfo.pointer, array_child);
+ const ptr = array_child.pointer;
+ testing.expectEqual(true, ptr.is_const);
+ testing.expectEqual(registry.TypeInfo.alias, ptr.child.*);
+ testing.expectEqualSlices(u8, "Python", ptr.child.alias);
}
diff --git a/generator/spec-parse.zig b/generator/spec-parse.zig
index 0b324ab..55e6eff 100644
--- a/generator/spec-parse.zig
+++ b/generator/spec-parse.zig
@@ -67,6 +67,10 @@ fn parseTypes(allocator: *Allocator, out: []registry.Declaration, types_elem: *x
break :blk try parseHandleType(ty);
} else if (mem.eql(u8, category, "basetype")) {
break :blk try parseBaseType(allocator, ty);
+ } else if (mem.eql(u8, category, "struct")) {
+ break :blk try parseContainer(allocator, ty, false);
+ } else if (mem.eql(u8, category, "union")) {
+ break :blk try parseContainer(allocator, ty, true);
}
continue;
@@ -129,7 +133,7 @@ fn parseHandleType(ty: *xml.Element) !registry.Declaration {
.parent = ty.getAttribute("parent"),
.is_dispatchable = dispatchable,
}
- }
+ },
};
}
}
@@ -149,6 +153,38 @@ fn parseBaseType(allocator: *Allocator, ty: *xml.Element) !registry.Declaration
}
}
+fn parseContainer(allocator: *Allocator, ty: *xml.Element, is_union: bool) !registry.Declaration {
+ const name = ty.getAttribute("name") orelse return error.InvalidRegistry;
+
+ if (ty.getAttribute("alias")) |alias| {
+ return registry.Declaration{
+ .name = name,
+ .decl_type = .{.alias = alias},
+ };
+ }
+
+ var members = try allocator.alloc(registry.Container.Field, ty.children.count());
+ errdefer allocator.free(members);
+
+ var i: usize = 0;
+ var it = ty.findChildrenByTag("member");
+ while (it.next()) |member| {
+ var xctok = xmlc.XmlCTokenizer.init(member);
+ members[i] = try xmlc.parseMember(allocator, &xctok);
+ i += 1;
+ }
+
+ return registry.Declaration{
+ .name = name,
+ .decl_type = .{
+ .container = .{
+ .fields = allocator.shrink(members, i),
+ .is_union = is_union,
+ }
+ },
+ };
+}
+
fn parseEnums(allocator: *Allocator, out: []registry.Declaration, root: *xml.Element) !usize {
var i: usize = 0;
var it = root.findChildrenByTag("enums");