docs_writer.cpp 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
  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 = false;
  235. if (comment[1] == '/') {
  236. slash_slash = true;
  237. comment.text += 2;
  238. comment.len -= 2;
  239. } else if (comment[1] == '*') {
  240. comment.text += 2;
  241. comment.len -= 4;
  242. }
  243. // Ignore the first space
  244. if (comment.len > 0 && comment[0] == ' ') {
  245. comment.text += 1;
  246. comment.len -= 1;
  247. }
  248. if (slash_slash) {
  249. if (string_starts_with(comment, str_lit("+"))) {
  250. continue;
  251. }
  252. if (string_starts_with(comment, str_lit("@("))) {
  253. continue;
  254. }
  255. }
  256. if (slash_slash) {
  257. array_add_elems(buf, comment.text, comment.len);
  258. array_add(buf, cast(u8)'\n');
  259. count += 1;
  260. } else {
  261. isize pos = 0;
  262. for (; pos < comment.len; pos++) {
  263. isize end = pos;
  264. for (; end < comment.len; end++) {
  265. if (comment[end] == '\n') {
  266. break;
  267. }
  268. }
  269. String line = substring(comment, pos, end);
  270. pos = end;
  271. String trimmed_line = string_trim_whitespace(line);
  272. if (trimmed_line.len == 0) {
  273. if (count == 0) {
  274. continue;
  275. }
  276. }
  277. /*
  278. * Remove comments with
  279. * styles
  280. * like this
  281. */
  282. if (string_starts_with(line, str_lit("* "))) {
  283. line = substring(line, 2, line.len);
  284. }
  285. array_add_elems(buf, line.text, line.len);
  286. array_add(buf, cast(u8)'\n');
  287. count += 1;
  288. }
  289. }
  290. }
  291. if (count > 0) {
  292. array_add(buf, cast(u8)'\n');
  293. return true;
  294. }
  295. return false;
  296. }
  297. OdinDocString odin_doc_pkg_doc_string(OdinDocWriter *w, AstPackage *pkg) {
  298. if (pkg == nullptr) {
  299. return {};
  300. }
  301. auto buf = array_make<u8>(permanent_allocator(), 0, 0); // Minor leak
  302. for_array(i, pkg->files) {
  303. AstFile *f = pkg->files[i];
  304. if (f->pkg_decl) {
  305. GB_ASSERT(f->pkg_decl->kind == Ast_PackageDecl);
  306. odin_doc_append_comment_group_string(&buf, f->pkg_decl->PackageDecl.docs);
  307. }
  308. }
  309. return odin_doc_write_string_without_cache(w, make_string(buf.data, buf.count));
  310. }
  311. OdinDocString odin_doc_comment_group_string(OdinDocWriter *w, CommentGroup *g) {
  312. if (g == nullptr) {
  313. return {};
  314. }
  315. auto buf = array_make<u8>(permanent_allocator(), 0, 0); // Minor leak
  316. odin_doc_append_comment_group_string(&buf, g);
  317. return odin_doc_write_string_without_cache(w, make_string(buf.data, buf.count));
  318. }
  319. OdinDocString odin_doc_expr_string(OdinDocWriter *w, Ast *expr) {
  320. if (expr == nullptr) {
  321. return {};
  322. }
  323. gbString s = write_expr_to_string( // Minor leak
  324. gb_string_make(permanent_allocator(), ""),
  325. expr,
  326. build_context.cmd_doc_flags & CmdDocFlag_Short
  327. );
  328. return odin_doc_write_string(w, make_string(cast(u8 *)s, gb_string_length(s)));
  329. }
  330. OdinDocArray<OdinDocAttribute> odin_doc_attributes(OdinDocWriter *w, Array<Ast *> const &attributes) {
  331. isize count = 0;
  332. for_array(i, attributes) {
  333. Ast *attr = attributes[i];
  334. if (attr->kind != Ast_Attribute) continue;
  335. count += attr->Attribute.elems.count;
  336. };
  337. auto attribs = array_make<OdinDocAttribute>(heap_allocator(), 0, count);
  338. defer (array_free(&attribs));
  339. for_array(i, attributes) {
  340. Ast *attr = attributes[i];
  341. if (attr->kind != Ast_Attribute) continue;
  342. for_array(j, attr->Attribute.elems) {
  343. Ast *elem = attr->Attribute.elems[j];
  344. String name = {};
  345. Ast *value = nullptr;
  346. switch (elem->kind) {
  347. case_ast_node(i, Ident, elem);
  348. name = i->token.string;
  349. case_end;
  350. case_ast_node(i, Implicit, elem);
  351. name = i->string;
  352. case_end;
  353. case_ast_node(fv, FieldValue, elem);
  354. if (fv->field->kind == Ast_Ident) {
  355. name = fv->field->Ident.token.string;
  356. } else if (fv->field->kind == Ast_Implicit) {
  357. name = fv->field->Implicit.string;
  358. }
  359. value = fv->value;
  360. case_end;
  361. default:
  362. continue;
  363. }
  364. OdinDocAttribute doc_attrib = {};
  365. doc_attrib.name = odin_doc_write_string(w, name);
  366. doc_attrib.value = odin_doc_expr_string(w, value);
  367. array_add(&attribs, doc_attrib);
  368. }
  369. }
  370. return odin_write_slice(w, attribs.data, attribs.count);
  371. }
  372. OdinDocArray<OdinDocString> odin_doc_where_clauses(OdinDocWriter *w, Slice<Ast *> const &where_clauses) {
  373. if (where_clauses.count == 0) {
  374. return {};
  375. }
  376. auto clauses = array_make<OdinDocString>(heap_allocator(), where_clauses.count);
  377. defer (array_free(&clauses));
  378. for_array(i, where_clauses) {
  379. clauses[i] = odin_doc_expr_string(w, where_clauses[i]);
  380. }
  381. return odin_write_slice(w, clauses.data, clauses.count);
  382. }
  383. OdinDocArray<OdinDocTypeIndex> odin_doc_type_as_slice(OdinDocWriter *w, Type *type) {
  384. OdinDocTypeIndex index = odin_doc_type(w, type);
  385. return odin_write_item_as_slice(w, index);
  386. }
  387. OdinDocArray<OdinDocEntityIndex> odin_doc_add_entity_as_slice(OdinDocWriter *w, Entity *e) {
  388. OdinDocEntityIndex index = odin_doc_add_entity(w, e);
  389. return odin_write_item_as_slice(w, index);
  390. }
  391. OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
  392. if (type == nullptr) {
  393. return 0;
  394. }
  395. OdinDocTypeIndex *found = map_get(&w->type_cache, type);
  396. if (found) {
  397. return *found;
  398. }
  399. for_array(i, w->type_cache.entries) {
  400. // NOTE(bill): THIS IS SLOW
  401. Type *other = w->type_cache.entries[i].key;
  402. if (are_types_identical_unique_tuples(type, other)) {
  403. OdinDocTypeIndex index = w->type_cache.entries[i].value;
  404. map_set(&w->type_cache, type, index);
  405. return index;
  406. }
  407. }
  408. OdinDocType *dst = nullptr;
  409. OdinDocType doc_type = {};
  410. OdinDocTypeIndex type_index = 0;
  411. type_index = odin_doc_write_item(w, &w->types, &doc_type, &dst);
  412. map_set(&w->type_cache, type, type_index);
  413. switch (type->kind) {
  414. case Type_Basic:
  415. doc_type.kind = OdinDocType_Basic;
  416. doc_type.name = odin_doc_write_string(w, type->Basic.name);
  417. if (is_type_untyped(type)) {
  418. doc_type.flags |= OdinDocTypeFlag_Basic_untyped;
  419. }
  420. break;
  421. case Type_Named:
  422. doc_type.kind = OdinDocType_Named;
  423. doc_type.name = odin_doc_write_string(w, type->Named.name);
  424. doc_type.types = odin_doc_type_as_slice(w, base_type(type));
  425. doc_type.entities = odin_doc_add_entity_as_slice(w, type->Named.type_name);
  426. break;
  427. case Type_Generic:
  428. {
  429. String name = type->Generic.name;
  430. if (type->Generic.entity) {
  431. name = type->Generic.entity->token.string;
  432. }
  433. doc_type.kind = OdinDocType_Generic;
  434. doc_type.name = odin_doc_write_string(w, name);
  435. if (type->Generic.specialized) {
  436. doc_type.types = odin_doc_type_as_slice(w, type->Generic.specialized);
  437. }
  438. }
  439. break;
  440. case Type_Pointer:
  441. doc_type.kind = OdinDocType_Pointer;
  442. doc_type.types = odin_doc_type_as_slice(w, type->Pointer.elem);
  443. break;
  444. case Type_MultiPointer:
  445. doc_type.kind = OdinDocType_MultiPointer;
  446. doc_type.types = odin_doc_type_as_slice(w, type->MultiPointer.elem);
  447. break;
  448. case Type_Array:
  449. doc_type.kind = OdinDocType_Array;
  450. doc_type.elem_count_len = 1;
  451. doc_type.elem_counts[0] = type->Array.count;
  452. doc_type.types = odin_doc_type_as_slice(w, type->Array.elem);
  453. break;
  454. case Type_EnumeratedArray:
  455. doc_type.kind = OdinDocType_EnumeratedArray;
  456. doc_type.elem_count_len = 1;
  457. doc_type.elem_counts[0] = type->EnumeratedArray.count;
  458. {
  459. OdinDocTypeIndex types[2] = {};
  460. types[0] = odin_doc_type(w, type->EnumeratedArray.index);
  461. types[1] = odin_doc_type(w, type->EnumeratedArray.elem);
  462. doc_type.types = odin_write_slice(w, types, gb_count_of(types));
  463. }
  464. break;
  465. case Type_Slice:
  466. doc_type.kind = OdinDocType_Slice;
  467. doc_type.types = odin_doc_type_as_slice(w, type->Slice.elem);
  468. break;
  469. case Type_DynamicArray:
  470. doc_type.kind = OdinDocType_DynamicArray;
  471. doc_type.types = odin_doc_type_as_slice(w, type->DynamicArray.elem);
  472. break;
  473. case Type_Map:
  474. doc_type.kind = OdinDocType_Map;
  475. {
  476. OdinDocTypeIndex types[2] = {};
  477. types[0] = odin_doc_type(w, type->Map.key);
  478. types[1] = odin_doc_type(w, type->Map.value);
  479. doc_type.types = odin_write_slice(w, types, gb_count_of(types));
  480. }
  481. break;
  482. case Type_Struct:
  483. doc_type.kind = OdinDocType_Struct;
  484. if (type->Struct.soa_kind != StructSoa_None) {
  485. switch (type->Struct.soa_kind) {
  486. case StructSoa_Fixed:
  487. doc_type.kind = OdinDocType_SOAStructFixed;
  488. doc_type.elem_count_len = 1;
  489. doc_type.elem_counts[0] = type->Struct.soa_count;
  490. break;
  491. case StructSoa_Slice:
  492. doc_type.kind = OdinDocType_SOAStructSlice;
  493. break;
  494. case StructSoa_Dynamic:
  495. doc_type.kind = OdinDocType_SOAStructDynamic;
  496. break;
  497. }
  498. doc_type.types = odin_doc_type_as_slice(w, type->Struct.soa_elem);
  499. } else {
  500. if (type->Struct.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Struct_polymorphic; }
  501. if (type->Struct.is_packed) { doc_type.flags |= OdinDocTypeFlag_Struct_packed; }
  502. if (type->Struct.is_raw_union) { doc_type.flags |= OdinDocTypeFlag_Struct_raw_union; }
  503. auto fields = array_make<OdinDocEntityIndex>(heap_allocator(), type->Struct.fields.count);
  504. defer (array_free(&fields));
  505. for_array(i, type->Struct.fields) {
  506. fields[i] = odin_doc_add_entity(w, type->Struct.fields[i]);
  507. }
  508. doc_type.entities = odin_write_slice(w, fields.data, fields.count);
  509. doc_type.polmorphic_params = odin_doc_type(w, type->Struct.polymorphic_params);
  510. if (type->Struct.node) {
  511. ast_node(st, StructType, type->Struct.node);
  512. if (st->align) {
  513. doc_type.custom_align = odin_doc_expr_string(w, st->align);
  514. }
  515. doc_type.where_clauses = odin_doc_where_clauses(w, st->where_clauses);
  516. }
  517. auto tags = array_make<OdinDocString>(heap_allocator(), type->Struct.fields.count);
  518. defer (array_free(&tags));
  519. for_array(i, type->Struct.fields) {
  520. tags[i] = odin_doc_write_string(w, type->Struct.tags[i]);
  521. }
  522. doc_type.tags = odin_write_slice(w, tags.data, tags.count);
  523. }
  524. break;
  525. case Type_Union:
  526. doc_type.kind = OdinDocType_Union;
  527. if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; }
  528. if (type->Union.no_nil) { doc_type.flags |= OdinDocTypeFlag_Union_no_nil; }
  529. if (type->Union.maybe) { doc_type.flags |= OdinDocTypeFlag_Union_maybe; }
  530. {
  531. auto variants = array_make<OdinDocTypeIndex>(heap_allocator(), type->Union.variants.count);
  532. defer (array_free(&variants));
  533. for_array(i, type->Union.variants) {
  534. variants[i] = odin_doc_type(w, type->Union.variants[i]);
  535. }
  536. doc_type.types = odin_write_slice(w, variants.data, variants.count);
  537. doc_type.polmorphic_params = odin_doc_type(w, type->Union.polymorphic_params);
  538. }
  539. if (type->Union.node) {
  540. ast_node(ut, UnionType, type->Union.node);
  541. if (ut->align) {
  542. doc_type.custom_align = odin_doc_expr_string(w, ut->align);
  543. }
  544. doc_type.where_clauses = odin_doc_where_clauses(w, ut->where_clauses);
  545. }
  546. break;
  547. case Type_Enum:
  548. doc_type.kind = OdinDocType_Enum;
  549. {
  550. auto fields = array_make<OdinDocEntityIndex>(heap_allocator(), type->Enum.fields.count);
  551. defer (array_free(&fields));
  552. for_array(i, type->Enum.fields) {
  553. fields[i] = odin_doc_add_entity(w, type->Enum.fields[i]);
  554. }
  555. doc_type.entities = odin_write_slice(w, fields.data, fields.count);
  556. if (type->Enum.base_type) {
  557. doc_type.types = odin_doc_type_as_slice(w, type->Enum.base_type);
  558. }
  559. }
  560. break;
  561. case Type_Tuple:
  562. doc_type.kind = OdinDocType_Tuple;
  563. {
  564. auto variables = array_make<OdinDocEntityIndex>(heap_allocator(), type->Tuple.variables.count);
  565. defer (array_free(&variables));
  566. for_array(i, type->Tuple.variables) {
  567. variables[i] = odin_doc_add_entity(w, type->Tuple.variables[i]);
  568. }
  569. doc_type.entities = odin_write_slice(w, variables.data, variables.count);
  570. }
  571. break;
  572. case Type_Proc:
  573. doc_type.kind = OdinDocType_Proc;
  574. if (type->Proc.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Proc_polymorphic; }
  575. if (type->Proc.diverging) { doc_type.flags |= OdinDocTypeFlag_Proc_diverging; }
  576. if (type->Proc.optional_ok) { doc_type.flags |= OdinDocTypeFlag_Proc_optional_ok; }
  577. if (type->Proc.variadic) { doc_type.flags |= OdinDocTypeFlag_Proc_variadic; }
  578. if (type->Proc.c_vararg) { doc_type.flags |= OdinDocTypeFlag_Proc_c_vararg; }
  579. {
  580. OdinDocTypeIndex types[2];
  581. types[0] = odin_doc_type(w, type->Proc.params);
  582. types[1] = odin_doc_type(w, type->Proc.results);
  583. doc_type.types = odin_write_slice(w, types, gb_count_of(types));
  584. String calling_convention = make_string_c(proc_calling_convention_strings[type->Proc.calling_convention]);
  585. doc_type.calling_convention = odin_doc_write_string(w, calling_convention);
  586. }
  587. break;
  588. case Type_BitSet:
  589. doc_type.kind = OdinDocType_BitSet;
  590. {
  591. isize type_count = 0;
  592. OdinDocTypeIndex types[2] = {};
  593. if (type->BitSet.elem) {
  594. types[type_count++] = odin_doc_type(w, type->BitSet.elem);
  595. }
  596. if (type->BitSet.underlying) {
  597. types[type_count++] = odin_doc_type(w, type->BitSet.underlying);
  598. doc_type.flags |= OdinDocTypeFlag_BitSet_UnderlyingType;
  599. }
  600. doc_type.types = odin_write_slice(w, types, type_count);
  601. doc_type.elem_count_len = 2;
  602. doc_type.elem_counts[0] = type->BitSet.lower;
  603. doc_type.elem_counts[1] = type->BitSet.upper;
  604. }
  605. break;
  606. case Type_SimdVector:
  607. doc_type.kind = OdinDocType_SimdVector;
  608. doc_type.elem_count_len = 1;
  609. doc_type.elem_counts[0] = type->SimdVector.count;
  610. doc_type.types = odin_doc_type_as_slice(w, type->SimdVector.elem);
  611. // TODO(bill):
  612. break;
  613. case Type_RelativePointer:
  614. doc_type.kind = OdinDocType_RelativePointer;
  615. {
  616. OdinDocTypeIndex types[2] = {};
  617. types[0] = odin_doc_type(w, type->RelativePointer.pointer_type);
  618. types[1] = odin_doc_type(w, type->RelativePointer.base_integer);
  619. doc_type.types = odin_write_slice(w, types, gb_count_of(types));
  620. }
  621. break;
  622. case Type_RelativeSlice:
  623. doc_type.kind = OdinDocType_RelativeSlice;
  624. {
  625. OdinDocTypeIndex types[2] = {};
  626. types[0] = odin_doc_type(w, type->RelativeSlice.slice_type);
  627. types[1] = odin_doc_type(w, type->RelativeSlice.base_integer);
  628. doc_type.types = odin_write_slice(w, types, gb_count_of(types));
  629. }
  630. break;
  631. case Type_Matrix:
  632. doc_type.kind = OdinDocType_Matrix;
  633. doc_type.elem_count_len = 2;
  634. doc_type.elem_counts[0] = type->Matrix.row_count;
  635. doc_type.elem_counts[1] = type->Matrix.column_count;
  636. doc_type.types = odin_doc_type_as_slice(w, type->Matrix.elem);
  637. break;
  638. }
  639. if (dst) {
  640. *dst = doc_type;
  641. }
  642. return type_index;
  643. }
  644. OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
  645. if (e == nullptr) {
  646. return 0;
  647. }
  648. OdinDocEntityIndex *prev_index = map_get(&w->entity_cache, e);
  649. if (prev_index) {
  650. return *prev_index;
  651. }
  652. if (e->pkg != nullptr && map_get(&w->pkg_cache, e->pkg) == nullptr) {
  653. return 0;
  654. }
  655. OdinDocEntity doc_entity = {};
  656. OdinDocEntity* dst = nullptr;
  657. OdinDocEntityIndex doc_entity_index = odin_doc_write_item(w, &w->entities, &doc_entity, &dst);
  658. map_set(&w->entity_cache, e, doc_entity_index);
  659. Ast *type_expr = nullptr;
  660. Ast *init_expr = nullptr;
  661. Ast *decl_node = nullptr;
  662. CommentGroup *comment = nullptr;
  663. CommentGroup *docs = nullptr;
  664. if (e->decl_info != nullptr) {
  665. type_expr = e->decl_info->type_expr;
  666. init_expr = e->decl_info->init_expr;
  667. decl_node = e->decl_info->decl_node;
  668. comment = e->decl_info->comment;
  669. docs = e->decl_info->docs;
  670. }
  671. if (e->kind == Entity_Variable) {
  672. if (!comment) { comment = e->Variable.comment; }
  673. if (!docs) { docs = e->Variable.docs; }
  674. } else if (e->kind == Entity_Constant) {
  675. if (!comment) { comment = e->Constant.comment; }
  676. if (!docs) { docs = e->Constant.docs; }
  677. }
  678. String name = e->token.string;
  679. String link_name = {};
  680. TokenPos pos = e->token.pos;
  681. OdinDocEntityKind kind = OdinDocEntity_Invalid;
  682. u64 flags = 0;
  683. i32 field_group_index = -1;
  684. switch (e->kind) {
  685. case Entity_Invalid: kind = OdinDocEntity_Invalid; break;
  686. case Entity_Constant: kind = OdinDocEntity_Constant; break;
  687. case Entity_Variable: kind = OdinDocEntity_Variable; break;
  688. case Entity_TypeName: kind = OdinDocEntity_TypeName; break;
  689. case Entity_Procedure: kind = OdinDocEntity_Procedure; break;
  690. case Entity_ProcGroup: kind = OdinDocEntity_ProcGroup; break;
  691. case Entity_ImportName: kind = OdinDocEntity_ImportName; break;
  692. case Entity_LibraryName: kind = OdinDocEntity_LibraryName; break;
  693. case Entity_Builtin: kind = OdinDocEntity_Builtin; break;
  694. }
  695. switch (e->kind) {
  696. case Entity_TypeName:
  697. if (e->TypeName.is_type_alias) {
  698. flags |= OdinDocEntityFlag_Type_Alias;
  699. }
  700. break;
  701. case Entity_Variable:
  702. if (e->Variable.is_foreign) { flags |= OdinDocEntityFlag_Foreign; }
  703. if (e->Variable.is_export) { flags |= OdinDocEntityFlag_Export; }
  704. if (e->Variable.thread_local_model != "") {
  705. flags |= OdinDocEntityFlag_Var_Thread_Local;
  706. }
  707. if (e->flags & EntityFlag_Static) { flags |= OdinDocEntityFlag_Var_Static; }
  708. link_name = e->Variable.link_name;
  709. if (init_expr == nullptr) {
  710. init_expr = e->Variable.init_expr;
  711. }
  712. field_group_index = e->Variable.field_group_index;
  713. break;
  714. case Entity_Constant:
  715. field_group_index = e->Constant.field_group_index;
  716. break;
  717. case Entity_Procedure:
  718. if (e->Procedure.is_foreign) { flags |= OdinDocEntityFlag_Foreign; }
  719. if (e->Procedure.is_export) { flags |= OdinDocEntityFlag_Export; }
  720. link_name = e->Procedure.link_name;
  721. break;
  722. case Entity_Builtin:
  723. {
  724. auto bp = builtin_procs[e->Builtin.id];
  725. pos = {};
  726. name = bp.name;
  727. switch (bp.pkg) {
  728. case BuiltinProcPkg_builtin:
  729. flags |= OdinDocEntityFlag_Builtin_Pkg_Builtin;
  730. break;
  731. case BuiltinProcPkg_intrinsics:
  732. flags |= OdinDocEntityFlag_Builtin_Pkg_Intrinsics;
  733. break;
  734. default:
  735. GB_PANIC("Unhandled BuiltinProcPkg");
  736. }
  737. }
  738. break;
  739. }
  740. if (e->flags & EntityFlag_Param) {
  741. if (e->flags & EntityFlag_Using) { flags |= OdinDocEntityFlag_Param_Using; }
  742. if (e->flags & EntityFlag_ConstInput) { flags |= OdinDocEntityFlag_Param_Const; }
  743. if (e->flags & EntityFlag_AutoCast) { flags |= OdinDocEntityFlag_Param_AutoCast; }
  744. if (e->flags & EntityFlag_Ellipsis) { flags |= OdinDocEntityFlag_Param_Ellipsis; }
  745. if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; }
  746. if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; }
  747. }
  748. if (e->scope && (e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) && !is_entity_exported(e)) {
  749. flags |= OdinDocEntityFlag_Private;
  750. }
  751. OdinDocString init_string = {};
  752. if (init_expr) {
  753. init_string = odin_doc_expr_string(w, init_expr);
  754. } else {
  755. if (e->kind == Entity_Constant) {
  756. if (e->Constant.flags & EntityConstantFlag_ImplicitEnumValue) {
  757. init_string = {}; // Blank
  758. } else if (e->Constant.param_value.original_ast_expr) {
  759. init_string = odin_doc_expr_string(w, e->Constant.param_value.original_ast_expr);
  760. } else {
  761. init_string = odin_doc_write_string(w, make_string_c(exact_value_to_string(e->Constant.value)));
  762. }
  763. } else if (e->kind == Entity_Variable) {
  764. if (e->Variable.param_value.original_ast_expr) {
  765. init_string = odin_doc_expr_string(w, e->Variable.param_value.original_ast_expr);
  766. }
  767. }
  768. }
  769. doc_entity.kind = kind;
  770. doc_entity.flags = flags;
  771. doc_entity.pos = odin_doc_token_pos_cast(w, pos);
  772. doc_entity.name = odin_doc_write_string(w, name);
  773. doc_entity.type = 0; // Set later
  774. doc_entity.init_string = init_string;
  775. doc_entity.comment = odin_doc_comment_group_string(w, comment);
  776. doc_entity.docs = odin_doc_comment_group_string(w, docs);
  777. doc_entity.field_group_index = field_group_index;
  778. doc_entity.foreign_library = 0; // Set later
  779. doc_entity.link_name = odin_doc_write_string(w, link_name);
  780. if (e->decl_info != nullptr) {
  781. doc_entity.attributes = odin_doc_attributes(w, e->decl_info->attributes);
  782. }
  783. doc_entity.grouped_entities = {}; // Set later
  784. if (dst) {
  785. *dst = doc_entity;
  786. }
  787. return doc_entity_index;
  788. }
  789. void odin_doc_update_entities(OdinDocWriter *w) {
  790. {
  791. // NOTE(bill): Double pass, just in case entities are created on odin_doc_type
  792. auto entities = array_make<Entity *>(heap_allocator(), w->entity_cache.entries.count);
  793. defer (array_free(&entities));
  794. for_array(i, w->entity_cache.entries) {
  795. Entity *e = w->entity_cache.entries[i].key;
  796. entities[i] = e;
  797. }
  798. for_array(i, entities) {
  799. Entity *e = entities[i];
  800. OdinDocTypeIndex type_index = odin_doc_type(w, e->type);
  801. gb_unused(type_index);
  802. }
  803. }
  804. for_array(i, w->entity_cache.entries) {
  805. Entity *e = w->entity_cache.entries[i].key;
  806. OdinDocEntityIndex entity_index = w->entity_cache.entries[i].value;
  807. OdinDocTypeIndex type_index = odin_doc_type(w, e->type);
  808. OdinDocEntityIndex foreign_library = 0;
  809. OdinDocArray<OdinDocEntityIndex> grouped_entities = {};
  810. switch (e->kind) {
  811. case Entity_Variable:
  812. foreign_library = odin_doc_add_entity(w, e->Variable.foreign_library);
  813. break;
  814. case Entity_Procedure:
  815. foreign_library = odin_doc_add_entity(w, e->Procedure.foreign_library);
  816. break;
  817. case Entity_ProcGroup:
  818. {
  819. auto pges = array_make<OdinDocEntityIndex>(heap_allocator(), 0, e->ProcGroup.entities.count);
  820. defer (array_free(&pges));
  821. for_array(j, e->ProcGroup.entities) {
  822. OdinDocEntityIndex index = odin_doc_add_entity(w, e->ProcGroup.entities[j]);
  823. array_add(&pges, index);
  824. }
  825. grouped_entities = odin_write_slice(w, pges.data, pges.count);
  826. }
  827. break;
  828. }
  829. OdinDocEntity *dst = odin_doc_get_item(w, &w->entities, entity_index);
  830. if (dst) {
  831. dst->type = type_index;
  832. dst->foreign_library = foreign_library;
  833. dst->grouped_entities = grouped_entities;
  834. }
  835. }
  836. }
  837. OdinDocArray<OdinDocScopeEntry> odin_doc_add_pkg_entries(OdinDocWriter *w, AstPackage *pkg) {
  838. if (pkg->scope == nullptr) {
  839. return {};
  840. }
  841. if (map_get(&w->pkg_cache, pkg) == nullptr) {
  842. return {};
  843. }
  844. auto entries = array_make<OdinDocScopeEntry>(heap_allocator(), 0, w->entity_cache.entries.count);
  845. defer (array_free(&entries));
  846. for_array(i, pkg->scope->elements.entries) {
  847. String name = pkg->scope->elements.entries[i].key.string;
  848. Entity *e = pkg->scope->elements.entries[i].value;
  849. switch (e->kind) {
  850. case Entity_Invalid:
  851. case Entity_Nil:
  852. case Entity_Label:
  853. continue;
  854. case Entity_Constant:
  855. case Entity_Variable:
  856. case Entity_TypeName:
  857. case Entity_Procedure:
  858. case Entity_ProcGroup:
  859. case Entity_ImportName:
  860. case Entity_LibraryName:
  861. case Entity_Builtin:
  862. // Fine
  863. break;
  864. }
  865. if (e->pkg != pkg) {
  866. continue;
  867. }
  868. if (!is_entity_exported(e, true)) {
  869. continue;
  870. }
  871. if (e->token.string.len == 0) {
  872. continue;
  873. }
  874. OdinDocScopeEntry entry = {};
  875. entry.name = odin_doc_write_string(w, name);
  876. entry.entity = odin_doc_add_entity(w, e);
  877. array_add(&entries, entry);
  878. }
  879. return odin_write_slice(w, entries.data, entries.count);
  880. }
  881. void odin_doc_write_docs(OdinDocWriter *w) {
  882. auto pkgs = array_make<AstPackage *>(heap_allocator(), 0, w->info->packages.entries.count);
  883. defer (array_free(&pkgs));
  884. for_array(i, w->info->packages.entries) {
  885. AstPackage *pkg = w->info->packages.entries[i].value;
  886. if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) {
  887. array_add(&pkgs, pkg);
  888. } else {
  889. if (pkg->kind == Package_Init) {
  890. array_add(&pkgs, pkg);
  891. } else if (pkg->is_extra) {
  892. array_add(&pkgs, pkg);
  893. }
  894. }
  895. }
  896. gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name);
  897. for_array(i, pkgs) {
  898. gbAllocator allocator = heap_allocator();
  899. AstPackage *pkg = pkgs[i];
  900. u32 pkg_flags = 0;
  901. switch (pkg->kind) {
  902. case Package_Normal:
  903. break;
  904. case Package_Runtime:
  905. pkg_flags |= OdinDocPkgFlag_Runtime;
  906. break;
  907. case Package_Init:
  908. pkg_flags |= OdinDocPkgFlag_Init;
  909. break;
  910. }
  911. if (pkg->name == "builtin") {
  912. pkg_flags |= OdinDocPkgFlag_Builtin;
  913. } else if (pkg->name == "intrinsics") {
  914. pkg_flags |= OdinDocPkgFlag_Builtin;
  915. }
  916. OdinDocPkg doc_pkg = {};
  917. doc_pkg.fullpath = odin_doc_write_string(w, pkg->fullpath);
  918. doc_pkg.name = odin_doc_write_string(w, pkg->name);
  919. doc_pkg.flags = pkg_flags;
  920. doc_pkg.docs = odin_doc_pkg_doc_string(w, pkg);
  921. OdinDocPkg *dst = nullptr;
  922. OdinDocPkgIndex pkg_index = odin_doc_write_item(w, &w->pkgs, &doc_pkg, &dst);
  923. map_set(&w->pkg_cache, pkg, pkg_index);
  924. auto file_indices = array_make<OdinDocFileIndex>(heap_allocator(), 0, pkg->files.count);
  925. defer (array_free(&file_indices));
  926. for_array(j, pkg->files) {
  927. AstFile *file = pkg->files[j];
  928. OdinDocFile doc_file = {};
  929. doc_file.pkg = pkg_index;
  930. doc_file.name = odin_doc_write_string(w, file->fullpath);
  931. OdinDocFileIndex file_index = odin_doc_write_item(w, &w->files, &doc_file);
  932. map_set(&w->file_cache, file, file_index);
  933. array_add(&file_indices, file_index);
  934. }
  935. doc_pkg.files = odin_write_slice(w, file_indices.data, file_indices.count);
  936. doc_pkg.entries = odin_doc_add_pkg_entries(w, pkg);
  937. if (dst) {
  938. *dst = doc_pkg;
  939. }
  940. }
  941. odin_doc_update_entities(w);
  942. }
  943. void odin_doc_write_to_file(OdinDocWriter *w, char const *filename) {
  944. gbFile f = {};
  945. gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, filename);
  946. if (err != gbFileError_None) {
  947. gb_printf_err("Failed to write .odin-doc to: %s\n", filename);
  948. gb_exit(1);
  949. return;
  950. }
  951. defer (gb_file_close(&f));
  952. if (gb_file_write(&f, w->data, w->data_len)) {
  953. err = gb_file_truncate(&f, w->data_len);
  954. gb_printf("Wrote .odin-doc file to: %s\n", filename);
  955. }
  956. }
  957. void odin_doc_write(CheckerInfo *info, char const *filename) {
  958. OdinDocWriter w_ = {};
  959. OdinDocWriter *w = &w_;
  960. defer (odin_doc_writer_destroy(w));
  961. w->info = info;
  962. odin_doc_writer_prepare(w);
  963. odin_doc_write_docs(w);
  964. odin_doc_writer_start_writing(w);
  965. odin_doc_write_docs(w);
  966. odin_doc_writer_end_writing(w);
  967. odin_doc_write_to_file(w, filename);
  968. }