// Write a description of the given method to the current type file static void doc_method(docgen_t* docgen, ast_t* method) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(method != NULL); AST_GET_CHILDREN(method, cap, id, t_params, params, ret, error, body, doc); const char* name = ast_name(id); assert(name != NULL); // Sub heading fprintf(docgen->type_file, "## %s %s()\n", ast_get_print(method), name); // Reconstruct signature fprintf(docgen->type_file, "%s", ast_get_print(method)); if(ast_id(method) == TK_FUN) { const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, " %s\n", cap_text); } fprintf(docgen->type_file, " %s", name); doc_type_params(docgen, t_params); doc_params(docgen, params); if(ast_id(method) == TK_FUN) { fprintf(docgen->type_file, ": "); doc_type(docgen, ret); } if(ast_id(error) == TK_QUESTION) fprintf(docgen->type_file, " ?"); // Further information fprintf(docgen->type_file, "\n\n"); fprintf(docgen->type_file, "%s", (name[0] == '_') ? "Private" : "Public"); if(ast_id(error) == TK_QUESTION) fprintf(docgen->type_file, ", may raise an error"); fprintf(docgen->type_file, ".\n\n"); // Finally the docstring, if any if(ast_id(doc) != TK_NONE) fprintf(docgen->type_file, "%s\n\n", ast_name(doc)); }
// Write a description of the given entity to its own type file. // The containing package is handed in to save looking it up again. static void doc_entity(docgen_t* docgen, ast_t* ast, ast_t* package) { assert(docgen != NULL); assert(docgen->index_file != NULL); assert(docgen->type_file == NULL); assert(ast != NULL); assert(package != NULL); // First open a file size_t tqfn_len; char* tqfn = write_tqfn(ast, NULL, &tqfn_len); docgen->type_file = doc_open_file(docgen, true, tqfn, ".md"); if(docgen->type_file == NULL) return; // Add reference to new file to index file AST_GET_CHILDREN(ast, id, tparams, cap, provides, members, c_api, doc); const char* name = ast_name(id); assert(name != NULL); fprintf(docgen->index_file, " - %s %s: \"%s.md\"\n", ast_get_print(ast), name, tqfn); pool_free_size(tqfn_len, tqfn); // Now we can write the actual documentation for the entity fprintf(docgen->type_file, "%s %s", ast_get_print(ast), name); doc_type_params(docgen, tparams); doc_type_list(docgen, provides, " is ", ", ", ""); fprintf(docgen->type_file, "\n\nIn package \"%s\".\n\n", package_qualified_name(package)); fprintf(docgen->type_file, "%s", (name[0] == '_') ? "Private" : "Public"); const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, ", default capability %s", cap_text); fprintf(docgen->type_file, ".\n\n"); if(ast_id(c_api) == TK_AT) fprintf(docgen->type_file, "May be called from C.\n"); if(ast_id(doc) != TK_NONE) fprintf(docgen->type_file, "%s\n\n", ast_name(doc)); else fprintf(docgen->type_file, "No doc string provided.\n\n"); // Sort members into varieties ast_list_t pub_fields = { NULL, NULL, NULL }; ast_list_t news = { NULL, NULL, NULL }; ast_list_t bes = { NULL, NULL, NULL }; ast_list_t funs = { NULL, NULL, NULL }; for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p)) { switch(ast_id(p)) { case TK_FVAR: case TK_FLET: doc_list_add_named(&pub_fields, p, 0, true, false); break; case TK_NEW: doc_list_add_named(&news, p, 1, true, true); break; case TK_BE: doc_list_add_named(&bes, p, 1, true, true); break; case TK_FUN: doc_list_add_named(&funs, p, 1, true, true); break; default: assert(0); break; } } // Handle member variety lists doc_fields(docgen, &pub_fields, "Public fields"); doc_methods(docgen, &news, "Constructors"); doc_methods(docgen, &bes, "Behaviours"); doc_methods(docgen, &funs, "Functions"); doc_list_free(&pub_fields); doc_list_free(&news); doc_list_free(&bes); doc_list_free(&funs); fclose(docgen->type_file); docgen->type_file = NULL; }
// Write the given type to the current type file static void doc_type(docgen_t* docgen, ast_t* type) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(type != NULL); switch(ast_id(type)) { case TK_NOMINAL: { AST_GET_CHILDREN(type, package, id, tparams, cap, ephemeral); // Find type we reference so we can link to it ast_t* target = (ast_t*)ast_data(type); assert(target != NULL); size_t link_len; char* tqfn = write_tqfn(target, NULL, &link_len); // Links are of the form: [text](target) fprintf(docgen->type_file, "[%s](%s)", ast_name(id), tqfn); pool_free_size(link_len, tqfn); doc_type_list(docgen, tparams, "\\[", ", ", "\\]"); const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, " %s", cap_text); if(ast_id(ephemeral) != TK_NONE) fprintf(docgen->type_file, "%s", ast_get_print(ephemeral)); break; } case TK_UNIONTYPE: doc_type_list(docgen, type, "(", " | ", ")"); break; case TK_ISECTTYPE: doc_type_list(docgen, type, "(", " & ", ")"); break; case TK_TUPLETYPE: doc_type_list(docgen, type, "(", " , ", ")"); break; case TK_TYPEPARAMREF: { AST_GET_CHILDREN(type, id, cap, ephemeral); fprintf(docgen->type_file, "%s", ast_name(id)); const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, " %s", cap_text); if(ast_id(ephemeral) != TK_NONE) fprintf(docgen->type_file, "%s", ast_get_print(ephemeral)); break; } case TK_ARROW: { AST_GET_CHILDREN(type, left, right); doc_type(docgen, left); fprintf(docgen->type_file, "->"); doc_type(docgen, right); break; } case TK_THISTYPE: fprintf(docgen->type_file, "this"); break; case TK_BOXTYPE: fprintf(docgen->type_file, "box"); break; default: assert(0); } }
// Write a description of the given entity to its own type file. static void doc_entity(docgen_t* docgen, ast_t* ast) { assert(docgen != NULL); assert(docgen->index_file != NULL); assert(docgen->package_file != NULL); assert(docgen->test_types != NULL); assert(docgen->public_types != NULL); assert(docgen->private_types != NULL); assert(docgen->type_file == NULL); assert(ast != NULL); // First open a file size_t tqfn_len; char* tqfn = write_tqfn(ast, NULL, &tqfn_len); docgen->type_file = doc_open_file(docgen, true, tqfn, ".md"); if(docgen->type_file == NULL) return; // Add reference to new file to index file AST_GET_CHILDREN(ast, id, tparams, cap, provides, members, c_api, doc); const char* name = ast_name(id); assert(name != NULL); fprintf(docgen->index_file, " - %s %s: \"%s.md\"\n", ast_get_print(ast), name, tqfn); // Add to appropriate package types buffer printbuf_t* buffer = docgen->public_types; if(is_for_testing(name, provides)) buffer = docgen->test_types; else if(name[0] == '_') buffer = docgen->private_types; printbuf(buffer, "* [%s %s](%s.md)\n", ast_get_print(ast), name, tqfn); ponyint_pool_free_size(tqfn_len, tqfn); // Now we can write the actual documentation for the entity fprintf(docgen->type_file, "# %s", name); doc_type_params(docgen, tparams, true); fprintf(docgen->type_file, "\n\n"); if(ast_id(doc) != TK_NONE) fprintf(docgen->type_file, "%s\n\n", ast_name(doc)); // code block fprintf(docgen->type_file, "```pony\n"); fprintf(docgen->type_file, "%s ",ast_get_print(ast)); const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, "%s ", cap_text); fprintf(docgen->type_file, "%s", name); doc_type_params(docgen, tparams, false); doc_type_list(docgen, provides, " is\n ", ",\n ", "", false); fprintf(docgen->type_file, "\n```\n\n"); if (ast_id(ast) != TK_TYPE) doc_type_list(docgen, provides, "#### Implements\n\n* ", "\n* ", "\n\n---\n\n", true); else doc_type_list(docgen, provides, "#### Type Alias For\n\n* ", "\n* ", "\n\n---\n\n", true); // Sort members into varieties ast_list_t pub_fields = { NULL, NULL, NULL }; ast_list_t news = { NULL, NULL, NULL }; ast_list_t pub_bes = { NULL, NULL, NULL }; ast_list_t priv_bes = { NULL, NULL, NULL }; ast_list_t pub_funs = { NULL, NULL, NULL }; ast_list_t priv_funs = { NULL, NULL, NULL }; for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p)) { switch(ast_id(p)) { case TK_FVAR: case TK_FLET: case TK_EMBED: doc_list_add_named(&pub_fields, p, 0, true, false); break; case TK_NEW: doc_list_add_named(&news, p, 1, true, true); break; case TK_BE: doc_list_add_named(&pub_bes, p, 1, true, false); doc_list_add_named(&priv_bes, p, 1, false, true); break; case TK_FUN: doc_list_add_named(&pub_funs, p, 1, true, false); doc_list_add_named(&priv_funs, p, 1, false, true); break; default: assert(0); break; } } // Handle member variety lists doc_methods(docgen, &news, "Constructors"); doc_fields(docgen, &pub_fields, "Public fields"); doc_methods(docgen, &pub_bes, "Public Behaviours"); doc_methods(docgen, &pub_funs, "Public Functions"); doc_methods(docgen, &priv_bes, "Private Behaviours"); doc_methods(docgen, &priv_funs, "Private Functions"); doc_list_free(&pub_fields); doc_list_free(&news); doc_list_free(&pub_bes); doc_list_free(&priv_bes); doc_list_free(&pub_funs); doc_list_free(&priv_funs); fclose(docgen->type_file); docgen->type_file = NULL; }
// Write a description of the given method to the current type file static void doc_method(docgen_t* docgen, ast_t* method) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(method != NULL); AST_GET_CHILDREN(method, cap, id, t_params, params, ret, error, body, doc); const char* name = ast_name(id); assert(name != NULL); // Method fprintf(docgen->type_file, "### %s", name); doc_type_params(docgen, t_params, true); fprintf(docgen->type_file, "\n\n"); // The docstring, if any if(ast_id(doc) != TK_NONE) fprintf(docgen->type_file, "%s\n\n", ast_name(doc)); // SYLVAN'S FULL CODE BLOCK HERE fprintf(docgen->type_file, "```pony\n"); fprintf(docgen->type_file, "%s ", ast_get_print(method)); if(ast_id(method) == TK_FUN || ast_id(method) == TK_NEW) { const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, "%s ", cap_text); } fprintf(docgen->type_file, "%s", name); doc_type_params(docgen, t_params, false); // parameters of the code block code_block_doc_params(docgen, params); // return type if(ast_id(method) == TK_FUN || ast_id(method) == TK_NEW) { fprintf(docgen->type_file, "\n: "); doc_type(docgen, ret, false); if(ast_id(error) == TK_QUESTION) fprintf(docgen->type_file, " ?"); } // close the block fprintf(docgen->type_file, "\n```\n"); // Parameters list_doc_params(docgen, params); // Return value if(ast_id(method) == TK_FUN || ast_id(method) == TK_NEW) { fprintf(docgen->type_file, "#### Returns\n\n"); fprintf(docgen->type_file, "* "); doc_type(docgen, ret, true); if(ast_id(error) == TK_QUESTION) fprintf(docgen->type_file, " ?"); fprintf(docgen->type_file, "\n\n"); } // horizontal rule at the end // separate us from the next method visually fprintf(docgen->type_file, "---\n\n"); }
// Write the given type to the current type file static void doc_type(docgen_t* docgen, ast_t* type, bool generate_links) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(type != NULL); switch(ast_id(type)) { case TK_NOMINAL: { AST_GET_CHILDREN(type, package, id, tparams, cap, ephemeral); // Generate links only if directed to and if the type is not anonymous (as // indicated by a name created by package_hygienic_id). if(generate_links && *ast_name(id) != '$') { // Find type we reference so we can link to it ast_t* target = (ast_t*)ast_data(type); assert(target != NULL); size_t link_len; char* tqfn = write_tqfn(target, NULL, &link_len); // Links are of the form: [text](target) fprintf(docgen->type_file, "[%s](%s)", ast_nice_name(id), tqfn); ponyint_pool_free_size(link_len, tqfn); doc_type_list(docgen, tparams, "\\[", ", ", "\\]", true, false); } else { fprintf(docgen->type_file, "%s", ast_nice_name(id)); doc_type_list(docgen, tparams, "[", ", ", "]", false, false); } const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, " %s", cap_text); if(ast_id(ephemeral) != TK_NONE) fprintf(docgen->type_file, "%s", ast_get_print(ephemeral)); break; } case TK_UNIONTYPE: doc_type_list(docgen, type, "(", " | ", ")", generate_links, true); break; case TK_ISECTTYPE: doc_type_list(docgen, type, "(", " & ", ")", generate_links, false); break; case TK_TUPLETYPE: doc_type_list(docgen, type, "(", " , ", ")", generate_links, false); break; case TK_TYPEPARAMREF: { AST_GET_CHILDREN(type, id, cap, ephemeral); fprintf(docgen->type_file, "%s", ast_nice_name(id)); const char* cap_text = doc_get_cap(cap); if(cap_text != NULL) fprintf(docgen->type_file, " %s", cap_text); if(ast_id(ephemeral) != TK_NONE) fprintf(docgen->type_file, "%s", ast_get_print(ephemeral)); break; } case TK_ARROW: { AST_GET_CHILDREN(type, left, right); doc_type(docgen, left, generate_links); fprintf(docgen->type_file, "->"); doc_type(docgen, right, generate_links); break; } case TK_THISTYPE: fprintf(docgen->type_file, "this"); break; case TK_ISO: case TK_TRN: case TK_REF: case TK_VAL: case TK_BOX: case TK_TAG: fprintf(docgen->type_file, "%s", ast_get_print(type)); break; default: assert(0); } }