docs_writer.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140
  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 = {};
  585. switch (type->Proc.calling_convention) {
  586. case ProcCC_Invalid:
  587. // no need
  588. break;
  589. case ProcCC_Odin:
  590. if (default_calling_convention() != ProcCC_Odin) {
  591. calling_convention = str_lit("odin");
  592. }
  593. break;
  594. case ProcCC_Contextless:
  595. if (default_calling_convention() != ProcCC_Contextless) {
  596. calling_convention = str_lit("contextless");
  597. }
  598. break;
  599. case ProcCC_CDecl:
  600. calling_convention = str_lit("cdecl");
  601. break;
  602. case ProcCC_StdCall:
  603. calling_convention = str_lit("stdcall");
  604. break;
  605. case ProcCC_FastCall:
  606. calling_convention = str_lit("fastcall");
  607. break;
  608. case ProcCC_None:
  609. calling_convention = str_lit("none");
  610. break;
  611. case ProcCC_Naked:
  612. calling_convention = str_lit("naked");
  613. break;
  614. case ProcCC_InlineAsm:
  615. calling_convention = str_lit("inline-assembly");
  616. break;
  617. }
  618. doc_type.calling_convention = odin_doc_write_string(w, calling_convention);
  619. }
  620. break;
  621. case Type_BitSet:
  622. doc_type.kind = OdinDocType_BitSet;
  623. {
  624. isize type_count = 0;
  625. OdinDocTypeIndex types[2] = {};
  626. if (type->BitSet.elem) {
  627. types[type_count++] = odin_doc_type(w, type->BitSet.elem);
  628. }
  629. if (type->BitSet.underlying) {
  630. types[type_count++] = odin_doc_type(w, type->BitSet.underlying);
  631. doc_type.flags |= OdinDocTypeFlag_BitSet_UnderlyingType;
  632. }
  633. doc_type.types = odin_write_slice(w, types, type_count);
  634. doc_type.elem_count_len = 2;
  635. doc_type.elem_counts[0] = type->BitSet.lower;
  636. doc_type.elem_counts[1] = type->BitSet.upper;
  637. }
  638. break;
  639. case Type_SimdVector:
  640. doc_type.kind = OdinDocType_SimdVector;
  641. doc_type.elem_count_len = 1;
  642. doc_type.elem_counts[0] = type->SimdVector.count;
  643. doc_type.types = odin_doc_type_as_slice(w, type->SimdVector.elem);
  644. // TODO(bill):
  645. break;
  646. case Type_RelativePointer:
  647. doc_type.kind = OdinDocType_RelativePointer;
  648. {
  649. OdinDocTypeIndex types[2] = {};
  650. types[0] = odin_doc_type(w, type->RelativePointer.pointer_type);
  651. types[1] = odin_doc_type(w, type->RelativePointer.base_integer);
  652. doc_type.types = odin_write_slice(w, types, gb_count_of(types));
  653. }
  654. break;
  655. case Type_RelativeSlice:
  656. doc_type.kind = OdinDocType_RelativeSlice;
  657. {
  658. OdinDocTypeIndex types[2] = {};
  659. types[0] = odin_doc_type(w, type->RelativeSlice.slice_type);
  660. types[1] = odin_doc_type(w, type->RelativeSlice.base_integer);
  661. doc_type.types = odin_write_slice(w, types, gb_count_of(types));
  662. }
  663. break;
  664. case Type_Matrix:
  665. doc_type.kind = OdinDocType_Matrix;
  666. doc_type.elem_count_len = 2;
  667. doc_type.elem_counts[0] = type->Matrix.row_count;
  668. doc_type.elem_counts[1] = type->Matrix.column_count;
  669. doc_type.types = odin_doc_type_as_slice(w, type->Matrix.elem);
  670. break;
  671. }
  672. if (dst) {
  673. *dst = doc_type;
  674. }
  675. return type_index;
  676. }
  677. OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
  678. if (e == nullptr) {
  679. return 0;
  680. }
  681. OdinDocEntityIndex *prev_index = map_get(&w->entity_cache, e);
  682. if (prev_index) {
  683. return *prev_index;
  684. }
  685. if (e->pkg != nullptr && map_get(&w->pkg_cache, e->pkg) == nullptr) {
  686. return 0;
  687. }
  688. OdinDocEntity doc_entity = {};
  689. OdinDocEntity* dst = nullptr;
  690. OdinDocEntityIndex doc_entity_index = odin_doc_write_item(w, &w->entities, &doc_entity, &dst);
  691. map_set(&w->entity_cache, e, doc_entity_index);
  692. Ast *type_expr = nullptr;
  693. Ast *init_expr = nullptr;
  694. Ast *decl_node = nullptr;
  695. CommentGroup *comment = nullptr;
  696. CommentGroup *docs = nullptr;
  697. if (e->decl_info != nullptr) {
  698. type_expr = e->decl_info->type_expr;
  699. init_expr = e->decl_info->init_expr;
  700. decl_node = e->decl_info->decl_node;
  701. comment = e->decl_info->comment;
  702. docs = e->decl_info->docs;
  703. }
  704. if (e->kind == Entity_Variable) {
  705. if (!comment) { comment = e->Variable.comment; }
  706. if (!docs) { docs = e->Variable.docs; }
  707. } else if (e->kind == Entity_Constant) {
  708. if (!comment) { comment = e->Constant.comment; }
  709. if (!docs) { docs = e->Constant.docs; }
  710. }
  711. String link_name = {};
  712. OdinDocEntityKind kind = OdinDocEntity_Invalid;
  713. u64 flags = 0;
  714. i32 field_group_index = -1;
  715. switch (e->kind) {
  716. case Entity_Invalid: kind = OdinDocEntity_Invalid; break;
  717. case Entity_Constant: kind = OdinDocEntity_Constant; break;
  718. case Entity_Variable: kind = OdinDocEntity_Variable; break;
  719. case Entity_TypeName: kind = OdinDocEntity_TypeName; break;
  720. case Entity_Procedure: kind = OdinDocEntity_Procedure; break;
  721. case Entity_ProcGroup: kind = OdinDocEntity_ProcGroup; break;
  722. case Entity_ImportName: kind = OdinDocEntity_ImportName; break;
  723. case Entity_LibraryName: kind = OdinDocEntity_LibraryName; break;
  724. }
  725. switch (e->kind) {
  726. case Entity_TypeName:
  727. if (e->TypeName.is_type_alias) {
  728. flags |= OdinDocEntityFlag_Type_Alias;
  729. }
  730. break;
  731. case Entity_Variable:
  732. if (e->Variable.is_foreign) { flags |= OdinDocEntityFlag_Foreign; }
  733. if (e->Variable.is_export) { flags |= OdinDocEntityFlag_Export; }
  734. if (e->Variable.thread_local_model != "") {
  735. flags |= OdinDocEntityFlag_Var_Thread_Local;
  736. }
  737. if (e->flags & EntityFlag_Static) { flags |= OdinDocEntityFlag_Var_Static; }
  738. link_name = e->Variable.link_name;
  739. if (init_expr == nullptr) {
  740. init_expr = e->Variable.init_expr;
  741. }
  742. field_group_index = e->Variable.field_group_index;
  743. break;
  744. case Entity_Constant:
  745. field_group_index = e->Constant.field_group_index;
  746. break;
  747. case Entity_Procedure:
  748. if (e->Procedure.is_foreign) { flags |= OdinDocEntityFlag_Foreign; }
  749. if (e->Procedure.is_export) { flags |= OdinDocEntityFlag_Export; }
  750. link_name = e->Procedure.link_name;
  751. break;
  752. }
  753. if (e->flags & EntityFlag_Param) {
  754. if (e->flags & EntityFlag_Using) { flags |= OdinDocEntityFlag_Param_Using; }
  755. if (e->flags & EntityFlag_ConstInput) { flags |= OdinDocEntityFlag_Param_Const; }
  756. if (e->flags & EntityFlag_AutoCast) { flags |= OdinDocEntityFlag_Param_AutoCast; }
  757. if (e->flags & EntityFlag_Ellipsis) { flags |= OdinDocEntityFlag_Param_Ellipsis; }
  758. if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; }
  759. if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; }
  760. }
  761. if (e->scope && (e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) && !is_entity_exported(e)) {
  762. flags |= OdinDocEntityFlag_Private;
  763. }
  764. OdinDocString init_string = {};
  765. if (init_expr) {
  766. init_string = odin_doc_expr_string(w, init_expr);
  767. } else {
  768. if (e->kind == Entity_Constant) {
  769. if (e->Constant.flags & EntityConstantFlag_ImplicitEnumValue) {
  770. init_string = {}; // Blank
  771. } else if (e->Constant.param_value.original_ast_expr) {
  772. init_string = odin_doc_expr_string(w, e->Constant.param_value.original_ast_expr);
  773. } else {
  774. init_string = odin_doc_write_string(w, make_string_c(exact_value_to_string(e->Constant.value)));
  775. }
  776. } else if (e->kind == Entity_Variable) {
  777. if (e->Variable.param_value.original_ast_expr) {
  778. init_string = odin_doc_expr_string(w, e->Variable.param_value.original_ast_expr);
  779. }
  780. }
  781. }
  782. doc_entity.kind = kind;
  783. doc_entity.flags = flags;
  784. doc_entity.pos = odin_doc_token_pos_cast(w, e->token.pos);
  785. doc_entity.name = odin_doc_write_string(w, e->token.string);
  786. doc_entity.type = 0; // Set later
  787. doc_entity.init_string = init_string;
  788. doc_entity.comment = odin_doc_comment_group_string(w, comment);
  789. doc_entity.docs = odin_doc_comment_group_string(w, docs);
  790. doc_entity.field_group_index = field_group_index;
  791. doc_entity.foreign_library = 0; // Set later
  792. doc_entity.link_name = odin_doc_write_string(w, link_name);
  793. if (e->decl_info != nullptr) {
  794. doc_entity.attributes = odin_doc_attributes(w, e->decl_info->attributes);
  795. }
  796. doc_entity.grouped_entities = {}; // Set later
  797. if (dst) {
  798. *dst = doc_entity;
  799. }
  800. return doc_entity_index;
  801. }
  802. void odin_doc_update_entities(OdinDocWriter *w) {
  803. {
  804. // NOTE(bill): Double pass, just in case entities are created on odin_doc_type
  805. auto entities = array_make<Entity *>(heap_allocator(), w->entity_cache.entries.count);
  806. defer (array_free(&entities));
  807. for_array(i, w->entity_cache.entries) {
  808. Entity *e = w->entity_cache.entries[i].key;
  809. entities[i] = e;
  810. }
  811. for_array(i, entities) {
  812. Entity *e = entities[i];
  813. OdinDocTypeIndex type_index = odin_doc_type(w, e->type);
  814. gb_unused(type_index);
  815. }
  816. }
  817. for_array(i, w->entity_cache.entries) {
  818. Entity *e = w->entity_cache.entries[i].key;
  819. OdinDocEntityIndex entity_index = w->entity_cache.entries[i].value;
  820. OdinDocTypeIndex type_index = odin_doc_type(w, e->type);
  821. OdinDocEntityIndex foreign_library = 0;
  822. OdinDocArray<OdinDocEntityIndex> grouped_entities = {};
  823. switch (e->kind) {
  824. case Entity_Variable:
  825. foreign_library = odin_doc_add_entity(w, e->Variable.foreign_library);
  826. break;
  827. case Entity_Procedure:
  828. foreign_library = odin_doc_add_entity(w, e->Procedure.foreign_library);
  829. break;
  830. case Entity_ProcGroup:
  831. {
  832. auto pges = array_make<OdinDocEntityIndex>(heap_allocator(), 0, e->ProcGroup.entities.count);
  833. defer (array_free(&pges));
  834. for_array(j, e->ProcGroup.entities) {
  835. OdinDocEntityIndex index = odin_doc_add_entity(w, e->ProcGroup.entities[j]);
  836. array_add(&pges, index);
  837. }
  838. grouped_entities = odin_write_slice(w, pges.data, pges.count);
  839. }
  840. break;
  841. }
  842. OdinDocEntity *dst = odin_doc_get_item(w, &w->entities, entity_index);
  843. if (dst) {
  844. dst->type = type_index;
  845. dst->foreign_library = foreign_library;
  846. dst->grouped_entities = grouped_entities;
  847. }
  848. }
  849. }
  850. OdinDocArray<OdinDocEntityIndex> odin_doc_add_pkg_entities(OdinDocWriter *w, AstPackage *pkg) {
  851. if (pkg->scope == nullptr) {
  852. return {};
  853. }
  854. if (map_get(&w->pkg_cache, pkg) == nullptr) {
  855. return {};
  856. }
  857. auto entities = array_make<Entity *>(heap_allocator(), 0, pkg->scope->elements.entries.count);
  858. defer (array_free(&entities));
  859. for_array(i, pkg->scope->elements.entries) {
  860. Entity *e = pkg->scope->elements.entries[i].value;
  861. switch (e->kind) {
  862. case Entity_Invalid:
  863. case Entity_Builtin:
  864. case Entity_Nil:
  865. case Entity_Label:
  866. continue;
  867. case Entity_Constant:
  868. case Entity_Variable:
  869. case Entity_TypeName:
  870. case Entity_Procedure:
  871. case Entity_ProcGroup:
  872. case Entity_ImportName:
  873. case Entity_LibraryName:
  874. // Fine
  875. break;
  876. }
  877. array_add(&entities, e);
  878. }
  879. gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
  880. auto entity_indices = array_make<OdinDocEntityIndex>(heap_allocator(), 0, w->entity_cache.entries.count);
  881. defer (array_free(&entity_indices));
  882. for_array(i, entities) {
  883. Entity *e = entities[i];
  884. if (e->pkg != pkg) {
  885. continue;
  886. }
  887. if (!is_entity_exported(e)) {
  888. continue;
  889. }
  890. if (e->token.string.len == 0) {
  891. continue;
  892. }
  893. OdinDocEntityIndex doc_entity_index = 0;
  894. doc_entity_index = odin_doc_add_entity(w, e);
  895. array_add(&entity_indices, doc_entity_index);
  896. }
  897. return odin_write_slice(w, entity_indices.data, entity_indices.count);
  898. }
  899. void odin_doc_write_docs(OdinDocWriter *w) {
  900. auto pkgs = array_make<AstPackage *>(heap_allocator(), 0, w->info->packages.entries.count);
  901. defer (array_free(&pkgs));
  902. for_array(i, w->info->packages.entries) {
  903. AstPackage *pkg = w->info->packages.entries[i].value;
  904. if (build_context.cmd_doc_flags & CmdDocFlag_AllPackages) {
  905. array_add(&pkgs, pkg);
  906. } else {
  907. if (pkg->kind == Package_Init) {
  908. array_add(&pkgs, pkg);
  909. } else if (pkg->is_extra) {
  910. array_add(&pkgs, pkg);
  911. }
  912. }
  913. }
  914. gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name);
  915. for_array(i, pkgs) {
  916. gbAllocator allocator = heap_allocator();
  917. AstPackage *pkg = pkgs[i];
  918. u32 pkg_flags = 0;
  919. switch (pkg->kind) {
  920. case Package_Normal:
  921. break;
  922. case Package_Runtime:
  923. pkg_flags |= OdinDocPkgFlag_Runtime;
  924. break;
  925. case Package_Init:
  926. pkg_flags |= OdinDocPkgFlag_Init;
  927. break;
  928. }
  929. if (pkg->name == "builtin") {
  930. pkg_flags |= OdinDocPkgFlag_Builtin;
  931. } else if (pkg->name == "intrinsics") {
  932. pkg_flags |= OdinDocPkgFlag_Builtin;
  933. }
  934. OdinDocPkg doc_pkg = {};
  935. doc_pkg.fullpath = odin_doc_write_string(w, pkg->fullpath);
  936. doc_pkg.name = odin_doc_write_string(w, pkg->name);
  937. doc_pkg.flags = pkg_flags;
  938. doc_pkg.docs = odin_doc_pkg_doc_string(w, pkg);
  939. OdinDocPkg *dst = nullptr;
  940. OdinDocPkgIndex pkg_index = odin_doc_write_item(w, &w->pkgs, &doc_pkg, &dst);
  941. map_set(&w->pkg_cache, pkg, pkg_index);
  942. auto file_indices = array_make<OdinDocFileIndex>(heap_allocator(), 0, pkg->files.count);
  943. defer (array_free(&file_indices));
  944. for_array(j, pkg->files) {
  945. AstFile *file = pkg->files[j];
  946. OdinDocFile doc_file = {};
  947. doc_file.pkg = pkg_index;
  948. doc_file.name = odin_doc_write_string(w, file->fullpath);
  949. OdinDocFileIndex file_index = odin_doc_write_item(w, &w->files, &doc_file);
  950. map_set(&w->file_cache, file, file_index);
  951. array_add(&file_indices, file_index);
  952. }
  953. doc_pkg.files = odin_write_slice(w, file_indices.data, file_indices.count);
  954. doc_pkg.entities = odin_doc_add_pkg_entities(w, pkg);
  955. if (dst) {
  956. *dst = doc_pkg;
  957. }
  958. }
  959. odin_doc_update_entities(w);
  960. }
  961. void odin_doc_write_to_file(OdinDocWriter *w, char const *filename) {
  962. gbFile f = {};
  963. gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, filename);
  964. if (err != gbFileError_None) {
  965. gb_printf_err("Failed to write .odin-doc to: %s\n", filename);
  966. gb_exit(1);
  967. return;
  968. }
  969. defer (gb_file_close(&f));
  970. if (gb_file_write(&f, w->data, w->data_len)) {
  971. err = gb_file_truncate(&f, w->data_len);
  972. gb_printf("Wrote .odin-doc file to: %s\n", filename);
  973. }
  974. }
  975. void odin_doc_write(CheckerInfo *info, char const *filename) {
  976. OdinDocWriter w_ = {};
  977. OdinDocWriter *w = &w_;
  978. defer (odin_doc_writer_destroy(w));
  979. w->info = info;
  980. odin_doc_writer_prepare(w);
  981. odin_doc_write_docs(w);
  982. odin_doc_writer_start_writing(w);
  983. odin_doc_write_docs(w);
  984. odin_doc_writer_end_writing(w);
  985. odin_doc_write_to_file(w, filename);
  986. }