From 2f1c89629021cda7880f010f6a7e2e484fb92a46 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 18 Apr 2021 18:33:15 +0100 Subject: [PATCH] Add `-doc-format` command for the new .odin-doc file format (to be used to generate documentation tools) --- core/odin/doc-format/doc_format.odin | 251 +++++++ src/build_settings.cpp | 1 + src/check_type.cpp | 1 - src/docs.cpp | 79 +- src/docs_format.cpp | 208 ++++++ src/docs_writer.cpp | 1023 ++++++++++++++++++++++++++ src/llvm_backend.cpp | 1 - src/main.cpp | 5 + src/types.cpp | 1 - 9 files changed, 1551 insertions(+), 19 deletions(-) create mode 100644 core/odin/doc-format/doc_format.odin create mode 100644 src/docs_format.cpp create mode 100644 src/docs_writer.cpp diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin new file mode 100644 index 000000000..e8f6d40f2 --- /dev/null +++ b/core/odin/doc-format/doc_format.odin @@ -0,0 +1,251 @@ +package odin_doc_format + +import "core:mem" + +Array :: struct($T: typeid) { + offset: u32, + length: u32, +} + +String :: distinct Array(byte); + +Version_Type_Major :: 0; +Version_Type_Minor :: 1; +Version_Type_Patch :: 0; + +Version_Type :: struct { + major, minor, patch: u8, + _: u8, +}; + +Version_Type_Default :: Version_Type{ + major=Version_Type_Major, + minor=Version_Type_Minor, + patch=Version_Type_Patch, +}; + +Magic_String :: "odindoc\x00"; + +Header_Base :: struct { + magic: [8]byte, + _: u32, + version: Version_Type, + total_size: u32, + header_size: u32, + hash: u32, +} + +Header :: struct { + using base: Header_Base, + + // NOTE: These arrays reserve the zero element as a sentinel value + files: Array(File), + pkgs: Array(Pkg), + entities: Array(Entity), + types: Array(Type), +} + +File_Index :: distinct u32; +Pkg_Index :: distinct u32; +Entity_Index :: distinct u32; +Type_Index :: distinct u32; + + +Position :: struct { + file: File_Index, + line: u32, + column: u32, + offset: u32, +}; + +File :: struct { + pkg: Pkg_Index, + name: String, +} + +Pkg :: struct { + fullpath: String, + name: String, + docs: String, + files: Array(File_Index), + entities: Array(Entity_Index), +} + +Entity_Kind :: enum u32 { + Invalid = 0, + Constant = 1, + Variable = 2, + Type_Name = 3, + Procedure = 4, + Proc_Group = 5, + Import_Name = 6, + Library_Name = 7, +} + +Entity_Flag :: enum u32 { + Foreign = 0, + Export = 1, + + Param_Using = 2, + Param_Const = 3, + Param_Auto_Cast = 4, + Param_Ellipsis = 5, + Param_CVararg = 6, + Param_No_Alias = 7, + + Type_Alias = 8, + + Var_Thread_Local = 9, +} + +Entity_Flags :: distinct bit_set[Entity_Flag; u32]; + +Entity :: struct { + kind: Entity_Kind, + flags: Entity_Flags, + pos: Position, + name: String, + type: Type_Index, + init_string: String, + _: u32, + comment: String, + docs: String, + foreign_library: Entity_Index, + link_name: String, + attributes: Array(Attribute), + grouped_entities: Array(Entity_Index), // Procedure Groups + where_clauses: Array(String), // Procedures +} + +Attribute :: struct { + name: String, + value: String, +} + +Type_Kind :: enum u32 { + Invalid = 0, + Basic = 1, + Named = 2, + Generic = 3, + Pointer = 4, + Array = 5, + Enumerated_Array = 6, + Slice = 7, + Dynamic_Array = 8, + Map = 9, + Struct = 10, + Union = 11, + Enum = 12, + Tuple = 13, + Proc = 14, + Bit_Set = 15, + Simd_Vector = 16, + SOA_Struct_Fixed = 17, + SOA_Struct_Slice = 18, + SOA_Struct_Dynamic = 19, + Relative_Pointer = 20, + Relative_Slice = 21, +} + +Type_Elems_Cap :: 4; + +Type :: struct { + kind: Type_Kind, + flags: u32, // Type_Kind specific + name: String, + custom_align: String, + + // Used by some types + elem_count_len: u32, + elem_counts: [Type_Elems_Cap]u64, + + // Each of these is esed by some types, not all + types: Array(Type_Index), + entities: Array(Entity_Index), + polymorphic_params: Type_Index, // Struct, Union + where_clauses: Array(String), // Struct, Union +} + +Type_Flags_Basic :: distinct bit_set[Type_Flag_Basic; u32]; +Type_Flag_Basic :: enum u32 { + Untyped = 1, +} + +Type_Flags_Struct :: distinct bit_set[Type_Flag_Struct; u32]; +Type_Flag_Struct :: enum u32 { + Polymorphic = 0, + Packed = 1, + Raw_Union = 2, +} + +Type_Flags_Union :: distinct bit_set[Type_Flag_Union; u32]; +Type_Flag_Union :: enum u32 { + Polymorphic = 0, + No_Nil = 1, + Maybe = 2, +} + +Type_Flags_Proc :: distinct bit_set[Type_Flag_Proc; u32]; +Type_Flag_Proc :: enum u32 { + Polymorphic = 0, + Diverging = 1, + Optional_Ok = 2, + Variadic = 3, + C_Vararg = 4, +} + +Type_Flags_BitSet :: distinct bit_set[Type_Flag_BitSet; u32]; +Type_Flag_BitSet :: enum u32 { + Range = 1, + Op_Lt = 2, + Op_Lt_Eq = 3, + Underlying_Type = 4, +} + +Type_Flags_SimdVector :: distinct bit_set[Type_Flag_SimdVector; u32]; +Type_Flag_SimdVector :: enum u32 { + x86_mmx = 1, +} + +from_array :: proc(base: ^Header_Base, a: $A/Array($T)) -> []T { + s: mem.Raw_Slice; + s.data = rawptr(uintptr(base) + uintptr(a.offset)); + s.len = int(a.length); + return transmute([]T)s; +} +from_string :: proc(base: ^Header_Base, s: String) -> string { + return string(from_array(base, s)); +} + + + + +Reader_Error :: enum { + None, + Header_Too_Small, + Invalid_Magic, + Data_Too_Small, + Invalid_Version, +} + +read_from_bytes :: proc(data: []byte) -> (h: ^Header, err: Reader_Error) { + if len(data) < size_of(Header_Base) { + err = .Header_Too_Small; + return; + } + header_base := (^Header_Base)(raw_data(data)); + if header_base.magic != Magic_String { + err = .Invalid_Magic; + return; + } + if len(data) < int(header_base.total_size) { + err = .Data_Too_Small; + return; + } + if header_base.version != Version_Type_Default { + err = .Invalid_Version; + return; + } + h = (^Header)(header_base); + return; +} diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 6d32d1c8d..8df045a82 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -135,6 +135,7 @@ char const *odin_command_strings[32] = { enum CmdDocFlag : u32 { CmdDocFlag_Short = 1<<0, CmdDocFlag_AllPackages = 1<<1, + CmdDocFlag_DocFormat = 1<<2, }; diff --git a/src/check_type.cpp b/src/check_type.cpp index 8420c4687..de81592c8 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2461,7 +2461,6 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, type->Proc.specialization_count = specialization_count; type->Proc.diverging = pt->diverging; type->Proc.optional_ok = optional_ok; - type->Proc.tags = pt->tags; if (param_count > 0) { Entity *end = params->Tuple.variables[param_count-1]; diff --git a/src/docs.cpp b/src/docs.cpp index aa1b89560..65166faa4 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -34,9 +34,17 @@ GB_COMPARE_PROC(cmp_entities_for_printing) { Entity *x = *cast(Entity **)a; Entity *y = *cast(Entity **)b; int res = 0; - res = string_compare(x->pkg->name, y->pkg->name); - if (res != 0) { - return res; + if (x->pkg != y->pkg) { + if (x->pkg == nullptr) { + return -1; + } + if (y->pkg == nullptr) { + return +1; + } + res = string_compare(x->pkg->name, y->pkg->name); + if (res != 0) { + return res; + } } int ox = print_entity_kind_ordering[x->kind]; int oy = print_entity_kind_ordering[y->kind]; @@ -56,6 +64,9 @@ GB_COMPARE_PROC(cmp_ast_package_by_name) { return string_compare(x->name, y->name); } +#include "docs_format.cpp" +#include "docs_writer.cpp" + void print_doc_line(i32 indent, char const *fmt, ...) { while (indent --> 0) { gb_printf("\t"); @@ -297,23 +308,59 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) { void generate_documentation(Checker *c) { CheckerInfo *info = &c->info; - auto pkgs = array_make(permanent_allocator(), 0, info->packages.entries.count); - for_array(i, info->packages.entries) { - AstPackage *pkg = info->packages.entries[i].value; - if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) { - array_add(&pkgs, pkg); + if (build_context.cmd_doc_flags & CmdDocFlag_DocFormat) { + String init_fullpath = c->parser->init_fullpath; + String output_name = {}; + String output_base = {}; + + if (build_context.out_filepath.len == 0) { + output_name = remove_directory_from_path(init_fullpath); + output_name = remove_extension_from_path(output_name); + output_name = string_trim_whitespace(output_name); + if (output_name.len == 0) { + output_name = info->init_scope->pkg->name; + } + output_base = output_name; } else { - if (pkg->kind == Package_Init) { - array_add(&pkgs, pkg); - } else if (pkg->is_extra) { - array_add(&pkgs, pkg); + output_name = build_context.out_filepath; + output_name = string_trim_whitespace(output_name); + if (output_name.len == 0) { + output_name = info->init_scope->pkg->name; + } + isize pos = string_extension_position(output_name); + if (pos < 0) { + output_base = output_name; + } else { + output_base = substring(output_name, 0, pos); } } - } - gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name); + output_base = path_to_full_path(permanent_allocator(), output_base); - for_array(i, pkgs) { - print_doc_package(info, pkgs[i]); + gbString output_file_path = gb_string_make_length(heap_allocator(), output_base.text, output_base.len); + output_file_path = gb_string_appendc(output_file_path, ".odin-doc"); + defer (gb_string_free(output_file_path)); + + odin_doc_write(info, output_file_path); + } else { + auto pkgs = array_make(permanent_allocator(), 0, info->packages.entries.count); + for_array(i, info->packages.entries) { + AstPackage *pkg = info->packages.entries[i].value; + if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) { + array_add(&pkgs, pkg); + } else { + if (pkg->kind == Package_Init) { + array_add(&pkgs, pkg); + } else if (pkg->is_extra) { + array_add(&pkgs, pkg); + } + } + } + + gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name); + + for_array(i, pkgs) { + print_doc_package(info, pkgs[i]); + } } } diff --git a/src/docs_format.cpp b/src/docs_format.cpp new file mode 100644 index 000000000..f30a0bf43 --- /dev/null +++ b/src/docs_format.cpp @@ -0,0 +1,208 @@ +#define OdinDocHeader_MagicString "odindoc\0" + +template +struct OdinDocArray { + u32 offset; + u32 length; +}; + +using OdinDocString = OdinDocArray; + +struct OdinDocVersionType { + u8 major, minor, patch; + u8 pad0; +}; + +#define OdinDocVersionType_Major 0 +#define OdinDocVersionType_Minor 1 +#define OdinDocVersionType_Patch 0 + +struct OdinDocHeaderBase { + u8 magic[8]; + u32 padding0; + OdinDocVersionType version; + u32 total_size; + u32 header_size; + u32 hash; // after header +}; + +template +Slice from_array(OdinDocHeaderBase *base, OdinDocArray const &a) { + Slice s = {}; + s.data = cast(T *)(cast(uintptr)base + cast(uintptr)a.offset); + s.count = cast(isize)a.length; + return s; +} + +String from_string(OdinDocHeaderBase *base, OdinDocString const &s) { + String str = {}; + str.text = cast(u8 *)(cast(uintptr)base + cast(uintptr)s.offset); + str.len = cast(isize)s.length; + return str; +} + +typedef u32 OdinDocFileIndex; +typedef u32 OdinDocPkgIndex; +typedef u32 OdinDocEntityIndex; +typedef u32 OdinDocTypeIndex; + +struct OdinDocFile { + OdinDocPkgIndex pkg; + OdinDocString name; +}; + +struct OdinDocPosition { + OdinDocFileIndex file; + u32 line; + u32 column; + u32 offset; +}; + +enum OdinDocTypeKind : u32 { + OdinDocType_Invalid = 0, + OdinDocType_Basic = 1, + OdinDocType_Named = 2, + OdinDocType_Generic = 3, + OdinDocType_Pointer = 4, + OdinDocType_Array = 5, + OdinDocType_EnumeratedArray = 6, + OdinDocType_Slice = 7, + OdinDocType_DynamicArray = 8, + OdinDocType_Map = 9, + OdinDocType_Struct = 10, + OdinDocType_Union = 11, + OdinDocType_Enum = 12, + OdinDocType_Tuple = 13, + OdinDocType_Proc = 14, + OdinDocType_BitSet = 15, + OdinDocType_SimdVector = 16, + OdinDocType_SOAStructFixed = 17, + OdinDocType_SOAStructSlice = 18, + OdinDocType_SOAStructDynamic = 19, + OdinDocType_RelativePointer = 20, + OdinDocType_RelativeSlice = 21, +}; + +enum OdinDocTypeFlag_Basic : u32 { + OdinDocTypeFlag_Basic_untyped = 1<<1, +}; + +enum OdinDocTypeFlag_Struct : u32 { + OdinDocTypeFlag_Struct_polymorphic = 1<<0, + OdinDocTypeFlag_Struct_packed = 1<<1, + OdinDocTypeFlag_Struct_raw_union = 1<<2, +}; + +enum OdinDocTypeFlag_Union : u32 { + OdinDocTypeFlag_Union_polymorphic = 1<<0, + OdinDocTypeFlag_Union_no_nil = 1<<1, + OdinDocTypeFlag_Union_maybe = 1<<2, +}; + +enum OdinDocTypeFlag_Proc : u32 { + OdinDocTypeFlag_Proc_polymorphic = 1<<0, + OdinDocTypeFlag_Proc_diverging = 1<<1, + OdinDocTypeFlag_Proc_optional_ok = 1<<2, + OdinDocTypeFlag_Proc_variadic = 1<<3, + OdinDocTypeFlag_Proc_c_vararg = 1<<4, +}; + +enum OdinDocTypeFlag_BitSet : u32 { + OdinDocTypeFlag_BitSet_Range = 1<<1, + OdinDocTypeFlag_BitSet_OpLt = 1<<2, + OdinDocTypeFlag_BitSet_OpLtEq = 1<<3, + OdinDocTypeFlag_BitSet_UnderlyingType = 1<<4, +}; + +enum OdinDocTypeFlag_SimdVector : u32 { + OdinDocTypeFlag_BitSet_x86_mmx = 1<<1, +}; + +enum { + // constants + OdinDocType_ElemsCap = 4, +}; + +struct OdinDocType { + OdinDocTypeKind kind; + u32 flags; + OdinDocString name; + OdinDocString custom_align; + + // Used by some types + u32 elem_count_len; + u64 elem_counts[OdinDocType_ElemsCap]; + + // Each of these is esed by some types, not all + OdinDocArray types; + OdinDocArray entities; + OdinDocTypeIndex polmorphic_params; + OdinDocArray where_clauses; +}; + +struct OdinDocAttribute { + OdinDocString name; + OdinDocString value; +}; + +enum OdinDocEntityKind : u32 { + OdinDocEntity_Invalid = 0, + OdinDocEntity_Constant = 1, + OdinDocEntity_Variable = 2, + OdinDocEntity_TypeName = 3, + OdinDocEntity_Procedure = 4, + OdinDocEntity_ProcGroup = 5, + OdinDocEntity_ImportName = 6, + OdinDocEntity_LibraryName = 7, +}; + +enum OdinDocEntityFlag : u32 { + OdinDocEntityFlag_Foreign = 1<<0, + OdinDocEntityFlag_Export = 1<<1, + + OdinDocEntityFlag_Param_Using = 1<<2, + OdinDocEntityFlag_Param_Const = 1<<3, + OdinDocEntityFlag_Param_AutoCast = 1<<4, + OdinDocEntityFlag_Param_Ellipsis = 1<<5, + OdinDocEntityFlag_Param_CVararg = 1<<6, + OdinDocEntityFlag_Param_NoAlias = 1<<7, + + OdinDocEntityFlag_Type_Alias = 1<<8, + + OdinDocEntityFlag_Var_Thread_Local = 1<<9, +}; + +struct OdinDocEntity { + OdinDocEntityKind kind; + u32 flags; + OdinDocPosition pos; + OdinDocString name; + OdinDocTypeIndex type; + OdinDocString init_string; + u32 reserved_for_init; + OdinDocString comment; + OdinDocString docs; + OdinDocEntityIndex foreign_library; + OdinDocString link_name; + OdinDocArray attributes; + OdinDocArray grouped_entities; // Procedure Groups + OdinDocArray where_clauses; // Procedures +}; + +struct OdinDocPkg { + OdinDocString fullpath; + OdinDocString name; + OdinDocString docs; + OdinDocArray files; + OdinDocArray entities; +}; + +struct OdinDocHeader { + OdinDocHeaderBase base; + + OdinDocArray files; + OdinDocArray pkgs; + OdinDocArray entities; + OdinDocArray types; +}; + diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp new file mode 100644 index 000000000..daae40930 --- /dev/null +++ b/src/docs_writer.cpp @@ -0,0 +1,1023 @@ + +template +struct OdinDocWriterItemTracker { + isize len; + isize cap; + isize offset; +}; + +enum OdinDocWriterState { + OdinDocWriterState_Preparing, + OdinDocWriterState_Writing, +}; + +char const* OdinDocWriterState_strings[] { + "preparing", + "writing ", +}; + +struct OdinDocWriter { + CheckerInfo *info; + OdinDocWriterState state; + + void *data; + isize data_len; + OdinDocHeader *header; + + StringMap string_cache; + + Map file_cache; // Key: AstFile * + Map pkg_cache; // Key: AstPackage * + Map entity_cache; // Key: Entity * + Map entity_id_cache; // Key: OdinDocEntityIndex + Map type_cache; // Key: Type * + Map type_id_cache; // Key: OdinDocTypeIndex + + OdinDocWriterItemTracker files; + OdinDocWriterItemTracker pkgs; + OdinDocWriterItemTracker entities; + OdinDocWriterItemTracker types; + + OdinDocWriterItemTracker strings; + OdinDocWriterItemTracker blob; +}; + +OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e); +OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type); + +template +void odin_doc_writer_item_tracker_init(OdinDocWriterItemTracker *t, isize size) { + t->len = size; + t->cap = size; +} + + +void odin_doc_writer_prepare(OdinDocWriter *w) { + w->state = OdinDocWriterState_Preparing; + + gbAllocator a = heap_allocator(); + string_map_init(&w->string_cache, a); + + map_init(&w->file_cache, a); + map_init(&w->pkg_cache, a); + map_init(&w->entity_cache, a); + map_init(&w->entity_id_cache, a); + map_init(&w->type_cache, a); + map_init(&w->type_id_cache, a); + + odin_doc_writer_item_tracker_init(&w->files, 1); + odin_doc_writer_item_tracker_init(&w->pkgs, 1); + odin_doc_writer_item_tracker_init(&w->entities, 1); + odin_doc_writer_item_tracker_init(&w->types, 1); + odin_doc_writer_item_tracker_init(&w->strings, 16); + odin_doc_writer_item_tracker_init(&w->blob, 16); +} + + +void odin_doc_writer_destroy(OdinDocWriter *w) { + gb_free(heap_allocator(), w->data); + + string_map_destroy(&w->string_cache); + map_destroy(&w->file_cache); + map_destroy(&w->pkg_cache); + map_destroy(&w->entity_cache); + map_destroy(&w->entity_id_cache); + map_destroy(&w->type_cache); + map_destroy(&w->type_id_cache); +} + + + +template +void odin_doc_writer_tracker_size(isize *offset, OdinDocWriterItemTracker *t, isize alignment=1) { + isize size = t->cap*gb_size_of(T); + isize align = gb_max(gb_align_of(T), alignment); + *offset = align_formula_isize(*offset, align); + t->offset = *offset; + *offset += size; +} + +isize odin_doc_writer_calc_total_size(OdinDocWriter *w) { + isize total_size = gb_size_of(OdinDocHeader); + odin_doc_writer_tracker_size(&total_size, &w->files); + odin_doc_writer_tracker_size(&total_size, &w->pkgs); + odin_doc_writer_tracker_size(&total_size, &w->entities); + odin_doc_writer_tracker_size(&total_size, &w->types); + odin_doc_writer_tracker_size(&total_size, &w->strings, 16); + odin_doc_writer_tracker_size(&total_size, &w->blob, 16); + return total_size; +} + +void odin_doc_writer_start_writing(OdinDocWriter *w) { + w->state = OdinDocWriterState_Writing; + + string_map_clear(&w->string_cache); + map_clear(&w->file_cache); + map_clear(&w->pkg_cache); + map_clear(&w->entity_cache); + map_clear(&w->entity_id_cache); + map_clear(&w->type_cache); + map_clear(&w->type_id_cache); + + isize total_size = odin_doc_writer_calc_total_size(w); + total_size = align_formula_isize(total_size, 8); + w->data = gb_alloc_align(heap_allocator(), total_size, 8); + w->data_len = total_size; + w->header = cast(OdinDocHeader *)w->data; +} + +u32 hash_data_after_header(OdinDocHeaderBase *base, void *data, isize data_len) { + u8 *start = cast(u8 *)data; + u8 *end = start + base->total_size; + start += base->header_size; + + u32 h = 0x811c9dc5; + for (u8 *b = start; b != end; b++) { + h = (h ^ cast(u32)*b) * 0x01000193; + } + return h; +} + + +template +void odin_doc_writer_assign_tracker(OdinDocArray *array, OdinDocWriterItemTracker const &t) { + array->offset = cast(u32)t.offset; + array->length = cast(u32)t.len; +} + + +void odin_doc_writer_end_writing(OdinDocWriter *w) { + OdinDocHeader *h = w->header; + + gb_memmove(h->base.magic, OdinDocHeader_MagicString, gb_strlen(OdinDocHeader_MagicString)); + h->base.version.major = OdinDocVersionType_Major; + h->base.version.minor = OdinDocVersionType_Minor; + h->base.version.patch = OdinDocVersionType_Patch; + h->base.total_size = cast(u32)w->data_len; + h->base.header_size = gb_size_of(*h); + h->base.hash = hash_data_after_header(&h->base, w->data, w->data_len); + + odin_doc_writer_assign_tracker(&h->files, w->files); + odin_doc_writer_assign_tracker(&h->pkgs, w->pkgs); + odin_doc_writer_assign_tracker(&h->entities, w->entities); + odin_doc_writer_assign_tracker(&h->types, w->types); +} + +template +u32 odin_doc_write_item(OdinDocWriter *w, OdinDocWriterItemTracker *t, T const *item, T **dst=nullptr) { + if (w->state == OdinDocWriterState_Preparing) { + t->cap += 1; + if (dst) *dst = nullptr; + return 0; + } else { + GB_ASSERT_MSG(t->len < t->cap, "%td < %td", t->len, t->cap); + isize item_index = t->len++; + uintptr data = cast(uintptr)w->data + cast(uintptr)(t->offset + gb_size_of(T)*item_index); + if (item) { + gb_memmove(cast(T *)data, item, gb_size_of(T)); + } + if (dst) *dst = cast(T *)data; + + return cast(u32)item_index; + } +} + +template +T *odin_doc_get_item(OdinDocWriter *w, OdinDocWriterItemTracker *t, u32 index) { + if (w->state != OdinDocWriterState_Writing) { + return nullptr; + } + GB_ASSERT(index < t->len); + uintptr data = cast(uintptr)w->data + cast(uintptr)(t->offset + gb_size_of(T)*index); + return cast(T *)data; +} + +OdinDocString odin_doc_write_string_without_cache(OdinDocWriter *w, String const &str) { + OdinDocString res = {}; + + if (w->state == OdinDocWriterState_Preparing) { + w->strings.cap += str.len+1; + } else { + GB_ASSERT_MSG(w->strings.len+str.len+1 <= w->strings.cap, "%td <= %td", w->strings.len+str.len, w->strings.cap); + + isize offset = w->strings.offset + w->strings.len; + u8 *data = cast(u8 *)w->data + offset; + gb_memmove(data, str.text, str.len); + data[str.len] = 0; + w->strings.len += str.len+1; + res.offset = cast(u32)offset; + res.length = cast(u32)str.len; + } + + return res; +} + +OdinDocString odin_doc_write_string(OdinDocWriter *w, String const &str) { + OdinDocString *c = string_map_get(&w->string_cache, str); + if (c != nullptr) { + if (w->state == OdinDocWriterState_Writing) { + GB_ASSERT(from_string(&w->header->base, *c) == str); + } + return *c; + } + + OdinDocString res = odin_doc_write_string_without_cache(w, str); + + string_map_set(&w->string_cache, str, res); + + return res; +} + + + +template +OdinDocArray odin_write_slice(OdinDocWriter *w, T *data, isize len) { + GB_ASSERT(gb_align_of(T) <= 4); + if (len <= 0) { + return {0, 0}; + } + isize alignment = 4; + + if (w->state == OdinDocWriterState_Preparing) { + w->blob.cap = align_formula_isize(w->blob.cap, alignment); + w->blob.cap += len * gb_size_of(T); + return {0, 0}; + } + + w->blob.len = align_formula_isize(w->blob.len, alignment); + + isize offset = w->blob.offset + w->blob.len; + u8 *dst = cast(u8 *)w->data + offset; + gb_memmove(dst, data, len*gb_size_of(T)); + + w->blob.len += len * gb_size_of(T); + + return {cast(u32)offset, cast(u32)len}; +} + + +template +OdinDocArray odin_write_item_as_slice(OdinDocWriter *w, T data) { + return odin_write_slice(w, &data, 1); +} + + +OdinDocPosition odin_doc_token_pos_cast(OdinDocWriter *w, TokenPos const &pos) { + OdinDocFileIndex file_index = 0; + if (pos.file_id != 0) { + String file_path = get_file_path_string(pos.file_id); + if (file_path != "") { + AstFile **found = string_map_get(&w->info->files, file_path); + GB_ASSERT(found != nullptr); + AstFile *file = *found; + OdinDocFileIndex *file_index_found = map_get(&w->file_cache, hash_pointer(file)); + GB_ASSERT(file_index_found != nullptr); + file_index = *file_index_found; + } + } + + OdinDocPosition doc_pos = {}; + doc_pos.file = file_index; + doc_pos.line = cast(u32)pos.line; + doc_pos.column = cast(u32)pos.column; + doc_pos.offset = cast(u32)pos.offset; + return doc_pos; +} + +bool odin_doc_append_comment_group_string(Array *buf, CommentGroup *g) { + if (g == nullptr) { + return false; + } + isize len = 0; + for_array(i, g->list) { + String comment = g->list[i].string; + len += comment.len; + len += 1; // for \n + } + if (len <= g->list.count) { + return false; + } + + isize count = 0; + for_array(i, g->list) { + String comment = g->list[i].string; + String original_comment = comment; + + bool slash_slash = comment[1] == '/'; + bool slash_star = comment[1] == '*'; + if (comment[1] == '/') { + comment.text += 2; + comment.len -= 2; + } else if (comment[1] == '*') { + comment.text += 2; + comment.len -= 4; + } + + // Ignore the first space + if (comment.len > 0 && comment[0] == ' ') { + comment.text += 1; + comment.len -= 1; + } + + if (slash_slash) { + if (string_starts_with(comment, str_lit("+"))) { + continue; + } + if (string_starts_with(comment, str_lit("@("))) { + continue; + } + } + + if (slash_slash) { + array_add_elems(buf, comment.text, comment.len); + array_add(buf, cast(u8)'\n'); + count += 1; + } else { + isize pos = 0; + for (; pos < comment.len; pos++) { + isize end = pos; + for (; end < comment.len; end++) { + if (comment[end] == '\n') { + break; + } + } + String line = substring(comment, pos, end); + pos = end+1; + String trimmed_line = string_trim_whitespace(line); + if (trimmed_line.len == 0) { + if (count == 0) { + continue; + } + } + /* + * Remove comments with + * styles + * like this + */ + if (string_starts_with(line, str_lit("* "))) { + line = substring(line, 2, line.len); + } + + array_add_elems(buf, line.text, line.len); + array_add(buf, cast(u8)'\n'); + count += 1; + } + } + } + + if (count > 0) { + array_add(buf, cast(u8)'\n'); + return true; + } + return false; +} + +OdinDocString odin_doc_pkg_doc_string(OdinDocWriter *w, AstPackage *pkg) { + if (pkg == nullptr) { + return {}; + } + auto buf = array_make(permanent_allocator(), 0, 0); // Minor leak + + for_array(i, pkg->files) { + AstFile *f = pkg->files[i]; + if (f->pkg_decl) { + GB_ASSERT(f->pkg_decl->kind == Ast_PackageDecl); + odin_doc_append_comment_group_string(&buf, f->pkg_decl->PackageDecl.docs); + } + } + + return odin_doc_write_string_without_cache(w, make_string(buf.data, buf.count)); +} + +OdinDocString odin_doc_comment_group_string(OdinDocWriter *w, CommentGroup *g) { + if (g == nullptr) { + return {}; + } + auto buf = array_make(permanent_allocator(), 0, 0); // Minor leak + + odin_doc_append_comment_group_string(&buf, g); + + return odin_doc_write_string_without_cache(w, make_string(buf.data, buf.count)); +} + +OdinDocString odin_doc_expr_string(OdinDocWriter *w, Ast *expr) { + if (expr == nullptr) { + return {}; + } + gbString s = write_expr_to_string( // Minor leak + gb_string_make(permanent_allocator(), ""), + expr, + build_context.cmd_doc_flags & CmdDocFlag_Short + ); + + return odin_doc_write_string(w, make_string(cast(u8 *)s, gb_string_length(s))); +} + +OdinDocArray odin_doc_attributes(OdinDocWriter *w, Array const &attributes) { + isize count = 0; + for_array(i, attributes) { + Ast *attr = attributes[i]; + if (attr->kind != Ast_Attribute) continue; + count += attr->Attribute.elems.count; + }; + + auto attribs = array_make(heap_allocator(), 0, count); + defer (array_free(&attribs)); + + for_array(i, attributes) { + Ast *attr = attributes[i]; + if (attr->kind != Ast_Attribute) continue; + for_array(j, attr->Attribute.elems) { + Ast *elem = attr->Attribute.elems[j]; + String name = {}; + Ast *value = nullptr; + switch (elem->kind) { + case_ast_node(i, Ident, elem); + name = i->token.string; + case_end; + case_ast_node(i, Implicit, elem); + name = i->string; + case_end; + case_ast_node(fv, FieldValue, elem); + if (fv->field->kind == Ast_Ident) { + name = fv->field->Ident.token.string; + } else if (fv->field->kind == Ast_Implicit) { + name = fv->field->Implicit.string; + } + value = fv->value; + case_end; + default: + continue; + } + + OdinDocAttribute doc_attrib = {}; + doc_attrib.name = odin_doc_write_string(w, name); + doc_attrib.value = odin_doc_expr_string(w, value); + array_add(&attribs, doc_attrib); + } + } + return odin_write_slice(w, attribs.data, attribs.count); +} + +OdinDocArray odin_doc_where_clauses(OdinDocWriter *w, Slice const &where_clauses) { + if (where_clauses.count == 0) { + return {}; + } + auto clauses = array_make(heap_allocator(), where_clauses.count); + defer (array_free(&clauses)); + + for_array(i, where_clauses) { + clauses[i] = odin_doc_expr_string(w, where_clauses[i]); + } + + return odin_write_slice(w, clauses.data, clauses.count); +} + +OdinDocArray odin_doc_type_as_slice(OdinDocWriter *w, Type *type) { + OdinDocTypeIndex index = odin_doc_type(w, type); + return odin_write_item_as_slice(w, index); +} + +OdinDocArray odin_doc_add_entity_as_slice(OdinDocWriter *w, Entity *e) { + OdinDocEntityIndex index = odin_doc_add_entity(w, e); + return odin_write_item_as_slice(w, index); +} + +OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { + if (type == nullptr) { + return 0; + } + OdinDocTypeIndex *found = map_get(&w->type_cache, hash_pointer(type)); + if (found) { + return *found; + } + for_array(i, w->type_cache.entries) { + // NOTE(bill): THIS IS SLOW + Type *other = cast(Type *)cast(uintptr)w->type_cache.entries[i].key.key; + if (are_types_identical(type, other)) { + OdinDocTypeIndex index = w->type_cache.entries[i].value; + map_set(&w->type_cache, hash_pointer(type), index); + return index; + } + } + + + OdinDocType *dst = nullptr; + OdinDocType doc_type = {}; + OdinDocTypeIndex type_index = 0; + type_index = odin_doc_write_item(w, &w->types, &doc_type, &dst); + map_set(&w->type_cache, hash_pointer(type), type_index); + map_set(&w->type_id_cache, hash_integer(type_index), type); + + + switch (type->kind) { + case Type_Basic: + doc_type.kind = OdinDocType_Basic; + doc_type.name = odin_doc_write_string(w, type->Basic.name); + if (is_type_untyped(type)) { + doc_type.flags |= OdinDocTypeFlag_Basic_untyped; + } + break; + case Type_Named: + doc_type.kind = OdinDocType_Named; + doc_type.name = odin_doc_write_string(w, type->Named.name); + doc_type.entities = odin_doc_add_entity_as_slice(w, type->Named.type_name); + break; + case Type_Generic: + doc_type.kind = OdinDocType_Generic; + doc_type.name = odin_doc_write_string(w, type->Generic.name); + if (type->Generic.specialized) { + doc_type.types = odin_doc_type_as_slice(w, type->Generic.specialized); + } + break; + case Type_Pointer: + doc_type.kind = OdinDocType_Pointer; + doc_type.types = odin_doc_type_as_slice(w, type->Pointer.elem); + break; + case Type_Array: + doc_type.kind = OdinDocType_Array; + doc_type.elem_count_len = 1; + doc_type.elem_counts[0] = type->Array.count; + doc_type.types = odin_doc_type_as_slice(w, type->Array.elem); + break; + case Type_EnumeratedArray: + doc_type.kind = OdinDocType_EnumeratedArray; + doc_type.elem_count_len = 1; + doc_type.elem_counts[0] = type->EnumeratedArray.count; + doc_type.types = odin_doc_type_as_slice(w, type->EnumeratedArray.elem); + break; + case Type_Slice: + doc_type.kind = OdinDocType_Slice; + doc_type.types = odin_doc_type_as_slice(w, type->Slice.elem); + break; + case Type_DynamicArray: + doc_type.kind = OdinDocType_DynamicArray; + doc_type.types = odin_doc_type_as_slice(w, type->DynamicArray.elem); + break; + case Type_Map: + doc_type.kind = OdinDocType_Map; + { + OdinDocTypeIndex types[2] = {}; + types[0] = odin_doc_type(w, type->Map.key); + types[1] = odin_doc_type(w, type->Map.value); + doc_type.types = odin_write_slice(w, types, gb_count_of(types)); + } + break; + case Type_Struct: + doc_type.kind = OdinDocType_Struct; + if (type->Struct.soa_kind != StructSoa_None) { + switch (type->Struct.soa_kind) { + case StructSoa_Fixed: + doc_type.kind = OdinDocType_SOAStructFixed; + doc_type.elem_count_len = 1; + doc_type.elem_counts[0] = type->Struct.soa_count; + break; + case StructSoa_Slice: + doc_type.kind = OdinDocType_SOAStructSlice; + break; + case StructSoa_Dynamic: + doc_type.kind = OdinDocType_SOAStructDynamic; + break; + } + doc_type.types = odin_doc_type_as_slice(w, type->Struct.soa_elem); + } else { + if (type->Struct.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Struct_polymorphic; } + if (type->Struct.is_packed) { doc_type.flags |= OdinDocTypeFlag_Struct_packed; } + if (type->Struct.is_raw_union) { doc_type.flags |= OdinDocTypeFlag_Struct_raw_union; } + + auto fields = array_make(heap_allocator(), type->Struct.fields.count); + defer (array_free(&fields)); + + for_array(i, type->Struct.fields) { + fields[i] = odin_doc_add_entity(w, type->Struct.fields[i]); + } + + doc_type.entities = odin_write_slice(w, fields.data, fields.count); + doc_type.polmorphic_params = odin_doc_type(w, type->Struct.polymorphic_params); + + if (type->Struct.node) { + ast_node(st, StructType, type->Struct.node); + if (st->align) { + doc_type.custom_align = odin_doc_expr_string(w, st->align); + } + doc_type.where_clauses = odin_doc_where_clauses(w, st->where_clauses); + } + } + break; + case Type_Union: + doc_type.kind = OdinDocType_Union; + if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; } + if (type->Union.no_nil) { doc_type.flags |= OdinDocTypeFlag_Union_no_nil; } + if (type->Union.maybe) { doc_type.flags |= OdinDocTypeFlag_Union_maybe; } + + { + auto variants = array_make(heap_allocator(), type->Union.variants.count); + defer (array_free(&variants)); + + for_array(i, type->Union.variants) { + variants[i] = odin_doc_type(w, type->Union.variants[i]); + } + + doc_type.types = odin_write_slice(w, variants.data, variants.count); + doc_type.polmorphic_params = odin_doc_type(w, type->Union.polymorphic_params); + } + + if (type->Union.node) { + ast_node(ut, UnionType, type->Union.node); + if (ut->align) { + doc_type.custom_align = odin_doc_expr_string(w, ut->align); + } + doc_type.where_clauses = odin_doc_where_clauses(w, ut->where_clauses); + } + break; + case Type_Enum: + doc_type.kind = OdinDocType_Enum; + { + auto fields = array_make(heap_allocator(), type->Enum.fields.count); + defer (array_free(&fields)); + + for_array(i, type->Enum.fields) { + fields[i] = odin_doc_add_entity(w, type->Enum.fields[i]); + } + doc_type.entities = odin_write_slice(w, fields.data, fields.count); + if (type->Enum.base_type) { + doc_type.types = odin_doc_type_as_slice(w, type->Enum.base_type); + } + } + break; + case Type_Tuple: + doc_type.kind = OdinDocType_Tuple; + { + auto variables = array_make(heap_allocator(), type->Tuple.variables.count); + defer (array_free(&variables)); + + for_array(i, type->Tuple.variables) { + variables[i] = odin_doc_add_entity(w, type->Tuple.variables[i]); + } + + doc_type.entities = odin_write_slice(w, variables.data, variables.count); + } + break; + case Type_Proc: + doc_type.kind = OdinDocType_Proc; + if (type->Proc.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Proc_polymorphic; } + if (type->Proc.diverging) { doc_type.flags |= OdinDocTypeFlag_Proc_diverging; } + if (type->Proc.optional_ok) { doc_type.flags |= OdinDocTypeFlag_Proc_optional_ok; } + if (type->Proc.variadic) { doc_type.flags |= OdinDocTypeFlag_Proc_variadic; } + if (type->Proc.c_vararg) { doc_type.flags |= OdinDocTypeFlag_Proc_c_vararg; } + { + OdinDocTypeIndex types[2]; + types[0] = odin_doc_type(w, type->Proc.params); + types[1] = odin_doc_type(w, type->Proc.results); + doc_type.types = odin_write_slice(w, types, gb_count_of(types)); + } + break; + case Type_BitSet: + doc_type.kind = OdinDocType_BitSet; + { + isize type_count = 0; + OdinDocTypeIndex types[2] = {}; + if (type->BitSet.elem) { + types[type_count++] = odin_doc_type(w, type->BitSet.elem); + } + if (type->BitSet.underlying) { + types[type_count++] = odin_doc_type(w, type->BitSet.underlying); + doc_type.flags |= OdinDocTypeFlag_BitSet_UnderlyingType; + } + doc_type.types = odin_write_slice(w, types, type_count); + doc_type.elem_count_len = 2; + doc_type.elem_counts[0] = cast(u64)type->BitSet.lower; + doc_type.elem_counts[1] = cast(u64)type->BitSet.upper; + } + break; + case Type_SimdVector: + doc_type.kind = OdinDocType_SimdVector; + if (type->SimdVector.is_x86_mmx) { + doc_type.flags |= OdinDocTypeFlag_BitSet_x86_mmx; + } else { + doc_type.elem_count_len = 1; + doc_type.elem_counts[0] = type->SimdVector.count; + doc_type.types = odin_doc_type_as_slice(w, type->SimdVector.elem); + } + // TODO(bill): + break; + case Type_RelativePointer: + doc_type.kind = OdinDocType_RelativePointer; + { + OdinDocTypeIndex types[2] = {}; + types[0] = odin_doc_type(w, type->RelativePointer.pointer_type); + types[1] = odin_doc_type(w, type->RelativePointer.base_integer); + doc_type.types = odin_write_slice(w, types, gb_count_of(types)); + } + break; + case Type_RelativeSlice: + doc_type.kind = OdinDocType_RelativeSlice; + { + OdinDocTypeIndex types[2] = {}; + types[0] = odin_doc_type(w, type->RelativeSlice.slice_type); + types[1] = odin_doc_type(w, type->RelativeSlice.base_integer); + doc_type.types = odin_write_slice(w, types, gb_count_of(types)); + } + break; + } + + if (dst) { + *dst = doc_type; + } + return type_index; +} +OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) { + if (e == nullptr) { + return 0; + } + + OdinDocEntityIndex *prev_index = map_get(&w->entity_cache, hash_pointer(e)); + if (prev_index) { + return *prev_index; + } + + if (e->pkg != nullptr && map_get(&w->pkg_cache, hash_pointer(e->pkg)) == nullptr) { + return 0; + } + + OdinDocEntity doc_entity = {}; + OdinDocEntity* dst = nullptr; + + OdinDocEntityIndex doc_entity_index = odin_doc_write_item(w, &w->entities, &doc_entity, &dst); + map_set(&w->entity_cache, hash_pointer(e), doc_entity_index); + map_set(&w->entity_id_cache, hash_integer(doc_entity_index), e); + + + Ast *type_expr = nullptr; + Ast *init_expr = nullptr; + Ast *decl_node = nullptr; + CommentGroup *comment = nullptr; + CommentGroup *docs = nullptr; + if (e->decl_info != nullptr) { + type_expr = e->decl_info->type_expr; + init_expr = e->decl_info->init_expr; + decl_node = e->decl_info->decl_node; + comment = e->decl_info->comment; + docs = e->decl_info->docs; + } + + String link_name = {}; + + OdinDocEntityKind kind = OdinDocEntity_Invalid; + u32 flags = 0; + + switch (e->kind) { + case Entity_Invalid: kind = OdinDocEntity_Invalid; break; + case Entity_Constant: kind = OdinDocEntity_Constant; break; + case Entity_Variable: kind = OdinDocEntity_Variable; break; + case Entity_TypeName: kind = OdinDocEntity_TypeName; break; + case Entity_Procedure: kind = OdinDocEntity_Procedure; break; + case Entity_ProcGroup: kind = OdinDocEntity_ProcGroup; break; + case Entity_ImportName: kind = OdinDocEntity_ImportName; break; + case Entity_LibraryName: kind = OdinDocEntity_LibraryName; break; + } + + switch (e->kind) { + case Entity_TypeName: + if (e->TypeName.is_type_alias) { + flags |= OdinDocEntityFlag_Type_Alias; + } + break; + case Entity_Variable: + if (e->Variable.is_foreign) { flags |= OdinDocEntityFlag_Foreign; } + if (e->Variable.is_export) { flags |= OdinDocEntityFlag_Export; } + if (e->Variable.thread_local_model != "") { + flags |= OdinDocEntityFlag_Var_Thread_Local; + } + link_name = e->Variable.link_name; + break; + case Entity_Procedure: + if (e->Procedure.is_foreign) { flags |= OdinDocEntityFlag_Foreign; } + if (e->Procedure.is_export) { flags |= OdinDocEntityFlag_Export; } + link_name = e->Procedure.link_name; + break; + } + + + doc_entity.kind = kind; + doc_entity.flags = flags; + doc_entity.pos = odin_doc_token_pos_cast(w, e->token.pos); + doc_entity.name = odin_doc_write_string(w, e->token.string); + doc_entity.type = 0; // Set later + doc_entity.init_string = odin_doc_expr_string(w, init_expr); + doc_entity.comment = odin_doc_comment_group_string(w, comment); + doc_entity.docs = odin_doc_comment_group_string(w, docs); + doc_entity.foreign_library = 0; // Set later + doc_entity.link_name = odin_doc_write_string(w, link_name); + if (e->decl_info != nullptr) { + doc_entity.attributes = odin_doc_attributes(w, e->decl_info->attributes); + } + doc_entity.grouped_entities = {}; // Set later + + if (dst) { + *dst = doc_entity; + } + + return doc_entity_index; +} + +void odin_doc_update_entities(OdinDocWriter *w) { + { + // NOTE(bill): Double pass, just in case entities are created on odin_doc_type + auto entities = array_make(heap_allocator(), w->entity_cache.entries.count); + defer (array_free(&entities)); + + for_array(i, w->entity_cache.entries) { + Entity *e = cast(Entity *)cast(uintptr)w->entity_cache.entries[i].key.key; + entities[i] = e; + } + for_array(i, entities) { + Entity *e = entities[i]; + OdinDocTypeIndex type_index = odin_doc_type(w, e->type); + } + } + + for_array(i, w->entity_cache.entries) { + Entity *e = cast(Entity *)cast(uintptr)w->entity_cache.entries[i].key.key; + OdinDocEntityIndex entity_index = w->entity_cache.entries[i].value; + OdinDocTypeIndex type_index = odin_doc_type(w, e->type); + + OdinDocEntityIndex foreign_library = 0; + OdinDocArray grouped_entities = {}; + + switch (e->kind) { + case Entity_Variable: + foreign_library = odin_doc_add_entity(w, e->Variable.foreign_library); + break; + case Entity_Procedure: + foreign_library = odin_doc_add_entity(w, e->Procedure.foreign_library); + break; + case Entity_ProcGroup: + { + auto pges = array_make(heap_allocator(), 0, e->ProcGroup.entities.count); + defer (array_free(&pges)); + + for_array(j, e->ProcGroup.entities) { + OdinDocEntityIndex index = odin_doc_add_entity(w, e->ProcGroup.entities[j]); + array_add(&pges, index); + } + grouped_entities = odin_write_slice(w, pges.data, pges.count); + } + break; + } + + OdinDocEntity *dst = odin_doc_get_item(w, &w->entities, entity_index); + if (dst) { + dst->type = type_index; + dst->foreign_library = foreign_library; + dst->grouped_entities = grouped_entities; + } + } +} + + + +OdinDocArray odin_doc_add_pkg_entities(OdinDocWriter *w, AstPackage *pkg) { + if (pkg->scope == nullptr) { + return {}; + } + if (map_get(&w->pkg_cache, hash_pointer(pkg)) == nullptr) { + return {}; + } + + auto entities = array_make(heap_allocator(), 0, pkg->scope->elements.entries.count); + defer (array_free(&entities)); + + for_array(i, pkg->scope->elements.entries) { + Entity *e = pkg->scope->elements.entries[i].value; + switch (e->kind) { + case Entity_Invalid: + case Entity_Builtin: + case Entity_Nil: + case Entity_Label: + continue; + case Entity_Constant: + case Entity_Variable: + case Entity_TypeName: + case Entity_Procedure: + case Entity_ProcGroup: + case Entity_ImportName: + case Entity_LibraryName: + // Fine + break; + } + array_add(&entities, e); + } + gb_sort_array(entities.data, entities.count, cmp_entities_for_printing); + + auto entity_indices = array_make(heap_allocator(), 0, w->entity_cache.entries.count); + defer (array_free(&entity_indices)); + + EntityKind curr_entity_kind = Entity_Invalid; + for_array(i, entities) { + Entity *e = entities[i]; + if (e->pkg != pkg) { + continue; + } + if (!is_entity_exported(e)) { + continue; + } + if (e->token.string.len == 0) { + continue; + } + + OdinDocEntityIndex doc_entity_index = 0; + doc_entity_index = odin_doc_add_entity(w, e); + array_add(&entity_indices, doc_entity_index); + } + + return odin_write_slice(w, entity_indices.data, entity_indices.count); +} + + +void odin_doc_write_docs(OdinDocWriter *w) { + auto pkgs = array_make(heap_allocator(), 0, w->info->packages.entries.count); + defer (array_free(&pkgs)); + for_array(i, w->info->packages.entries) { + AstPackage *pkg = w->info->packages.entries[i].value; + if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) { + array_add(&pkgs, pkg); + } else { + if (pkg->kind == Package_Init) { + array_add(&pkgs, pkg); + } else if (pkg->is_extra) { + array_add(&pkgs, pkg); + } + } + } + + gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name); + + for_array(i, pkgs) { + gbAllocator allocator = heap_allocator(); + + AstPackage *pkg = pkgs[i]; + OdinDocPkg doc_pkg = {}; + doc_pkg.fullpath = odin_doc_write_string(w, pkg->fullpath); + doc_pkg.name = odin_doc_write_string(w, pkg->name); + doc_pkg.docs = odin_doc_pkg_doc_string(w, pkg); + + OdinDocPkg *dst = nullptr; + OdinDocPkgIndex pkg_index = odin_doc_write_item(w, &w->pkgs, &doc_pkg, &dst); + map_set(&w->pkg_cache, hash_pointer(pkg), pkg_index); + + auto file_indices = array_make(heap_allocator(), 0, pkg->files.count); + defer (array_free(&file_indices)); + + for_array(j, pkg->files) { + AstFile *file = pkg->files[j]; + OdinDocFile doc_file = {}; + doc_file.pkg = pkg_index; + doc_file.name = odin_doc_write_string(w, file->fullpath); + OdinDocFileIndex file_index = odin_doc_write_item(w, &w->files, &doc_file); + map_set(&w->file_cache, hash_pointer(file), file_index); + array_add(&file_indices, file_index); + } + + doc_pkg.files = odin_write_slice(w, file_indices.data, file_indices.count); + doc_pkg.entities = odin_doc_add_pkg_entities(w, pkg); + + if (dst) { + *dst = doc_pkg; + } + } + + odin_doc_update_entities(w); +} + + +void odin_doc_write_to_file(OdinDocWriter *w, char const *filename) { + gbFile f = {}; + gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, filename); + if (err != gbFileError_None) { + gb_printf_err("Failed to write .odin-doc to: %s\n", filename); + gb_exit(1); + return; + } + defer (gb_file_close(&f)); + if (gb_file_write(&f, w->data, w->data_len)) { + err = gb_file_truncate(&f, w->data_len); + gb_printf("Wrote .odin-doc file to: %s\n", filename); + } +} + +void odin_doc_write(CheckerInfo *info, char const *filename) { + OdinDocWriter w_ = {}; + OdinDocWriter *w = &w_; + defer (odin_doc_writer_destroy(w)); + w->info = info; + + odin_doc_writer_prepare(w); + odin_doc_write_docs(w); + + odin_doc_writer_start_writing(w); + odin_doc_write_docs(w); + odin_doc_writer_end_writing(w); + + odin_doc_write_to_file(w, filename); +} diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 14a1a8007..62d5a58a6 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2523,7 +2523,6 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { p->type = entity->type; p->type_expr = decl->type_expr; p->body = pl->body; - p->tags = pt->Proc.tags; p->inlining = ProcInlining_none; p->is_foreign = entity->Procedure.is_foreign; p->is_export = entity->Procedure.is_export; diff --git a/src/main.cpp b/src/main.cpp index b52f970a2..20190a187 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -607,6 +607,7 @@ enum BuildFlagKind { BuildFlag_Short, BuildFlag_AllPackages, + BuildFlag_DocFormat, BuildFlag_IgnoreWarnings, BuildFlag_WarningsAsErrors, @@ -721,6 +722,7 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_Short, str_lit("short"), BuildFlagParam_None, Command_doc); add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc); + add_flag(&build_flags, BuildFlag_DocFormat, str_lit("doc-format"), BuildFlagParam_None, Command_doc); add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all); @@ -1227,6 +1229,9 @@ bool parse_build_flags(Array args) { case BuildFlag_AllPackages: build_context.cmd_doc_flags |= CmdDocFlag_AllPackages; break; + case BuildFlag_DocFormat: + build_context.cmd_doc_flags |= CmdDocFlag_DocFormat; + break; case BuildFlag_IgnoreWarnings: if (build_context.warnings_as_errors) { gb_printf_err("-ignore-warnings cannot be used with -warnings-as-errors\n"); diff --git a/src/types.cpp b/src/types.cpp index 143b08c63..7d85aa6bb 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -195,7 +195,6 @@ struct TypeProc { Type * results; // Type_Tuple i32 param_count; i32 result_count; - u64 tags; isize specialization_count; ProcCallingConvention calling_convention; i32 variadic_index;