| ... | @@ -9,6 +9,7 @@ const Ref = Zir.Inst.Ref; | ... | @@ -9,6 +9,7 @@ const Ref = Zir.Inst.Ref; |
| 9 | module: *Module, | 9 | module: *Module, |
| 10 | doc_location: Compilation.EmitLoc, | 10 | doc_location: Compilation.EmitLoc, |
| 11 | arena: std.mem.Allocator, | 11 | arena: std.mem.Allocator, |
| | 12 | files: std.AutoHashMapUnmanaged(*File, DocData.AutodocFile) = .{}, |
| 12 | types: std.ArrayListUnmanaged(DocData.Type) = .{}, | 13 | types: std.ArrayListUnmanaged(DocData.Type) = .{}, |
| 13 | decls: std.ArrayListUnmanaged(DocData.Decl) = .{}, | 14 | decls: std.ArrayListUnmanaged(DocData.Decl) = .{}, |
| 14 | ast_nodes: std.ArrayListUnmanaged(DocData.AstNode) = .{}, | 15 | ast_nodes: std.ArrayListUnmanaged(DocData.AstNode) = .{}, |
| ... | @@ -126,12 +127,20 @@ pub fn generateZirData(self: *Autodoc) !void { | ... | @@ -126,12 +127,20 @@ pub fn generateZirData(self: *Autodoc) !void { |
| 126 | } | 127 | } |
| 127 | } | 128 | } |
| 128 | | 129 | |
| 129 | var root_scope: Scope = .{ .parent = null }; | 130 | var root_scope = Scope{ .parent = null }; |
| 130 | try self.ast_nodes.append(self.arena, .{ .name = "(root)" }); | 131 | try self.ast_nodes.append(self.arena, .{ .name = "(root)" }); |
| | 132 | try self.files.put(self.arena, file, .{ |
| | 133 | .analyzed = false, |
| | 134 | .root_struct = self.types.items.len, |
| | 135 | }); |
| 131 | const main_type_index = try self.walkInstruction(file, &root_scope, Zir.main_struct_inst); | 136 | const main_type_index = try self.walkInstruction(file, &root_scope, Zir.main_struct_inst); |
| | 137 | self.files.getPtr(file).?.analyzed = true; |
| | 138 | |
| | 139 | // TODO: solve every single pending declpath whose analysis |
| | 140 | // was delayed because of circular imports. |
| 132 | | 141 | |
| 133 | var data = DocData{ | 142 | var data = DocData{ |
| 134 | .files = &[1][]const u8{root_file_path}, | 143 | .files = .{ .data = self.files }, |
| 135 | .types = self.types.items, | 144 | .types = self.types.items, |
| 136 | .decls = self.decls.items, | 145 | .decls = self.decls.items, |
| 137 | .astNodes = self.ast_nodes.items, | 146 | .astNodes = self.ast_nodes.items, |
| ... | @@ -221,11 +230,42 @@ const DocData = struct { | ... | @@ -221,11 +230,42 @@ const DocData = struct { |
| 221 | | 230 | |
| 222 | // non-hardcoded stuff | 231 | // non-hardcoded stuff |
| 223 | astNodes: []AstNode, | 232 | astNodes: []AstNode, |
| 224 | files: []const []const u8, | 233 | files: struct { |
| | 234 | // this struct is a temporary hack to support json serialization |
| | 235 | data: std.AutoHashMapUnmanaged(*File, AutodocFile), |
| | 236 | pub fn jsonStringify( |
| | 237 | self: @This(), |
| | 238 | opt: std.json.StringifyOptions, |
| | 239 | w: anytype, |
| | 240 | ) !void { |
| | 241 | var idx: usize = 0; |
| | 242 | var it = self.data.iterator(); |
| | 243 | try w.writeAll("{\n"); |
| | 244 | |
| | 245 | var options = opt; |
| | 246 | if (options.whitespace) |*ws| ws.indent_level += 1; |
| | 247 | while (it.next()) |kv| : (idx += 1) { |
| | 248 | if (options.whitespace) |ws| try ws.outputIndent(w); |
| | 249 | try w.print("\"{s}\": {d}", .{ |
| | 250 | kv.key_ptr.*.sub_file_path, |
| | 251 | kv.value_ptr.root_struct, |
| | 252 | }); |
| | 253 | if (idx != self.data.count() - 1) try w.writeByte(','); |
| | 254 | try w.writeByte('\n'); |
| | 255 | } |
| | 256 | if (opt.whitespace) |ws| try ws.outputIndent(w); |
| | 257 | try w.writeAll("}"); |
| | 258 | } |
| | 259 | }, |
| 225 | types: []Type, | 260 | types: []Type, |
| 226 | decls: []Decl, | 261 | decls: []Decl, |
| 227 | comptimeExprs: []ComptimeExpr, | 262 | comptimeExprs: []ComptimeExpr, |
| 228 | | 263 | |
| | 264 | const AutodocFile = struct { |
| | 265 | analyzed: bool, // omitted in json data |
| | 266 | root_struct: usize, // index into `types` |
| | 267 | }; |
| | 268 | |
| 229 | const DocTypeKinds = blk: { | 269 | const DocTypeKinds = blk: { |
| 230 | var info = @typeInfo(std.builtin.TypeId); | 270 | var info = @typeInfo(std.builtin.TypeId); |
| 231 | info.Enum.fields = info.Enum.fields ++ [1]std.builtin.TypeInfo.EnumField{ | 271 | info.Enum.fields = info.Enum.fields ++ [1]std.builtin.TypeInfo.EnumField{ |
| ... | @@ -289,7 +329,7 @@ const DocData = struct { | ... | @@ -289,7 +329,7 @@ const DocData = struct { |
| 289 | name: []const u8, | 329 | name: []const u8, |
| 290 | src: ?usize = null, // index into astNodes | 330 | src: ?usize = null, // index into astNodes |
| 291 | privDecls: ?[]usize = null, // index into decls | 331 | privDecls: ?[]usize = null, // index into decls |
| 292 | pubDecls: ?[]usize = null, // index into decls | 332 | pubDecls: []usize, // index into decls |
| 293 | fields: ?[]TypeRef = null, // (use src->fields to find names) | 333 | fields: ?[]TypeRef = null, // (use src->fields to find names) |
| 294 | }, | 334 | }, |
| 295 | ComptimeExpr: struct { name: []const u8 }, | 335 | ComptimeExpr: struct { name: []const u8 }, |
| ... | @@ -544,9 +584,27 @@ fn walkInstruction( | ... | @@ -544,9 +584,27 @@ fn walkInstruction( |
| 544 | // importFile cannot error out since all files | 584 | // importFile cannot error out since all files |
| 545 | // are already loaded at this point | 585 | // are already loaded at this point |
| 546 | const new_file = self.module.importFile(file, path) catch unreachable; | 586 | const new_file = self.module.importFile(file, path) catch unreachable; |
| 547 | // TODO: cycles not handled, add file info to outuput | 587 | |
| | 588 | const result = try self.files.getOrPut(self.arena, new_file.file); |
| | 589 | if (result.found_existing) { |
| | 590 | return DocData.WalkResult{ .type = result.value_ptr.root_struct }; |
| | 591 | } |
| | 592 | |
| | 593 | result.value_ptr.* = .{ |
| | 594 | .analyzed = false, |
| | 595 | .root_struct = self.types.items.len, |
| | 596 | }; |
| | 597 | |
| 548 | var new_scope = Scope{ .parent = null }; | 598 | var new_scope = Scope{ .parent = null }; |
| 549 | return self.walkInstruction(new_file.file, &new_scope, Zir.main_struct_inst); | 599 | const new_file_walk_result = self.walkInstruction( |
| | 600 | new_file.file, |
| | 601 | &new_scope, |
| | 602 | Zir.main_struct_inst, |
| | 603 | ); |
| | 604 | // We re-access the hashmap in case it was modified |
| | 605 | // by walkInstruction() |
| | 606 | self.files.getPtr(new_file.file).?.analyzed = true; |
| | 607 | return new_file_walk_result; |
| 550 | }, | 608 | }, |
| 551 | .block => { | 609 | .block => { |
| 552 | const res = DocData.WalkResult{ .comptimeExpr = self.comptimeExprs.items.len }; | 610 | const res = DocData.WalkResult{ .comptimeExpr = self.comptimeExprs.items.len }; |
| ... | @@ -641,18 +699,69 @@ fn walkInstruction( | ... | @@ -641,18 +699,69 @@ fn walkInstruction( |
| 641 | path[0] = decls_slot_index; | 699 | path[0] = decls_slot_index; |
| 642 | return DocData.WalkResult{ .declPath = path }; | 700 | return DocData.WalkResult{ .declPath = path }; |
| 643 | }, | 701 | }, |
| 644 | //.field_val => { | 702 | .field_val => { |
| 645 | // const pl_node = data[inst_index].pl_node; | 703 | const pl_node = data[inst_index].pl_node; |
| 646 | // const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index); | 704 | const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index); |
| 647 | // // In case that we have a path (eg Foo.Bar.Baz.X.Y.Z), | 705 | |
| 648 | // // then we want to deal with them in a while loop instead | 706 | var path: std.ArrayListUnmanaged(usize) = .{}; |
| 649 | // // of using `walkRef()`. | 707 | var lhs = @enumToInt(extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs |
| 650 | // | 708 | |
| 651 | // var path = try self.arena.alloc(usize, 1); | 709 | try path.append(self.arena, extra.data.field_name_start); |
| 652 | // path[0] = decls_slot_index; | 710 | // Put inside path the starting index of each decl name |
| 653 | // return DocData.WalkResult{ .declPath = path }; | 711 | // that we encounter as we navigate through all the field_vals |
| | 712 | while (tags[lhs] == .field_val) { |
| | 713 | const lhs_extra = file.zir.extraData( |
| | 714 | Zir.Inst.Field, |
| | 715 | data[lhs].pl_node.payload_index, |
| | 716 | ); |
| 654 | | 717 | |
| 655 | //}, | 718 | try path.append(self.arena, lhs_extra.data.field_name_start); |
| | 719 | lhs = @enumToInt(lhs_extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs |
| | 720 | } |
| | 721 | |
| | 722 | if (tags[lhs] != .decl_val) { |
| | 723 | @panic("TODO: handle non-decl_val endings in walkInstruction.field_val"); |
| | 724 | } |
| | 725 | const str_tok = data[lhs].str_tok; |
| | 726 | const decls_slot_index = parent_scope.resolveDeclName(str_tok.start); |
| | 727 | try path.append(self.arena, decls_slot_index); |
| | 728 | |
| | 729 | // Righ now, every element of `path` is the first index of a |
| | 730 | // decl name except for the final element, which instead points to |
| | 731 | // the analyzed data corresponding to the top-most decl of this path. |
| | 732 | // We are now going to reverse loop over `path` to resolve each name |
| | 733 | // to its corresponding index in `decls`. |
| | 734 | |
| | 735 | var i: usize = path.items.len; |
| | 736 | while (i > 1) { |
| | 737 | i -= 1; |
| | 738 | const parent = self.decls.items[path.items[i]]; |
| | 739 | const child_decl_name = file.zir.nullTerminatedString(path.items[i - 1]); |
| | 740 | switch (parent.value) { |
| | 741 | else => { |
| | 742 | std.debug.print( |
| | 743 | "TODO: handle `{s}`in walkInstruction.field_val\n", |
| | 744 | .{@tagName(parent.value)}, |
| | 745 | ); |
| | 746 | unreachable; |
| | 747 | }, |
| | 748 | .type => |t_index| { |
| | 749 | const t_struct = self.types.items[t_index].Struct; // todo: support more types |
| | 750 | for (t_struct.pubDecls) |d| { |
| | 751 | // TODO: this could be improved a lot |
| | 752 | // by having our own string table! |
| | 753 | const decl = self.decls.items[d]; |
| | 754 | if (std.mem.eql(u8, decl.name, child_decl_name)) { |
| | 755 | path.items[i - 1] = d; |
| | 756 | continue; |
| | 757 | } |
| | 758 | } |
| | 759 | }, |
| | 760 | } |
| | 761 | } |
| | 762 | |
| | 763 | return DocData.WalkResult{ .declPath = path.items }; |
| | 764 | }, |
| 656 | .int_type => { | 765 | .int_type => { |
| 657 | const int_type = data[inst_index].int_type; | 766 | const int_type = data[inst_index].int_type; |
| 658 | const sign = if (int_type.signedness == .unsigned) "u" else "i"; | 767 | const sign = if (int_type.signedness == .unsigned) "u" else "i"; |