Commit ef5a2e8d6f

Loris Cro <kappaloris@gmail.com>
2022-02-06 18:25:01
autodocs: added basic support for unions
1 parent a04045c
Changed files (2)
lib
docs
src
lib/docs/main.js
@@ -133,7 +133,7 @@
     function resolveDeclValue(decl) {
         var i = 0;
         while(i < 1000) {
-            i += 1; 
+            i += 1;
 
             if ("declRef" in decl.value) {
                 decl = zigAnalysis.decls[decl.value.declRef];
@@ -149,7 +149,7 @@
     function resolveDeclValueTypeId(decl){
         var i = 0;
         while(i < 1000) {
-            i += 1; 
+            i += 1;
             console.assert(isDecl(decl));
             if ("type" in decl.value) {
                 return typeTypeId;
@@ -250,7 +250,7 @@
             }
 
             var childDeclValue = resolveDeclValue(childDecl);
-            if ("type" in childDeclValue){                
+            if ("type" in childDeclValue){
                 if (i + 1 === curNav.declNames.length) {
                     curNav.declObjs.push(zigAnalysis.types[childDeclValue.type]);
                     break;
@@ -1070,7 +1070,7 @@
             case "float":
                 declValueText += decl.value.float.value;
             break;
-            default: 
+            default:
                 console.log("TODO: renderValue for ", Object.keys(decl.value)[0]);
                 declValueText += "#TODO#";
         }
@@ -1213,12 +1213,11 @@
             domSectFns.classList.remove("hidden");
         }
 
-        if (container.fields) {
-            resizeDomList(domListFields, container.fields.length, '<div></div>');
+        var containerNode = zigAnalysis.astNodes[container.src];
+        if (containerNode.fields) {
+            resizeDomList(domListFields, containerNode.fields.length, '<div></div>');
 
-            var containerNode = zigAnalysis.astNodes[container.src];
-            for (var i = 0; i < container.fields.length; i += 1) {
-                var field = container.fields[i];
+            for (var i = 0; i < containerNode.fields.length; i += 1) {
                 var fieldNode = zigAnalysis.astNodes[containerNode.fields[i]];
                 var divDom = domListFields.children[i];
 
@@ -1227,6 +1226,7 @@
                 if (container.kind === typeKinds.Enum) {
                     html += ' = <span class="tok-number">' + field + '</span>';
                 } else {
+                    var field = container.fields[i];
                     html += ": ";
                     if (typeof(field) === 'object') {
                         if (field.failure === true) {
@@ -1239,7 +1239,7 @@
 
                             var valTypeName = valType.name;
                             if (valType.kind === typeKinds.Struct) {
-                                valTypeName = "struct";                                
+                                valTypeName = "struct";
                             }
 
                             html += '<a href="'+navLinkDecl(decl.name)+'">';
@@ -1423,7 +1423,7 @@
         return null;
     }
 
- 
+
 
 
     function computeCanonicalPackagePaths() {
@@ -1916,7 +1916,7 @@
         if (ev.ctrlKey) name = "Ctrl+" + name;
         return name;
     }
-    
+
     function onWindowKeyDown(ev) {
         switch (getKeyString(ev)) {
             case "Esc":
src/Autodoc.zig
@@ -60,10 +60,10 @@ pub fn generateZirData(self: *Autodoc) !void {
             try self.types.append(self.arena, .{
                 .name = tmpbuf.toOwnedSlice(),
                 .kind = switch (@intToEnum(Ref, i)) {
-                    else => |t| blk: {
-                        std.debug.print("TODO: categorize `{s}` in typeKinds\n", .{
-                            @tagName(t),
-                        });
+                    else => blk: {
+                        //std.debug.print("TODO: categorize `{s}` in typeKinds\n", .{
+                        //    @tagName(t),
+                        //});
                         break :blk 7;
                     },
                     .u1_type,
@@ -302,7 +302,7 @@ const DocData = struct {
                 .int => |v| {
                     const neg = if (v.negated) "-" else "";
                     try w.print(
-                        \\{{ "int": {{ "typeRef": 
+                        \\{{ "int": {{ "typeRef":
                     , .{});
                     try v.typeRef.jsonStringify(options, w);
                     try w.print(
@@ -312,7 +312,7 @@ const DocData = struct {
                 .float => |v| {
                     const neg = if (v.negated) "-" else "";
                     try w.print(
-                        \\{{ "float": {{ "typeRef": 
+                        \\{{ "float": {{ "typeRef":
                     , .{});
                     try v.typeRef.jsonStringify(options, w);
                     try w.print(
@@ -429,13 +429,6 @@ fn walkInstruction(
             const pl_node = data[inst_index].pl_node;
             const extra = zir.extraData(Zir.Inst.Block, pl_node.payload_index);
             const break_index = zir.extra[extra.end..][extra.data.body_len - 1];
-
-            std.debug.print("[instr: {}] body len: {} last instr idx: {}\n", .{
-                inst_index,
-                extra.data.body_len,
-                break_index,
-            });
-
             const break_operand = data[break_index].@"break".operand;
             return self.walkRef(zir, parent_scope, break_operand);
         },
@@ -444,10 +437,227 @@ fn walkInstruction(
             switch (extended.opcode) {
                 else => {
                     std.debug.panic(
-                        "TODO: implement `walkInstruction` (inside .extended case) for {s}\n\n",
+                        "TODO: implement `walkInstruction.extended` for {s}\n\n",
                         .{@tagName(extended.opcode)},
                     );
                 },
+                .union_decl => {
+                    var scope: Scope = .{ .parent = parent_scope };
+
+                    const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small);
+                    var extra_index: usize = extended.operand;
+
+                    const src_node: ?i32 = if (small.has_src_node) blk: {
+                        const src_node = @bitCast(i32, zir.extra[extra_index]);
+                        extra_index += 1;
+                        break :blk src_node;
+                    } else null;
+                    _ = src_node;
+
+                    const tag_type: ?Ref = if (small.has_tag_type) blk: {
+                        const tag_type = zir.extra[extra_index];
+                        extra_index += 1;
+                        break :blk @intToEnum(Ref, tag_type);
+                    } else null;
+                    _ = tag_type;
+
+                    const body_len = if (small.has_body_len) blk: {
+                        const body_len = zir.extra[extra_index];
+                        extra_index += 1;
+                        break :blk body_len;
+                    } else 0;
+
+                    const fields_len = if (small.has_fields_len) blk: {
+                        const fields_len = zir.extra[extra_index];
+                        extra_index += 1;
+                        break :blk fields_len;
+                    } else 0;
+                    _ = fields_len;
+
+                    const decls_len = if (small.has_decls_len) blk: {
+                        const decls_len = zir.extra[extra_index];
+                        extra_index += 1;
+                        break :blk decls_len;
+                    } else 0;
+
+                    var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
+                    var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};
+
+                    const decls_first_index = self.decls.items.len;
+                    // Decl name lookahead for reserving slots in `scope` (and `decls`).
+                    // Done to make sure that all decl refs can be resolved correctly,
+                    // even if we haven't fully analyzed the decl yet.
+                    {
+                        var it = zir.declIterator(@intCast(u32, inst_index));
+                        try self.decls.resize(self.arena, decls_first_index + it.decls_len);
+                        var decls_slot_index = decls_first_index;
+                        while (it.next()) |d| : (decls_slot_index += 1) {
+                            const decl_name_index = zir.extra[d.sub_index + 5];
+                            try scope.insertDeclRef(self.arena, decl_name_index, decls_slot_index);
+                        }
+                    }
+
+                    extra_index = try self.walkDecls(
+                        zir,
+                        &scope,
+                        decls_first_index,
+                        decls_len,
+                        &decl_indexes,
+                        &priv_decl_indexes,
+                        extra_index,
+                    );
+
+                    // const body = zir.extra[extra_index..][0..body_len];
+                    extra_index += body_len;
+
+                    var field_type_indexes: std.ArrayListUnmanaged(DocData.WalkResult) = .{};
+                    var field_name_indexes: std.ArrayListUnmanaged(usize) = .{};
+                    try self.collectUnionFieldInfo(
+                        zir,
+                        &scope,
+                        fields_len,
+                        &field_type_indexes,
+                        &field_name_indexes,
+                        extra_index,
+                    );
+
+                    self.ast_nodes.items[self_ast_node_index].fields = field_name_indexes.items;
+
+                    try self.types.append(self.arena, .{
+                        .kind = @enumToInt(std.builtin.TypeId.Union),
+                        .name = "todo_name",
+                        .src = self_ast_node_index,
+                        .privDecls = priv_decl_indexes.items,
+                        .pubDecls = decl_indexes.items,
+                        .fields = field_type_indexes.items,
+                    });
+
+                    return DocData.WalkResult{ .type = self.types.items.len - 1 };
+                },
+                .enum_decl => {
+                    var scope: Scope = .{ .parent = parent_scope };
+
+                    const small = @bitCast(Zir.Inst.EnumDecl.Small, extended.small);
+                    var extra_index: usize = extended.operand;
+
+                    const src_node: ?i32 = if (small.has_src_node) blk: {
+                        const src_node = @bitCast(i32, zir.extra[extra_index]);
+                        extra_index += 1;
+                        break :blk src_node;
+                    } else null;
+                    _ = src_node;
+
+                    const tag_type: ?Ref = if (small.has_tag_type) blk: {
+                        const tag_type = zir.extra[extra_index];
+                        extra_index += 1;
+                        break :blk @intToEnum(Ref, tag_type);
+                    } else null;
+                    _ = tag_type;
+
+                    const body_len = if (small.has_body_len) blk: {
+                        const body_len = zir.extra[extra_index];
+                        extra_index += 1;
+                        break :blk body_len;
+                    } else 0;
+
+                    const fields_len = if (small.has_fields_len) blk: {
+                        const fields_len = zir.extra[extra_index];
+                        extra_index += 1;
+                        break :blk fields_len;
+                    } else 0;
+                    _ = fields_len;
+
+                    const decls_len = if (small.has_decls_len) blk: {
+                        const decls_len = zir.extra[extra_index];
+                        extra_index += 1;
+                        break :blk decls_len;
+                    } else 0;
+
+                    var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
+                    var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};
+
+                    const decls_first_index = self.decls.items.len;
+                    // Decl name lookahead for reserving slots in `scope` (and `decls`).
+                    // Done to make sure that all decl refs can be resolved correctly,
+                    // even if we haven't fully analyzed the decl yet.
+                    {
+                        var it = zir.declIterator(@intCast(u32, inst_index));
+                        try self.decls.resize(self.arena, decls_first_index + it.decls_len);
+                        var decls_slot_index = decls_first_index;
+                        while (it.next()) |d| : (decls_slot_index += 1) {
+                            const decl_name_index = zir.extra[d.sub_index + 5];
+                            try scope.insertDeclRef(self.arena, decl_name_index, decls_slot_index);
+                        }
+                    }
+
+                    extra_index = try self.walkDecls(
+                        zir,
+                        &scope,
+                        decls_first_index,
+                        decls_len,
+                        &decl_indexes,
+                        &priv_decl_indexes,
+                        extra_index,
+                    );
+
+                    // const body = zir.extra[extra_index..][0..body_len];
+                    extra_index += body_len;
+
+                    var field_name_indexes: std.ArrayListUnmanaged(usize) = .{};
+                    {
+                        var bit_bag_idx = extra_index;
+                        var cur_bit_bag: u32 = undefined;
+                        extra_index += std.math.divCeil(usize, fields_len, 32) catch unreachable;
+
+                        var idx: usize = 0;
+                        while (idx < fields_len) : (idx += 1) {
+                            if (idx % 32 == 0) {
+                                cur_bit_bag = zir.extra[bit_bag_idx];
+                                bit_bag_idx += 1;
+                            }
+
+                            const has_value = @truncate(u1, cur_bit_bag) != 0;
+                            cur_bit_bag >>= 1;
+
+                            const field_name_index = zir.extra[extra_index];
+                            extra_index += 1;
+
+                            const doc_comment_index = zir.extra[extra_index];
+                            extra_index += 1;
+
+                            const value_ref: ?Ref = if (has_value) blk: {
+                                const value_ref = zir.extra[extra_index];
+                                extra_index += 1;
+                                break :blk @intToEnum(Ref, value_ref);
+                            } else null;
+                            _ = value_ref;
+
+                            const field_name = zir.nullTerminatedString(field_name_index);
+
+                            try field_name_indexes.append(self.arena, self.ast_nodes.items.len);
+                            const doc_comment: ?[]const u8 = if (doc_comment_index != 0)
+                                zir.nullTerminatedString(doc_comment_index)
+                            else
+                                null;
+                            try self.ast_nodes.append(self.arena, .{
+                                .name = field_name,
+                                .docs = doc_comment,
+                            });
+                        }
+                    }
+
+                    self.ast_nodes.items[self_ast_node_index].fields = field_name_indexes.items;
+
+                    try self.types.append(self.arena, .{
+                        .kind = @enumToInt(std.builtin.TypeId.Enum),
+                        .name = "todo_name",
+                        .src = self_ast_node_index,
+                        .privDecls = priv_decl_indexes.items,
+                        .pubDecls = decl_indexes.items,
+                    });
+
+                    return DocData.WalkResult{ .type = self.types.items.len - 1 };
+                },
                 .struct_decl => {
                     var scope: Scope = .{ .parent = parent_scope };
 
@@ -512,7 +722,7 @@ fn walkInstruction(
 
                     var field_type_indexes: std.ArrayListUnmanaged(DocData.WalkResult) = .{};
                     var field_name_indexes: std.ArrayListUnmanaged(usize) = .{};
-                    try self.collectFieldInfo(
+                    try self.collectStructFieldInfo(
                         zir,
                         &scope,
                         fields_len,
@@ -539,12 +749,12 @@ fn walkInstruction(
     }
 }
 
-/// Called by `walkInstruction` when encountering a container type, 
+/// Called by `walkInstruction` when encountering a container type,
 /// iterates over all decl definitions in its body.
-/// It also analyzes each decl's body recursively. 
+/// It also analyzes each decl's body recursively.
 ///
-/// Does not append to `self.decls` directly because `walkInstruction` 
-/// is expected to (look-ahead) scan all decls and reserve `body_len` 
+/// Does not append to `self.decls` directly because `walkInstruction`
+/// is expected to (look-ahead) scan all decls and reserve `body_len`
 /// slots in `self.decls`, which are then filled out by `walkDecls`.
 fn walkDecls(
     self: *Autodoc,
@@ -678,7 +888,7 @@ fn walkDecls(
     return extra_index;
 }
 
-fn collectFieldInfo(
+fn collectUnionFieldInfo(
     self: *Autodoc,
     zir: Zir,
     scope: *Scope,
@@ -703,9 +913,78 @@ fn collectFieldInfo(
             cur_bit_bag = zir.extra[bit_bag_index];
             bit_bag_index += 1;
         }
-        // const has_align = @truncate(u1, cur_bit_bag) != 0;
+        const has_type = @truncate(u1, cur_bit_bag) != 0;
+        cur_bit_bag >>= 1;
+        const has_align = @truncate(u1, cur_bit_bag) != 0;
+        cur_bit_bag >>= 1;
+        const has_tag = @truncate(u1, cur_bit_bag) != 0;
+        cur_bit_bag >>= 1;
+        const unused = @truncate(u1, cur_bit_bag) != 0;
         cur_bit_bag >>= 1;
-        // const has_default = @truncate(u1, cur_bit_bag) != 0;
+        _ = unused;
+
+        const field_name = zir.nullTerminatedString(zir.extra[extra_index]);
+        extra_index += 1;
+        const doc_comment_index = zir.extra[extra_index];
+        extra_index += 1;
+        const field_type = if (has_type)
+            @intToEnum(Zir.Inst.Ref, zir.extra[extra_index])
+        else
+            .void_type;
+        extra_index += 1;
+
+        if (has_align) extra_index += 1;
+        if (has_tag) extra_index += 1;
+
+        // type
+        {
+            const walk_result = try self.walkRef(zir, scope, field_type);
+            try field_type_indexes.append(self.arena, walk_result);
+        }
+
+        // ast node
+        {
+            try field_name_indexes.append(self.arena, self.ast_nodes.items.len);
+            const doc_comment: ?[]const u8 = if (doc_comment_index != 0)
+                zir.nullTerminatedString(doc_comment_index)
+            else
+                null;
+            try self.ast_nodes.append(self.arena, .{
+                .name = field_name,
+                .docs = doc_comment,
+            });
+        }
+    }
+}
+
+fn collectStructFieldInfo(
+    self: *Autodoc,
+    zir: Zir,
+    scope: *Scope,
+    fields_len: usize,
+    field_type_indexes: *std.ArrayListUnmanaged(DocData.WalkResult),
+    field_name_indexes: *std.ArrayListUnmanaged(usize),
+    ei: usize,
+) !void {
+    if (fields_len == 0) return;
+    var extra_index = ei;
+
+    const bits_per_field = 4;
+    const fields_per_u32 = 32 / bits_per_field;
+    const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
+    var bit_bag_index: usize = extra_index;
+    extra_index += bit_bags_count;
+
+    var cur_bit_bag: u32 = undefined;
+    var field_i: u32 = 0;
+    while (field_i < fields_len) : (field_i += 1) {
+        if (field_i % fields_per_u32 == 0) {
+            cur_bit_bag = zir.extra[bit_bag_index];
+            bit_bag_index += 1;
+        }
+        const has_align = @truncate(u1, cur_bit_bag) != 0;
+        cur_bit_bag >>= 1;
+        const has_default = @truncate(u1, cur_bit_bag) != 0;
         cur_bit_bag >>= 1;
         // const is_comptime = @truncate(u1, cur_bit_bag) != 0;
         cur_bit_bag >>= 1;
@@ -720,6 +999,9 @@ fn collectFieldInfo(
         const doc_comment_index = zir.extra[extra_index];
         extra_index += 1;
 
+        if (has_align) extra_index += 1;
+        if (has_default) extra_index += 1;
+
         // type
         {
             const walk_result = try self.walkRef(zir, scope, field_type);