| ... | ... | @@ -210,7 +210,7 @@ const DocData = struct { |
| 210 | 210 | name: []const u8, |
| 211 | 211 | kind: []const u8, // TODO: where do we find this info? |
| 212 | 212 | src: usize, // index into astNodes |
| 213 | | type: usize, // index into types |
| 213 | // typeRef: TypeRef, |
| 214 | 214 | value: WalkResult, |
| 215 | 215 | }; |
| 216 | 216 | |
| ... | ... | @@ -231,28 +231,65 @@ const DocData = struct { |
| 231 | 231 | pubDecls: ?[]usize = null, // index into decls |
| 232 | 232 | fields: ?[]WalkResult = null, // (use src->fields to find names) |
| 233 | 233 | }; |
| 234 | const TypeRef = union(enum) { |
| 235 | unspecified, |
| 236 | declRef: usize, // index in `decls` |
| 237 | type: usize, // index in `types` |
| 238 | pub fn jsonStringify( |
| 239 | self: TypeRef, |
| 240 | _: std.json.StringifyOptions, |
| 241 | w: anytype, |
| 242 | ) !void { |
| 243 | switch (self) { |
| 244 | .unspecified => { |
| 245 | try w.print( |
| 246 | \\{{ "unspecified":{{}} }} |
| 247 | , .{}); |
| 248 | }, |
| 249 | .declRef, .type => |v| { |
| 250 | try w.print( |
| 251 | \\{{ "{s}":{} }} |
| 252 | , .{ @tagName(self), v }); |
| 253 | }, |
| 254 | } |
| 255 | } |
| 256 | }; |
| 234 | 257 | |
| 235 | 258 | const WalkResult = union(enum) { |
| 236 | | failure: bool, |
| 259 | void, |
| 260 | @"unreachable", |
| 261 | @"null": TypeRef, |
| 262 | @"undefined": TypeRef, |
| 263 | @"struct": struct { |
| 264 | typeRef: TypeRef, |
| 265 | fieldVals: []struct { |
| 266 | name: []const u8, |
| 267 | val: WalkResult, |
| 268 | }, |
| 269 | }, |
| 270 | bool: bool, |
| 237 | 271 | type: usize, // index in `types` |
| 238 | | decl_ref: usize, // index in `decls` |
| 272 | declRef: usize, // index in `decls` |
| 239 | 273 | int: struct { |
| 240 | | type: usize, // index in `types` |
| 274 | typeRef: TypeRef, |
| 241 | 275 | value: usize, // direct value |
| 242 | 276 | negated: bool = false, |
| 243 | 277 | }, |
| 278 | |
| 244 | 279 | pub fn jsonStringify( |
| 245 | 280 | self: WalkResult, |
| 246 | | _: std.json.StringifyOptions, |
| 281 | options: std.json.StringifyOptions, |
| 247 | 282 | w: anytype, |
| 248 | 283 | ) !void { |
| 249 | 284 | switch (self) { |
| 250 | | .failure => |v| { |
| 285 | .void, |
| 286 | .@"unreachable", |
| 287 | => { |
| 251 | 288 | try w.print( |
| 252 | | \\{{ "failure":{} }} |
| 253 | | , .{v}); |
| 289 | \\{{ "{s}":{{}} }} |
| 290 | , .{@tagName(self)}); |
| 254 | 291 | }, |
| 255 | | .type, .decl_ref => |v| { |
| 292 | .type, .declRef => |v| { |
| 256 | 293 | try w.print( |
| 257 | 294 | \\{{ "{s}":{} }} |
| 258 | 295 | , .{ @tagName(self), v }); |
| ... | ... | @@ -260,10 +297,21 @@ const DocData = struct { |
| 260 | 297 | .int => |v| { |
| 261 | 298 | const neg = if (v.negated) "-" else ""; |
| 262 | 299 | try w.print( |
| 263 | | \\{{ "int": {{ "type": {}, "value": {s}{} }} }} |
| 264 | | , .{ v.type, neg, v.value }); |
| 300 | \\{{ "int": {{ "typeRef": |
| 301 | , .{}); |
| 302 | try v.typeRef.jsonStringify(options, w); |
| 303 | try w.print( |
| 304 | \\, "value": {s}{} }} }} |
| 305 | , .{ neg, v.value }); |
| 265 | 306 | }, |
| 266 | | |
| 307 | .bool => |v| { |
| 308 | try w.print( |
| 309 | \\{{ "bool":{} }} |
| 310 | , .{v}); |
| 311 | }, |
| 312 | .@"undefined" => |v| try std.json.stringify(v, options, w), |
| 313 | .@"null" => |v| try std.json.stringify(v, options, w), |
| 314 | .@"struct" => |v| try std.json.stringify(v, options, w), |
| 267 | 315 | // .decl_ref => |v| { |
| 268 | 316 | // try w.print( |
| 269 | 317 | // \\{{ "{s}":"{s}" }} |
| ... | ... | @@ -288,17 +336,16 @@ fn walkInstruction( |
| 288 | 336 | |
| 289 | 337 | switch (tags[inst_index]) { |
| 290 | 338 | else => { |
| 291 | | std.debug.print( |
| 339 | std.debug.panic( |
| 292 | 340 | "TODO: implement `walkInstruction` for {s}\n\n", |
| 293 | 341 | .{@tagName(tags[inst_index])}, |
| 294 | 342 | ); |
| 295 | | return DocData.WalkResult{ .failure = true }; |
| 296 | 343 | }, |
| 297 | 344 | .int => { |
| 298 | 345 | const int = data[inst_index].int; |
| 299 | 346 | return DocData.WalkResult{ |
| 300 | 347 | .int = .{ |
| 301 | | .type = @enumToInt(Ref.comptime_int_type), |
| 348 | .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) }, |
| 302 | 349 | .value = int, |
| 303 | 350 | }, |
| 304 | 351 | }; |
| ... | ... | @@ -313,16 +360,34 @@ fn walkInstruction( |
| 313 | 360 | const pl_node = data[inst_index].pl_node; |
| 314 | 361 | const extra = zir.extraData(Zir.Inst.As, pl_node.payload_index); |
| 315 | 362 | const dest_type_walk = try self.walkRef(zir, parent_scope, extra.data.dest_type); |
| 316 | | const dest_type = dest_type_walk.type; // asserts that we got a type |
| 363 | const dest_type_ref = walkResultToTypeRef(dest_type_walk); |
| 317 | 364 | |
| 318 | 365 | var operand = try self.walkRef(zir, parent_scope, extra.data.operand); |
| 319 | | operand.int.type = dest_type; // only support ints for now |
| 366 | |
| 367 | switch (operand) { |
| 368 | else => std.debug.panic( |
| 369 | "TODO: handle {s} in `walkInstruction.as_node`\n", |
| 370 | .{@tagName(operand)}, |
| 371 | ), |
| 372 | .declRef => {}, |
| 373 | // we don't do anything because up until now, |
| 374 | // I've only seen this used as such: |
| 375 | // @as(@as(type, Baz), .{}) |
| 376 | // and we don't want to toss away the |
| 377 | // decl_val information (eg by replacing it with |
| 378 | // a WalkResult.type). |
| 379 | |
| 380 | .int => operand.int.typeRef = dest_type_ref, |
| 381 | .@"struct" => operand.@"struct".typeRef = dest_type_ref, |
| 382 | .@"undefined" => operand.@"undefined" = dest_type_ref, |
| 383 | } |
| 384 | |
| 320 | 385 | return operand; |
| 321 | 386 | }, |
| 322 | 387 | .decl_val => { |
| 323 | 388 | const str_tok = data[inst_index].str_tok; |
| 324 | 389 | const decls_slot_index = parent_scope.resolveDeclName(str_tok.start); |
| 325 | | return DocData.WalkResult{ .decl_ref = decls_slot_index }; |
| 390 | return DocData.WalkResult{ .declRef = decls_slot_index }; |
| 326 | 391 | }, |
| 327 | 392 | .int_type => { |
| 328 | 393 | const int_type = data[inst_index].int_type; |
| ... | ... | @@ -348,23 +413,16 @@ fn walkInstruction( |
| 348 | 413 | }); |
| 349 | 414 | |
| 350 | 415 | const break_operand = data[break_index].@"break".operand; |
| 351 | | return if (Zir.refToIndex(break_operand)) |bi| |
| 352 | | self.walkInstruction(zir, parent_scope, bi) |
| 353 | | else if (@enumToInt(break_operand) <= @enumToInt(Ref.anyerror_void_error_union_type)) |
| 354 | | // we append all the types in ref first, so we can just do this if we encounter a ref that is a type |
| 355 | | return DocData.WalkResult{ .type = @enumToInt(break_operand) } |
| 356 | | else |
| 357 | | std.debug.todo("generate WalkResults for refs that are not types"); |
| 416 | return self.walkRef(zir, parent_scope, break_operand); |
| 358 | 417 | }, |
| 359 | 418 | .extended => { |
| 360 | 419 | const extended = data[inst_index].extended; |
| 361 | 420 | switch (extended.opcode) { |
| 362 | 421 | else => { |
| 363 | | std.debug.print( |
| 422 | std.debug.panic( |
| 364 | 423 | "TODO: implement `walkInstruction` (inside .extended case) for {s}\n\n", |
| 365 | 424 | .{@tagName(extended.opcode)}, |
| 366 | 425 | ); |
| 367 | | return DocData.WalkResult{ .failure = true }; |
| 368 | 426 | }, |
| 369 | 427 | .struct_decl => { |
| 370 | 428 | var scope: Scope = .{ .parent = parent_scope }; |
| ... | ... | @@ -457,6 +515,13 @@ fn walkInstruction( |
| 457 | 515 | } |
| 458 | 516 | } |
| 459 | 517 | |
| 518 | /// Called by `walkInstruction` when encountering a container type, |
| 519 | /// iterates over all decl definitions in its body. |
| 520 | /// It also analyzes each decl's body recursively. |
| 521 | /// |
| 522 | /// Does not append to `self.decls` directly because `walkInstruction` |
| 523 | /// is expected to (look-ahead) scan all decls and reserve `body_len` |
| 524 | /// slots in `self.decls`, which are then filled out by `walkDecls`. |
| 460 | 525 | fn walkDecls( |
| 461 | 526 | self: *Autodoc, |
| 462 | 527 | zir: Zir, |
| ... | ... | @@ -562,15 +627,25 @@ fn walkDecls( |
| 562 | 627 | try priv_decl_indexes.append(self.arena, decls_slot_index); |
| 563 | 628 | } |
| 564 | 629 | |
| 565 | | const decl_type = switch (walk_result) { |
| 566 | | .int => |i| i.type, |
| 567 | | else => @enumToInt(Ref.type_type), |
| 568 | | }; |
| 630 | // // decl.typeRef == decl.val...typeRef |
| 631 | // const decl_type_ref: DocData.TypeRef = switch (walk_result) { |
| 632 | // .int => |i| i.typeRef, |
| 633 | // .void => .{ .type = @enumToInt(Ref.void_type) }, |
| 634 | // .@"undefined", .@"null" => |v| v, |
| 635 | // .@"unreachable" => .{ .type = @enumToInt(Ref.noreturn_type) }, |
| 636 | // .@"struct" => |s| s.typeRef, |
| 637 | // .bool => .{ .type = @enumToInt(Ref.bool_type) }, |
| 638 | // .type => .{ .type = @enumToInt(Ref.type_type) }, |
| 639 | // // this last case is special becauese it's not pointing |
| 640 | // // at the type of the value, but rather at the value itself |
| 641 | // // the js better be aware ot this! |
| 642 | // .declRef => |d| .{ .declRef = d }, |
| 643 | // }; |
| 569 | 644 | |
| 570 | 645 | self.decls.items[decls_slot_index] = .{ |
| 571 | 646 | .name = name, |
| 572 | 647 | .src = ast_node_index, |
| 573 | | .type = decl_type, |
| 648 | // .typeRef = decl_type_ref, |
| 574 | 649 | .value = walk_result, |
| 575 | 650 | .kind = "const", // find where this information can be found |
| 576 | 651 | }; |
| ... | ... | @@ -649,11 +724,101 @@ fn walkRef( |
| 649 | 724 | ref: Ref, |
| 650 | 725 | ) !DocData.WalkResult { |
| 651 | 726 | const enum_value = @enumToInt(ref); |
| 652 | | if (enum_value < Zir.Inst.Ref.typed_value_map.len) { |
| 653 | | // TODO: well... Refs are not all types |
| 727 | if (enum_value <= @enumToInt(Ref.anyerror_void_error_union_type)) { |
| 728 | // We can just return a type that indexes into `types` with the |
| 729 | // enum value because in the beginning we pre-filled `types` with |
| 730 | // the types that are listed in `Ref`. |
| 654 | 731 | return DocData.WalkResult{ .type = enum_value }; |
| 732 | } else if (enum_value < Ref.typed_value_map.len) { |
| 733 | switch (ref) { |
| 734 | else => { |
| 735 | std.debug.panic("TODO: handle {s} in `walkRef`\n", .{ |
| 736 | @tagName(ref), |
| 737 | }); |
| 738 | }, |
| 739 | .undef => { |
| 740 | return DocData.WalkResult{ .@"undefined" = .unspecified }; |
| 741 | }, |
| 742 | .zero => { |
| 743 | return DocData.WalkResult{ .int = .{ |
| 744 | .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) }, |
| 745 | .value = 0, |
| 746 | } }; |
| 747 | }, |
| 748 | .one => { |
| 749 | return DocData.WalkResult{ .int = .{ |
| 750 | .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) }, |
| 751 | .value = 1, |
| 752 | } }; |
| 753 | }, |
| 754 | |
| 755 | .void_value => { |
| 756 | return DocData.WalkResult{ .void = {} }; |
| 757 | }, |
| 758 | .unreachable_value => { |
| 759 | return DocData.WalkResult{ .@"unreachable" = {} }; |
| 760 | }, |
| 761 | .null_value => { |
| 762 | return DocData.WalkResult{ .@"null" = .unspecified }; |
| 763 | }, |
| 764 | .bool_true => { |
| 765 | return DocData.WalkResult{ .bool = true }; |
| 766 | }, |
| 767 | .bool_false => { |
| 768 | return DocData.WalkResult{ .bool = false }; |
| 769 | }, |
| 770 | .empty_struct => { |
| 771 | return DocData.WalkResult{ .@"struct" = .{ |
| 772 | .typeRef = .unspecified, |
| 773 | .fieldVals = &.{}, |
| 774 | } }; |
| 775 | }, |
| 776 | .zero_usize => { |
| 777 | return DocData.WalkResult{ .int = .{ |
| 778 | .typeRef = .{ .type = @enumToInt(Ref.usize_type) }, |
| 779 | .value = 0, |
| 780 | } }; |
| 781 | }, |
| 782 | .one_usize => { |
| 783 | return DocData.WalkResult{ .int = .{ |
| 784 | .typeRef = .{ .type = @enumToInt(Ref.usize_type) }, |
| 785 | .value = 1, |
| 786 | } }; |
| 787 | }, |
| 788 | // TODO: dunno what to do with those |
| 789 | // .calling_convention_c => { |
| 790 | // return DocData.WalkResult{ .int = .{ |
| 791 | // .type = @enumToInt(Ref.comptime_int_type), |
| 792 | // .value = 1, |
| 793 | // } }; |
| 794 | // }, |
| 795 | // .calling_convention_inline => { |
| 796 | // return DocData.WalkResult{ .int = .{ |
| 797 | // .type = @enumToInt(Ref.comptime_int_type), |
| 798 | // .value = 1, |
| 799 | // } }; |
| 800 | // }, |
| 801 | // .generic_poison => { |
| 802 | // return DocData.WalkResult{ .int = .{ |
| 803 | // .type = @enumToInt(Ref.comptime_int_type), |
| 804 | // .value = 1, |
| 805 | // } }; |
| 806 | // }, |
| 807 | } |
| 655 | 808 | } else { |
| 656 | 809 | const zir_index = enum_value - Ref.typed_value_map.len; |
| 657 | 810 | return self.walkInstruction(zir, parent_scope, zir_index); |
| 658 | 811 | } |
| 659 | 812 | } |
| 813 | |
| 814 | fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef { |
| 815 | return switch (wr) { |
| 816 | else => std.debug.panic( |
| 817 | "TODO: handle `{s}` in `walkInstruction.as_node.dest_type`\n", |
| 818 | .{@tagName(wr)}, |
| 819 | ), |
| 820 | |
| 821 | .declRef => |v| .{ .declRef = v }, |
| 822 | .type => |v| .{ .type = v }, |
| 823 | }; |
| 824 | } |