docs_writer.cpp 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105
  1. template <typename T>
  2. struct OdinDocWriterItemTracker {
  3. isize len;
  4. isize cap;
  5. isize offset;
  6. };
  7. enum OdinDocWriterState {
  8. OdinDocWriterState_Preparing,
  9. OdinDocWriterState_Writing,
  10. };
  11. char const* OdinDocWriterState_strings[] {
  12. "preparing",
  13. "writing ",
  14. };
  15. struct OdinDocWriter {
  16. CheckerInfo *info;
  17. OdinDocWriterState state;
  18. void *data;
  19. isize data_len;
  20. OdinDocHeader *header;
  21. StringMap<OdinDocString> string_cache;
  22. PtrMap<AstFile *, OdinDocFileIndex> file_cache;
  23. PtrMap<AstPackage *, OdinDocPkgIndex> pkg_cache;
  24. PtrMap<Entity *, OdinDocEntityIndex> entity_cache;
  25. PtrMap<Type *, OdinDocTypeIndex> type_cache;
  26. OdinDocWriterItemTracker<OdinDocFile> files;
  27. OdinDocWriterItemTracker<OdinDocPkg> pkgs;
  28. OdinDocWriterItemTracker<OdinDocEntity> entities;
  29. OdinDocWriterItemTracker<OdinDocType> types;
  30. OdinDocWriterItemTracker<u8> strings;
  31. OdinDocWriterItemTracker<u8> blob;
  32. };
  33. OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e);
  34. OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type);
  35. template <typename T>
  36. void odin_doc_writer_item_tracker_init(OdinDocWriterItemTracker<T> *t, isize size) {
  37. t->len = size;
  38. t->cap = size;
  39. }
  40. void odin_doc_writer_prepare(OdinDocWriter *w) {
  41. w->state = OdinDocWriterState_Preparing;
  42. gbAllocator a = heap_allocator();
  43. string_map_init(&w->string_cache, a);
  44. map_init(&w->file_cache, a);
  45. map_init(&w->pkg_cache, a);
  46. map_init(&w->entity_cache, a);
  47. map_init(&w->type_cache, a);
  48. odin_doc_writer_item_tracker_init(&w->files, 1);
  49. odin_doc_writer_item_tracker_init(&w->pkgs, 1);
  50. odin_doc_writer_item_tracker_init(&w->entities, 1);
  51. odin_doc_writer_item_tracker_init(&w->types, 1);
  52. odin_doc_writer_item_tracker_init(&w->strings, 16);
  53. odin_doc_writer_item_tracker_init(&w->blob, 16);
  54. }
  55. void odin_doc_writer_destroy(OdinDocWriter *w) {
  56. gb_free(heap_allocator(), w->data);
  57. string_map_destroy(&w->string_cache);
  58. map_destroy(&w->file_cache);
  59. map_destroy(&w->pkg_cache);
  60. map_destroy(&w->entity_cache);
  61. map_destroy(&w->type_cache);
  62. }
  63. template <typename T>
  64. void odin_doc_writer_tracker_size(isize *offset, OdinDocWriterItemTracker<T> *t, isize alignment=1) {
  65. isize size = t->cap*gb_size_of(T);
  66. isize align = gb_max(gb_align_of(T), alignment);
  67. *offset = align_formula_isize(*offset, align);
  68. t->offset = *offset;
  69. *offset += size;
  70. }
  71. isize odin_doc_writer_calc_total_size(OdinDocWriter *w) {
  72. isize total_size = gb_size_of(OdinDocHeader);
  73. odin_doc_writer_tracker_size(&total_size, &w->files);
  74. odin_doc_writer_tracker_size(&total_size, &w->pkgs);
  75. odin_doc_writer_tracker_size(&total_size, &w->entities);
  76. odin_doc_writer_tracker_size(&total_size, &w->types);
  77. odin_doc_writer_tracker_size(&total_size, &w->strings, 16);
  78. odin_doc_writer_tracker_size(&total_size, &w->blob, 16);
  79. return total_size;
  80. }
  81. void odin_doc_writer_start_writing(OdinDocWriter *w) {
  82. w->state = OdinDocWriterState_Writing;
  83. string_map_clear(&w->string_cache);
  84. map_clear(&w->file_cache);
  85. map_clear(&w->pkg_cache);
  86. map_clear(&w->entity_cache);
  87. map_clear(&w->type_cache);
  88. isize total_size = odin_doc_writer_calc_total_size(w);
  89. total_size = align_formula_isize(total_size, 8);
  90. w->data = gb_alloc_align(heap_allocator(), total_size, 8);
  91. w->data_len = total_size;
  92. w->header = cast(OdinDocHeader *)w->data;
  93. }
  94. u32 hash_data_after_header(OdinDocHeaderBase *base, void *data, isize data_len) {
  95. u8 *start = cast(u8 *)data;
  96. u8 *end = start + base->total_size;
  97. start += base->header_size;
  98. u32 h = 0x811c9dc5;
  99. for (u8 *b = start; b != end; b++) {
  100. h = (h ^ cast(u32)*b) * 0x01000193;
  101. }
  102. return h;
  103. }
  104. template <typename T>
  105. void odin_doc_writer_assign_tracker(OdinDocArray<T> *array, OdinDocWriterItemTracker<T> const &t) {
  106. array->offset = cast(u32)t.offset;
  107. array->length = cast(u32)t.len;
  108. }
  109. void odin_doc_writer_end_writing(OdinDocWriter *w) {
  110. OdinDocHeader *h = w->header;
  111. gb_memmove(h->base.magic, OdinDocHeader_MagicString, gb_strlen(OdinDocHeader_MagicString));
  112. h->base.version.major = OdinDocVersionType_Major;
  113. h->base.version.minor = OdinDocVersionType_Minor;
  114. h->base.version.patch = OdinDocVersionType_Patch;
  115. h->base.total_size = cast(u32)w->data_len;
  116. h->base.header_size = gb_size_of(*h);
  117. h->base.hash = hash_data_after_header(&h->base, w->data, w->data_len);
  118. odin_doc_writer_assign_tracker(&h->files, w->files);
  119. odin_doc_writer_assign_tracker(&h->pkgs, w->pkgs);
  120. odin_doc_writer_assign_tracker(&h->entities, w->entities);
  121. odin_doc_writer_assign_tracker(&h->types, w->types);
  122. }
  123. template <typename T>
  124. u32 odin_doc_write_item(OdinDocWriter *w, OdinDocWriterItemTracker<T> *t, T const *item, T **dst=nullptr) {
  125. if (w->state == OdinDocWriterState_Preparing) {
  126. t->cap += 1;
  127. if (dst) *dst = nullptr;
  128. return 0;
  129. } else {
  130. GB_ASSERT_MSG(t->len < t->cap, "%td < %td", t->len, t->cap);
  131. isize item_index = t->len++;
  132. uintptr data = cast(uintptr)w->data + cast(uintptr)(t->offset + gb_size_of(T)*item_index);
  133. if (item) {
  134. gb_memmove(cast(T *)data, item, gb_size_of(T));
  135. }
  136. if (dst) *dst = cast(T *)data;
  137. return cast(u32)item_index;
  138. }
  139. }
  140. template <typename T>
  141. T *odin_doc_get_item(OdinDocWriter *w, OdinDocWriterItemTracker<T> *t, u32 index) {
  142. if (w->state != OdinDocWriterState_Writing) {
  143. return nullptr;
  144. }
  145. GB_ASSERT(index < cast(u32)t->len);
  146. uintptr data = cast(uintptr)w->data + cast(uintptr)(t->offset + gb_size_of(T)*index);
  147. return cast(T *)data;
  148. }
  149. OdinDocString odin_doc_write_string_without_cache(OdinDocWriter *w, String const &str) {
  150. OdinDocString res = {};
  151. if (w->state == OdinDocWriterState_Preparing) {
  152. w->strings.cap += str.len+1;
  153. } else {
  154. GB_ASSERT_MSG(w->strings.len+str.len+1 <= w->strings.cap, "%td <= %td", w->strings.len+str.len, w->strings.cap);
  155. isize offset = w->strings.offset + w->strings.len;
  156. u8 *data = cast(u8 *)w->data + offset;
  157. gb_memmove(data, str.text, str.len);
  158. data[str.len] = 0;
  159. w->strings.len += str.len+1;
  160. res.offset = cast(u32)offset;
  161. res.length = cast(u32)str.len;
  162. }
  163. return res;
  164. }
  165. OdinDocString odin_doc_write_string(OdinDocWriter *w, String const &str) {
  166. OdinDocString *c = string_map_get(&w->string_cache, str);
  167. if (c != nullptr) {
  168. if (w->state == OdinDocWriterState_Writing) {
  169. GB_ASSERT(from_string(&w->header->base, *c) == str);
  170. }
  171. return *c;
  172. }
  173. OdinDocString res = odin_doc_write_string_without_cache(w, str);
  174. string_map_set(&w->string_cache, str, res);
  175. return res;
  176. }
  177. template <typename T>
  178. OdinDocArray<T> odin_write_slice(OdinDocWriter *w, T *data, isize len) {
  179. GB_ASSERT(gb_align_of(T) <= 4);
  180. if (len <= 0) {
  181. return {0, 0};
  182. }
  183. isize alignment = 4;
  184. if (w->state == OdinDocWriterState_Preparing) {
  185. w->blob.cap = align_formula_isize(w->blob.cap, alignment);
  186. w->blob.cap += len * gb_size_of(T);
  187. return {0, 0};
  188. }
  189. w->blob.len = align_formula_isize(w->blob.len, alignment);
  190. isize offset = w->blob.offset + w->blob.len;
  191. u8 *dst = cast(u8 *)w->data + offset;
  192. gb_memmove(dst, data, len*gb_size_of(T));
  193. w->blob.len += len * gb_size_of(T);
  194. return {cast(u32)offset, cast(u32)len};
  195. }
  196. template <typename T>
  197. OdinDocArray<T> odin_write_item_as_slice(OdinDocWriter *w, T data) {
  198. return odin_write_slice(w, &data, 1);
  199. }
  200. OdinDocPosition odin_doc_token_pos_cast(OdinDocWriter *w, TokenPos const &pos) {
  201. OdinDocFileIndex file_index = 0;
  202. if (pos.file_id != 0) {
  203. AstFile *file = global_files[pos.file_id];
  204. if (file != nullptr) {
  205. OdinDocFileIndex *file_index_found = map_get(&w->file_cache, file);
  206. GB_ASSERT(file_index_found != nullptr);
  207. file_index = *file_index_found;
  208. }
  209. }
  210. OdinDocPosition doc_pos = {};
  211. doc_pos.file = file_index;
  212. doc_pos.line = cast(u32)pos.line;
  213. doc_pos.column = cast(u32)pos.column;
  214. doc_pos.offset = cast(u32)pos.offset;
  215. return doc_pos;
  216. }
  217. bool odin_doc_append_comment_group_string(Array<u8> *buf, CommentGroup *g) {
  218. if (g == nullptr) {
  219. return false;
  220. }
  221. isize len = 0;
  222. for_array(i, g->list) {
  223. String comment = g->list[i].string;
  224. len += comment.len;
  225. len += 1; // for \n
  226. }
  227. if (len <= g->list.count) {
  228. return false;
  229. }
  230. isize count = 0;
  231. for_array(i, g->list) {
  232. String comment = g->list[i].string;
  233. String original_comment = comment;
  234. bool slash_slash = comment[1] == '/';
  235. if (comment[1] == '/') {
  236. comment.text += 2;
  237. comment.len -= 2;
  238. } else if (comment[1] == '*') {
  239. comment.text += 2;
  240. comment.len -= 4;
  241. }
  242. // Ignore the first space
  243. if (comment.len > 0 && comment[0] == ' ') {
  244. comment.text += 1;
  245. comment.len -= 1;
  246. }
  247. if (slash_slash) {
  248. if (string_starts_with(comment, str_lit("+"))) {
  249. continue;
  250. }
  251. if (string_starts_with(comment, str_lit("@("))) {
  252. continue;
  253. }
  254. }
  255. if (slash_slash) {
  256. array_add_elems(buf, comment.text, comment.len);
  257. array_add(buf, cast(u8)'\n');
  258. count += 1;
  259. } else {
  260. isize pos = 0;
  261. for (; pos < comment.len; pos++) {
  262. isize end = pos;
  263. for (; end < comment.len; end++) {
  264. if (comment[end] == '\n') {
  265. break;
  266. }
  267. }
  268. String line = substring(comment, pos, end);
  269. pos = end+1;
  270. String trimmed_line = string_trim_whitespace(line);
  271. if (trimmed_line.len == 0) {
  272. if (count == 0) {
  273. continue;
  274. }
  275. }
  276. /*
  277. * Remove comments with
  278. * styles
  279. * like this
  280. */
  281. if (string_starts_with(line, str_lit("* "))) {
  282. line = substring(line, 2, line.len);
  283. }
  284. array_add_elems(buf, line.text, line.len);
  285. array_add(buf, cast(u8)'\n');
  286. count += 1;
  287. }
  288. }
  289. }
  290. if (count > 0) {
  291. array_add(buf, cast(u8)'\n');
  292. return true;
  293. }
  294. return false;
  295. }
  296. OdinDocString odin_doc_pkg_doc_string(OdinDocWriter *w, AstPackage *pkg) {
  297. if (pkg == nullptr) {
  298. return {};
  299. }
  300. auto buf = array_make<u8>(permanent_allocator(), 0, 0); // Minor leak
  301. for_array(i, pkg->files) {
  302. AstFile *f = pkg->files[i];
  303. if (f->pkg_decl) {
  304. GB_ASSERT(f->pkg_decl->kind == Ast_PackageDecl);
  305. odin_doc_append_comment_group_string(&buf, f->pkg_decl->PackageDecl.docs);
  306. }
  307. }
  308. return odin_doc_write_string_without_cache(w, make_string(buf.data, buf.count));
  309. }
  310. OdinDocString odin_doc_comment_group_string(OdinDocWriter *w, CommentGroup *g) {
  311. if (g == nullptr) {
  312. return {};
  313. }
  314. auto buf = array_make<u8>(permanent_allocator(), 0, 0); // Minor leak
  315. odin_doc_append_comment_group_string(&buf, g);
  316. return odin_doc_write_string_without_cache(w, make_string(buf.data, buf.count));
  317. }
  318. OdinDocString odin_doc_expr_string(OdinDocWriter *w, Ast *expr) {
  319. if (expr == nullptr) {
  320. return {};
  321. }
  322. gbString s = write_expr_to_string( // Minor leak
  323. gb_string_make(permanent_allocator(), ""),
  324. expr,
  325. build_context.cmd_doc_flags & CmdDocFlag_Short
  326. );
  327. return odin_doc_write_string(w, make_string(cast(u8 *)s, gb_string_length(s)));
  328. }
  329. OdinDocArray<OdinDocAttribute> odin_doc_attributes(OdinDocWriter *w, Array<Ast *> const &attributes) {
  330. isize count = 0;
  331. for_array(i, attributes) {
  332. Ast *attr = attributes[i];
  333. if (attr->kind != Ast_Attribute) continue;
  334. count += attr->Attribute.elems.count;
  335. };
  336. auto attribs = array_make<OdinDocAttribute>(heap_allocator(), 0, count);
  337. defer (array_free(&attribs));
  338. for_array(i, attributes) {
  339. Ast *attr = attributes[i];
  340. if (attr->kind != Ast_Attribute) continue;
  341. for_array(j, attr->Attribute.elems) {
  342. Ast *elem = attr->Attribute.elems[j];
  343. String name = {};
  344. Ast *value = nullptr;
  345. switch (elem->kind) {
  346. case_ast_node(i, Ident, elem);
  347. name = i->token.string;
  348. case_end;
  349. case_ast_node(i, Implicit, elem);
  350. name = i->string;
  351. case_end;
  352. case_ast_node(fv, FieldValue, elem);
  353. if (fv->field->kind == Ast_Ident) {
  354. name = fv->field->Ident.token.string;
  355. } else if (fv->field->kind == Ast_Implicit) {
  356. name = fv->field->Implicit.string;
  357. }
  358. value = fv->value;
  359. case_end;
  360. default:
  361. continue;
  362. }
  363. OdinDocAttribute doc_attrib = {};
  364. doc_attrib.name = odin_doc_write_string(w, name);
  365. doc_attrib.value = odin_doc_expr_string(w, value);
  366. array_add(&attribs, doc_attrib);
  367. }
  368. }
  369. return odin_write_slice(w, attribs.data, attribs.count);
  370. }
  371. OdinDocArray<OdinDocString> odin_doc_where_clauses(OdinDocWriter *w, Slice<Ast *> const &where_clauses) {
  372. if (where_clauses.count == 0) {
  373. return {};
  374. }
  375. auto clauses = array_make<OdinDocString>(heap_allocator(), where_clauses.count);
  376. defer (array_free(&clauses));
  377. for_array(i, where_clauses) {
  378. clauses[i] = odin_doc_expr_string(w, where_clauses[i]);
  379. }
  380. return odin_write_slice(w, clauses.data, clauses.count);
  381. }
  382. OdinDocArray<OdinDocTypeIndex> odin_doc_type_as_slice(OdinDocWriter *w, Type *type) {
  383. OdinDocTypeIndex index = odin_doc_type(w, type);
  384. return odin_write_item_as_slice(w, index);
  385. }
  386. OdinDocArray<OdinDocEntityIndex> odin_doc_add_entity_as_slice(OdinDocWriter *w, Entity *e) {
  387. OdinDocEntityIndex index = odin_doc_add_entity(w, e);
  388. return odin_write_item_as_slice(w, index);
  389. }
  390. OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
  391. if (type == nullptr) {
  392. return 0;
  393. }
  394. OdinDocTypeIndex *found = map_get(&w->type_cache, type);
  395. if (found) {
  396. return *found;
  397. }
  398. for_array(i, w->type_cache.entries) {
  399. // NOTE(bill): THIS IS SLOW
  400. Type *other = w->type_cache.entries[i].key;
  401. if (are_types_identical(type, other)) {
  402. OdinDocTypeIndex index = w->type_cache.entries[i].value;
  403. map_set(&w->type_cache, type, index);
  404. return index;
  405. }
  406. }
  407. OdinDocType *dst = nullptr;
  408. OdinDocType doc_type = {};
  409. OdinDocTypeIndex type_index = 0;
  410. type_index = odin_doc_write_item(w, &w->types, &doc_type, &dst);
  411. map_set(&w->type_cache, type, type_index);
  412. switch (type->kind) {
  413. case Type_Basic:
  414. doc_type.kind = OdinDocType_Basic;
  415. doc_type.name = odin_doc_write_string(w, type->Basic.name);
  416. if (is_type_untyped(type)) {
  417. doc_type.flags |= OdinDocTypeFlag_Basic_untyped;
  418. }
  419. break;
  420. case Type_Named:
  421. doc_type.kind = OdinDocType_Named;
  422. doc_type.name = odin_doc_write_string(w, type->Named.name);
  423. doc_type.types = odin_doc_type_as_slice(w, base_type(type));
  424. doc_type.entities = odin_doc_add_entity_as_slice(w, type->Named.type_name);
  425. break;
  426. case Type_Generic:
  427. doc_type.kind = OdinDocType_Generic;
  428. doc_type.name = odin_doc_write_string(w, type->Generic.name);
  429. if (type->Generic.specialized) {
  430. doc_type.types = odin_doc_type_as_slice(w, type->Generic.specialized);
  431. }
  432. break;
  433. case Type_Pointer:
  434. doc_type.kind = OdinDocType_Pointer;
  435. doc_type.types = odin_doc_type_as_slice(w, type->Pointer.elem);
  436. break;
  437. case Type_MultiPointer:
  438. doc_type.kind = OdinDocType_MultiPointer;
  439. doc_type.types = odin_doc_type_as_slice(w, type->MultiPointer.elem);
  440. break;
  441. case Type_Array:
  442. doc_type.kind = OdinDocType_Array;
  443. doc_type.elem_count_len = 1;
  444. doc_type.elem_counts[0] = type->Array.count;
  445. doc_type.types = odin_doc_type_as_slice(w, type->Array.elem);
  446. break;
  447. case Type_EnumeratedArray:
  448. doc_type.kind = OdinDocType_EnumeratedArray;
  449. doc_type.elem_count_len = 1;
  450. doc_type.elem_counts[0] = type->EnumeratedArray.count;
  451. {
  452. OdinDocTypeIndex types[2] = {};
  453. types[0] = odin_doc_type(w, type->EnumeratedArray.index);
  454. types[1] = odin_doc_type(w, type->EnumeratedArray.elem);
  455. doc_type.types = odin_write_slice(w, types, gb_count_of(types));
  456. }
  457. break;
  458. case Type_Slice:
  459. doc_type.kind = OdinDocType_Slice;
  460. doc_type.types = odin_doc_type_as_slice(w, type->Slice.elem);
  461. break;
  462. case Type_DynamicArray:
  463. doc_type.kind = OdinDocType_DynamicArray;
  464. doc_type.types = odin_doc_type_as_slice(w, type->DynamicArray.elem);
  465. break;
  466. case Type_Map:
  467. doc_type.kind = OdinDocType_Map;
  468. {
  469. OdinDocTypeIndex types[2] = {};
  470. types[0] = odin_doc_type(w, type->Map.key);
  471. types[1] = odin_doc_type(w, type->Map.value);
  472. doc_type.types = odin_write_slice(w, types, gb_count_of(types));
  473. }
  474. break;
  475. case Type_Struct:
  476. doc_type.kind = OdinDocType_Struct;
  477. if (type->Struct.soa_kind != StructSoa_None) {
  478. switch (type->Struct.soa_kind) {
  479. case StructSoa_Fixed:
  480. doc_type.kind = OdinDocType_SOAStructFixed;
  481. doc_type.elem_count_len = 1;
  482. doc_type.elem_counts[0] = type->Struct.soa_count;
  483. break;
  484. case StructSoa_Slice:
  485. doc_type.kind = OdinDocType_SOAStructSlice;
  486. break;
  487. case StructSoa_Dynamic:
  488. doc_type.kind = OdinDocType_SOAStructDynamic;
  489. break;
  490. }
  491. doc_type.types = odin_doc_type_as_slice(w, type->Struct.soa_elem);
  492. } else {
  493. if (type->Struct.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Struct_polymorphic; }
  494. if (type->Struct.is_packed) { doc_type.flags |= OdinDocTypeFlag_Struct_packed; }
  495. if (type->Struct.is_raw_union) { doc_type.flags |= OdinDocTypeFlag_Struct_raw_union; }
  496. auto fields = array_make<OdinDocEntityIndex>(heap_allocator(), type->Struct.fields.count);
  497. defer (array_free(&fields));
  498. for_array(i, type->Struct.fields) {
  499. fields[i] = odin_doc_add_entity(w, type->Struct.fields[i]);
  500. }
  501. doc_type.entities = odin_write_slice(w, fields.data, fields.count);
  502. doc_type.polmorphic_params = odin_doc_type(w, type->Struct.polymorphic_params);
  503. if (type->Struct.node) {
  504. ast_node(st, StructType, type->Struct.node);
  505. if (st->align) {
  506. doc_type.custom_align = odin_doc_expr_string(w, st->align);
  507. }
  508. doc_type.where_clauses = odin_doc_where_clauses(w, st->where_clauses);
  509. }
  510. }
  511. break;
  512. case Type_Union:
  513. doc_type.kind = OdinDocType_Union;
  514. if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; }
  515. if (type->Union.no_nil) { doc_type.flags |= OdinDocTypeFlag_Union_no_nil; }
  516. if (type->Union.maybe) { doc_type.flags |= OdinDocTypeFlag_Union_maybe; }
  517. {
  518. auto variants = array_make<OdinDocTypeIndex>(heap_allocator(), type->Union.variants.count);
  519. defer (array_free(&variants));
  520. for_array(i, type->Union.variants) {
  521. variants[i] = odin_doc_type(w, type->Union.variants[i]);
  522. }
  523. doc_type.types = odin_write_slice(w, variants.data, variants.count);
  524. doc_type.polmorphic_params = odin_doc_type(w, type->Union.polymorphic_params);
  525. }
  526. if (type->Union.node) {
  527. ast_node(ut, UnionType, type->Union.node);
  528. if (ut->align) {
  529. doc_type.custom_align = odin_doc_expr_string(w, ut->align);
  530. }
  531. doc_type.where_clauses = odin_doc_where_clauses(w, ut->where_clauses);
  532. }
  533. break;
  534. case Type_Enum:
  535. doc_type.kind = OdinDocType_Enum;
  536. {
  537. auto fields = array_make<OdinDocEntityIndex>(heap_allocator(), type->Enum.fields.count);
  538. defer (array_free(&fields));
  539. for_array(i, type->Enum.fields) {
  540. fields[i] = odin_doc_add_entity(w, type->Enum.fields[i]);
  541. }
  542. doc_type.entities = odin_write_slice(w, fields.data, fields.count);
  543. if (type->Enum.base_type) {
  544. doc_type.types = odin_doc_type_as_slice(w, type->Enum.base_type);
  545. }
  546. }
  547. break;
  548. case Type_Tuple:
  549. doc_type.kind = OdinDocType_Tuple;
  550. {
  551. auto variables = array_make<OdinDocEntityIndex>(heap_allocator(), type->Tuple.variables.count);
  552. defer (array_free(&variables));
  553. for_array(i, type->Tuple.variables) {
  554. variables[i] = odin_doc_add_entity(w, type->Tuple.variables[i]);
  555. }
  556. doc_type.entities = odin_write_slice(w, variables.data, variables.count);
  557. }
  558. break;
  559. case Type_Proc:
  560. doc_type.kind = OdinDocType_Proc;
  561. if (type->Proc.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Proc_polymorphic; }
  562. if (type->Proc.diverging) { doc_type.flags |= OdinDocTypeFlag_Proc_diverging; }
  563. if (type->Proc.optional_ok) { doc_type.flags |= OdinDocTypeFlag_Proc_optional_ok; }
  564. if (type->Proc.variadic) { doc_type.flags |= OdinDocTypeFlag_Proc_variadic; }
  565. if (type->Proc.c_vararg) { doc_type.flags |= OdinDocTypeFlag_Proc_c_vararg; }
  566. {
  567. OdinDocTypeIndex types[2];
  568. types[0] = odin_doc_type(w, type->Proc.params);
  569. types[1] = odin_doc_type(w, type->Proc.results);
  570. doc_type.types = odin_write_slice(w, types, gb_count_of(types));
  571. String calling_convention = {};
  572. switch (type->Proc.calling_convention) {
  573. case ProcCC_Invalid:
  574. // no need
  575. break;
  576. case ProcCC_Odin:
  577. if (default_calling_convention() != ProcCC_Odin) {
  578. calling_convention = str_lit("odin");
  579. }
  580. break;
  581. case ProcCC_Contextless:
  582. if (default_calling_convention() != ProcCC_Contextless) {
  583. calling_convention = str_lit("contextless");
  584. }
  585. break;
  586. case ProcCC_CDecl:
  587. calling_convention = str_lit("cdecl");
  588. break;
  589. case ProcCC_StdCall:
  590. calling_convention = str_lit("stdcall");
  591. break;
  592. case ProcCC_FastCall:
  593. calling_convention = str_lit("fastcall");
  594. break;
  595. case ProcCC_None:
  596. calling_convention = str_lit("none");
  597. break;
  598. case ProcCC_Naked:
  599. calling_convention = str_lit("naked");
  600. break;
  601. case ProcCC_InlineAsm:
  602. calling_convention = str_lit("inline-assembly");
  603. break;
  604. }
  605. doc_type.calling_convention = odin_doc_write_string(w, calling_convention);
  606. }
  607. break;
  608. case Type_BitSet:
  609. doc_type.kind = OdinDocType_BitSet;
  610. {
  611. isize type_count = 0;
  612. OdinDocTypeIndex types[2] = {};
  613. if (type->BitSet.elem) {
  614. types[type_count++] = odin_doc_type(w, type->BitSet.elem);
  615. }
  616. if (type->BitSet.underlying) {
  617. types[type_count++] = odin_doc_type(w, type->BitSet.underlying);
  618. doc_type.flags |= OdinDocTypeFlag_BitSet_UnderlyingType;
  619. }
  620. doc_type.types = odin_write_slice(w, types, type_count);
  621. doc_type.elem_count_len = 2;
  622. doc_type.elem_counts[0] = type->BitSet.lower;
  623. doc_type.elem_counts[1] = type->BitSet.upper;
  624. }
  625. break;
  626. case Type_SimdVector:
  627. doc_type.kind = OdinDocType_SimdVector;
  628. doc_type.elem_count_len = 1;
  629. doc_type.elem_counts[0] = type->SimdVector.count;
  630. doc_type.types = odin_doc_type_as_slice(w, type->SimdVector.elem);
  631. // TODO(bill):
  632. break;
  633. case Type_RelativePointer:
  634. doc_type.kind = OdinDocType_RelativePointer;
  635. {
  636. OdinDocTypeIndex types[2] = {};
  637. types[0] = odin_doc_type(w, type->RelativePointer.pointer_type);
  638. types[1] = odin_doc_type(w, type->RelativePointer.base_integer);
  639. doc_type.types = odin_write_slice(w, types, gb_count_of(types));
  640. }
  641. break;
  642. case Type_RelativeSlice:
  643. doc_type.kind = OdinDocType_RelativeSlice;
  644. {
  645. OdinDocTypeIndex types[2] = {};
  646. types[0] = odin_doc_type(w, type->RelativeSlice.slice_type);
  647. types[1] = odin_doc_type(w, type->RelativeSlice.base_integer);
  648. doc_type.types = odin_write_slice(w, types, gb_count_of(types));
  649. }
  650. break;
  651. case Type_Matrix:
  652. doc_type.kind = OdinDocType_Matrix;
  653. doc_type.elem_count_len = 2;
  654. doc_type.elem_counts[0] = type->Matrix.row_count;
  655. doc_type.elem_counts[1] = type->Matrix.column_count;
  656. doc_type.types = odin_doc_type_as_slice(w, type->Matrix.elem);
  657. break;
  658. }
  659. if (dst) {
  660. *dst = doc_type;
  661. }
  662. return type_index;
  663. }
  664. OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
  665. if (e == nullptr) {
  666. return 0;
  667. }
  668. OdinDocEntityIndex *prev_index = map_get(&w->entity_cache, e);
  669. if (prev_index) {
  670. return *prev_index;
  671. }
  672. if (e->pkg != nullptr && map_get(&w->pkg_cache, e->pkg) == nullptr) {
  673. return 0;
  674. }
  675. OdinDocEntity doc_entity = {};
  676. OdinDocEntity* dst = nullptr;
  677. OdinDocEntityIndex doc_entity_index = odin_doc_write_item(w, &w->entities, &doc_entity, &dst);
  678. map_set(&w->entity_cache, e, doc_entity_index);
  679. Ast *type_expr = nullptr;
  680. Ast *init_expr = nullptr;
  681. Ast *decl_node = nullptr;
  682. CommentGroup *comment = nullptr;
  683. CommentGroup *docs = nullptr;
  684. if (e->decl_info != nullptr) {
  685. type_expr = e->decl_info->type_expr;
  686. init_expr = e->decl_info->init_expr;
  687. decl_node = e->decl_info->decl_node;
  688. comment = e->decl_info->comment;
  689. docs = e->decl_info->docs;
  690. }
  691. String link_name = {};
  692. OdinDocEntityKind kind = OdinDocEntity_Invalid;
  693. u32 flags = 0;
  694. switch (e->kind) {
  695. case Entity_Invalid: kind = OdinDocEntity_Invalid; break;
  696. case Entity_Constant: kind = OdinDocEntity_Constant; break;
  697. case Entity_Variable: kind = OdinDocEntity_Variable; break;
  698. case Entity_TypeName: kind = OdinDocEntity_TypeName; break;
  699. case Entity_Procedure: kind = OdinDocEntity_Procedure; break;
  700. case Entity_ProcGroup: kind = OdinDocEntity_ProcGroup; break;
  701. case Entity_ImportName: kind = OdinDocEntity_ImportName; break;
  702. case Entity_LibraryName: kind = OdinDocEntity_LibraryName; break;
  703. }
  704. switch (e->kind) {
  705. case Entity_TypeName:
  706. if (e->TypeName.is_type_alias) {
  707. flags |= OdinDocEntityFlag_Type_Alias;
  708. }
  709. break;
  710. case Entity_Variable:
  711. if (e->Variable.is_foreign) { flags |= OdinDocEntityFlag_Foreign; }
  712. if (e->Variable.is_export) { flags |= OdinDocEntityFlag_Export; }
  713. if (e->Variable.thread_local_model != "") {
  714. flags |= OdinDocEntityFlag_Var_Thread_Local;
  715. }
  716. if (e->flags & EntityFlag_Static) { flags |= OdinDocEntityFlag_Var_Static; }
  717. link_name = e->Variable.link_name;
  718. break;
  719. case Entity_Procedure:
  720. if (e->Procedure.is_foreign) { flags |= OdinDocEntityFlag_Foreign; }
  721. if (e->Procedure.is_export) { flags |= OdinDocEntityFlag_Export; }
  722. link_name = e->Procedure.link_name;
  723. break;
  724. }
  725. if (e->flags & EntityFlag_Param) {
  726. if (e->flags & EntityFlag_Using) { flags |= OdinDocEntityFlag_Param_Using; }
  727. if (e->flags & EntityFlag_ConstInput) { flags |= OdinDocEntityFlag_Param_Const; }
  728. if (e->flags & EntityFlag_AutoCast) { flags |= OdinDocEntityFlag_Param_AutoCast; }
  729. if (e->flags & EntityFlag_Ellipsis) { flags |= OdinDocEntityFlag_Param_Ellipsis; }
  730. if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; }
  731. if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; }
  732. }
  733. OdinDocString init_string = {};
  734. if (init_expr) {
  735. init_string = odin_doc_expr_string(w, init_expr);
  736. } else {
  737. if (e->kind == Entity_Constant) {
  738. if (e->Constant.flags & EntityConstantFlag_ImplicitEnumValue) {
  739. init_string = {}; // Blank
  740. } else if (e->Constant.param_value.original_ast_expr) {
  741. init_string = odin_doc_expr_string(w, e->Constant.param_value.original_ast_expr);
  742. } else {
  743. init_string = odin_doc_write_string(w, make_string_c(exact_value_to_string(e->Constant.value)));
  744. }
  745. } else if (e->kind == Entity_Variable) {
  746. if (e->Variable.param_expr) {
  747. init_string = odin_doc_expr_string(w, e->Variable.param_expr);
  748. }
  749. }
  750. }
  751. doc_entity.kind = kind;
  752. doc_entity.flags = flags;
  753. doc_entity.pos = odin_doc_token_pos_cast(w, e->token.pos);
  754. doc_entity.name = odin_doc_write_string(w, e->token.string);
  755. doc_entity.type = 0; // Set later
  756. doc_entity.init_string = init_string;
  757. doc_entity.comment = odin_doc_comment_group_string(w, comment);
  758. doc_entity.docs = odin_doc_comment_group_string(w, docs);
  759. doc_entity.foreign_library = 0; // Set later
  760. doc_entity.link_name = odin_doc_write_string(w, link_name);
  761. if (e->decl_info != nullptr) {
  762. doc_entity.attributes = odin_doc_attributes(w, e->decl_info->attributes);
  763. }
  764. doc_entity.grouped_entities = {}; // Set later
  765. if (dst) {
  766. *dst = doc_entity;
  767. }
  768. return doc_entity_index;
  769. }
  770. void odin_doc_update_entities(OdinDocWriter *w) {
  771. {
  772. // NOTE(bill): Double pass, just in case entities are created on odin_doc_type
  773. auto entities = array_make<Entity *>(heap_allocator(), w->entity_cache.entries.count);
  774. defer (array_free(&entities));
  775. for_array(i, w->entity_cache.entries) {
  776. Entity *e = w->entity_cache.entries[i].key;
  777. entities[i] = e;
  778. }
  779. for_array(i, entities) {
  780. Entity *e = entities[i];
  781. OdinDocTypeIndex type_index = odin_doc_type(w, e->type);
  782. gb_unused(type_index);
  783. }
  784. }
  785. for_array(i, w->entity_cache.entries) {
  786. Entity *e = w->entity_cache.entries[i].key;
  787. OdinDocEntityIndex entity_index = w->entity_cache.entries[i].value;
  788. OdinDocTypeIndex type_index = odin_doc_type(w, e->type);
  789. OdinDocEntityIndex foreign_library = 0;
  790. OdinDocArray<OdinDocEntityIndex> grouped_entities = {};
  791. switch (e->kind) {
  792. case Entity_Variable:
  793. foreign_library = odin_doc_add_entity(w, e->Variable.foreign_library);
  794. break;
  795. case Entity_Procedure:
  796. foreign_library = odin_doc_add_entity(w, e->Procedure.foreign_library);
  797. break;
  798. case Entity_ProcGroup:
  799. {
  800. auto pges = array_make<OdinDocEntityIndex>(heap_allocator(), 0, e->ProcGroup.entities.count);
  801. defer (array_free(&pges));
  802. for_array(j, e->ProcGroup.entities) {
  803. OdinDocEntityIndex index = odin_doc_add_entity(w, e->ProcGroup.entities[j]);
  804. array_add(&pges, index);
  805. }
  806. grouped_entities = odin_write_slice(w, pges.data, pges.count);
  807. }
  808. break;
  809. }
  810. OdinDocEntity *dst = odin_doc_get_item(w, &w->entities, entity_index);
  811. if (dst) {
  812. dst->type = type_index;
  813. dst->foreign_library = foreign_library;
  814. dst->grouped_entities = grouped_entities;
  815. }
  816. }
  817. }
  818. OdinDocArray<OdinDocEntityIndex> odin_doc_add_pkg_entities(OdinDocWriter *w, AstPackage *pkg) {
  819. if (pkg->scope == nullptr) {
  820. return {};
  821. }
  822. if (map_get(&w->pkg_cache, pkg) == nullptr) {
  823. return {};
  824. }
  825. auto entities = array_make<Entity *>(heap_allocator(), 0, pkg->scope->elements.entries.count);
  826. defer (array_free(&entities));
  827. for_array(i, pkg->scope->elements.entries) {
  828. Entity *e = pkg->scope->elements.entries[i].value;
  829. switch (e->kind) {
  830. case Entity_Invalid:
  831. case Entity_Builtin:
  832. case Entity_Nil:
  833. case Entity_Label:
  834. continue;
  835. case Entity_Constant:
  836. case Entity_Variable:
  837. case Entity_TypeName:
  838. case Entity_Procedure:
  839. case Entity_ProcGroup:
  840. case Entity_ImportName:
  841. case Entity_LibraryName:
  842. // Fine
  843. break;
  844. }
  845. array_add(&entities, e);
  846. }
  847. gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
  848. auto entity_indices = array_make<OdinDocEntityIndex>(heap_allocator(), 0, w->entity_cache.entries.count);
  849. defer (array_free(&entity_indices));
  850. for_array(i, entities) {
  851. Entity *e = entities[i];
  852. if (e->pkg != pkg) {
  853. continue;
  854. }
  855. if (!is_entity_exported(e)) {
  856. continue;
  857. }
  858. if (e->token.string.len == 0) {
  859. continue;
  860. }
  861. OdinDocEntityIndex doc_entity_index = 0;
  862. doc_entity_index = odin_doc_add_entity(w, e);
  863. array_add(&entity_indices, doc_entity_index);
  864. }
  865. return odin_write_slice(w, entity_indices.data, entity_indices.count);
  866. }
  867. void odin_doc_write_docs(OdinDocWriter *w) {
  868. auto pkgs = array_make<AstPackage *>(heap_allocator(), 0, w->info->packages.entries.count);
  869. defer (array_free(&pkgs));
  870. for_array(i, w->info->packages.entries) {
  871. AstPackage *pkg = w->info->packages.entries[i].value;
  872. if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) {
  873. array_add(&pkgs, pkg);
  874. } else {
  875. if (pkg->kind == Package_Init) {
  876. array_add(&pkgs, pkg);
  877. } else if (pkg->is_extra) {
  878. array_add(&pkgs, pkg);
  879. }
  880. }
  881. }
  882. gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name);
  883. for_array(i, pkgs) {
  884. gbAllocator allocator = heap_allocator();
  885. AstPackage *pkg = pkgs[i];
  886. u32 pkg_flags = 0;
  887. switch (pkg->kind) {
  888. case Package_Normal:
  889. break;
  890. case Package_Runtime:
  891. pkg_flags |= OdinDocPkgFlag_Runtime;
  892. break;
  893. case Package_Init:
  894. pkg_flags |= OdinDocPkgFlag_Init;
  895. break;
  896. }
  897. if (pkg->name == "builtin") {
  898. pkg_flags |= OdinDocPkgFlag_Builtin;
  899. } else if (pkg->name == "intrinsics") {
  900. pkg_flags |= OdinDocPkgFlag_Builtin;
  901. }
  902. OdinDocPkg doc_pkg = {};
  903. doc_pkg.fullpath = odin_doc_write_string(w, pkg->fullpath);
  904. doc_pkg.name = odin_doc_write_string(w, pkg->name);
  905. doc_pkg.flags = pkg_flags;
  906. doc_pkg.docs = odin_doc_pkg_doc_string(w, pkg);
  907. OdinDocPkg *dst = nullptr;
  908. OdinDocPkgIndex pkg_index = odin_doc_write_item(w, &w->pkgs, &doc_pkg, &dst);
  909. map_set(&w->pkg_cache, pkg, pkg_index);
  910. auto file_indices = array_make<OdinDocFileIndex>(heap_allocator(), 0, pkg->files.count);
  911. defer (array_free(&file_indices));
  912. for_array(j, pkg->files) {
  913. AstFile *file = pkg->files[j];
  914. OdinDocFile doc_file = {};
  915. doc_file.pkg = pkg_index;
  916. doc_file.name = odin_doc_write_string(w, file->fullpath);
  917. OdinDocFileIndex file_index = odin_doc_write_item(w, &w->files, &doc_file);
  918. map_set(&w->file_cache, file, file_index);
  919. array_add(&file_indices, file_index);
  920. }
  921. doc_pkg.files = odin_write_slice(w, file_indices.data, file_indices.count);
  922. doc_pkg.entities = odin_doc_add_pkg_entities(w, pkg);
  923. if (dst) {
  924. *dst = doc_pkg;
  925. }
  926. }
  927. odin_doc_update_entities(w);
  928. }
  929. void odin_doc_write_to_file(OdinDocWriter *w, char const *filename) {
  930. gbFile f = {};
  931. gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, filename);
  932. if (err != gbFileError_None) {
  933. gb_printf_err("Failed to write .odin-doc to: %s\n", filename);
  934. gb_exit(1);
  935. return;
  936. }
  937. defer (gb_file_close(&f));
  938. if (gb_file_write(&f, w->data, w->data_len)) {
  939. err = gb_file_truncate(&f, w->data_len);
  940. gb_printf("Wrote .odin-doc file to: %s\n", filename);
  941. }
  942. }
  943. void odin_doc_write(CheckerInfo *info, char const *filename) {
  944. OdinDocWriter w_ = {};
  945. OdinDocWriter *w = &w_;
  946. defer (odin_doc_writer_destroy(w));
  947. w->info = info;
  948. odin_doc_writer_prepare(w);
  949. odin_doc_write_docs(w);
  950. odin_doc_writer_start_writing(w);
  951. odin_doc_write_docs(w);
  952. odin_doc_writer_end_writing(w);
  953. odin_doc_write_to_file(w, filename);
  954. }