// Write the given list of parameters to the current type file, with // surrounding (). If the given list is empty () is still written. static void doc_params(docgen_t* docgen, ast_t* params) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(params != NULL); fprintf(docgen->type_file, "("); ast_t* first = ast_child(params); for(ast_t* param = first; param != NULL; param = ast_sibling(param)) { if(param != first) fprintf(docgen->type_file, ", "); AST_GET_CHILDREN(param, id, type, def_val); const char* name = ast_name(id); assert(name != NULL); if(ast_id(def_val) != TK_NONE) fprintf(docgen->type_file, "optional "); fprintf(docgen->type_file, "%s: ", name); doc_type(docgen, type); } fprintf(docgen->type_file, ")"); }
// Write the given list of types to the current type file, with the specified // preamble, separator and psotamble text. If the list is empty nothing is // written. static void doc_type_list(docgen_t* docgen, ast_t* list, const char* preamble, const char* separator, const char* postamble) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(list != NULL); assert(preamble != NULL); assert(separator != NULL); assert(postamble != NULL); if(ast_id(list) == TK_NONE) return; fprintf(docgen->type_file, "%s", preamble); for(ast_t* p = ast_child(list); p != NULL; p = ast_sibling(p)) { doc_type(docgen, p); if(ast_sibling(p) != NULL) fprintf(docgen->type_file, "%s", separator); } fprintf(docgen->type_file, "%s", postamble); }
// Write the given list of fields to the current type file. // The given title text is used as a section header. // If the field list is empty nothing is written. static void doc_fields(docgen_t* docgen, ast_list_t* fields, const char* title) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(fields != NULL); assert(title != NULL); if(fields->next == NULL) // No fields return; fprintf(docgen->type_file, "# %s\n\n", title); for(ast_list_t* p = fields->next; p != NULL; p = p->next) { ast_t* field = p->ast; assert(field != NULL); AST_GET_CHILDREN(field, id, type, init); const char* name = ast_name(id); assert(name != NULL); // Don't want ast_get_print() as that will give us flet or fvar fprintf(docgen->type_file, "* %s %s: ", (ast_id(field) == TK_VAR) ? "var" : "let", name); doc_type(docgen, type); fprintf(docgen->type_file, "\n"); } }
// Write the given list of types to the current type file, with the specified // preamble, separator and psotamble text. If the list is empty nothing is // written. static void doc_type_list(docgen_t* docgen, ast_t* list, const char* preamble, const char* separator, const char* postamble, bool generate_links, bool line_breaks) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(list != NULL); assert(preamble != NULL); assert(separator != NULL); assert(postamble != NULL); if(ast_id(list) == TK_NONE) return; fprintf(docgen->type_file, "%s", preamble); int listItemCount = 0; for(ast_t* p = ast_child(list); p != NULL; p = ast_sibling(p)) { doc_type(docgen, p, generate_links); if(ast_sibling(p) != NULL) { fprintf(docgen->type_file, "%s", separator); if (line_breaks) { if (listItemCount++ == 2) { fprintf(docgen->type_file, "\n "); listItemCount = 0; } } } } fprintf(docgen->type_file, "%s", postamble); }
// 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 the given list of type parameters to the current type file, with // surrounding []. If the given list is empty nothing is written. static void doc_type_params(docgen_t* docgen, ast_t* t_params, bool generate_links) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(t_params != NULL); if(ast_id(t_params) == TK_NONE) return; assert(ast_id(t_params) == TK_TYPEPARAMS); if(generate_links) fprintf(docgen->type_file, "\\["); else fprintf(docgen->type_file, "["); ast_t* first = ast_child(t_params); for(ast_t* t_param = first; t_param != NULL; t_param = ast_sibling(t_param)) { if(t_param != first) fprintf(docgen->type_file, ", "); AST_GET_CHILDREN(t_param, id, constraint, default_type); const char* name = ast_name(id); assert(name != NULL); if(ast_id(default_type) != TK_NONE) fprintf(docgen->type_file, "optional "); fprintf(docgen->type_file, "%s: ", name); if(ast_id(constraint) != TK_NONE) doc_type(docgen, constraint, generate_links); else fprintf(docgen->type_file, "no constraint"); } if(generate_links) fprintf(docgen->type_file, "\\]"); else fprintf(docgen->type_file, "]"); }
static void list_doc_params(docgen_t* docgen, ast_t* params) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(params != NULL); ast_t* first = ast_child(params); for(ast_t* param = first; param != NULL; param = ast_sibling(param)) { if(param == first) fprintf(docgen->type_file, "#### Parameters\n\n"); fprintf(docgen->type_file, "* "); AST_GET_CHILDREN(param, id, type, def_val); const char* name = ast_name(id); assert(name != NULL); fprintf(docgen->type_file, " %s: ", name); doc_type(docgen, type, true); // if we have a default value, add it to the documentation if(ast_id(def_val) != TK_NONE) { switch(ast_id(def_val)) { case TK_STRING: fprintf(docgen->type_file, "= \"%s\"", ast_get_print(def_val)); break; default: fprintf(docgen->type_file, " = %s", ast_get_print(def_val)); break; } } fprintf(docgen->type_file, "\n"); } fprintf(docgen->type_file, "\n"); }
// Write the given list of fields to the current type file. // The given title text is used as a section header. // If the field list is empty nothing is written. static void doc_fields(docgen_t* docgen, ast_list_t* fields, const char* title) { assert(docgen != NULL); assert(docgen->type_file != NULL); assert(fields != NULL); assert(title != NULL); if(fields->next == NULL) // No fields return; fprintf(docgen->type_file, "## %s\n\n", title); for(ast_list_t* p = fields->next; p != NULL; p = p->next) { ast_t* field = p->ast; assert(field != NULL); AST_GET_CHILDREN(field, id, type, init); const char* name = ast_name(id); assert(name != NULL); // Don't want ast_get_print() as that will give us flet or fvar const char* ftype = NULL; switch(ast_id(field)) { case TK_FVAR: ftype = "var"; break; case TK_FLET: ftype = "let"; break; case TK_EMBED: ftype = "embed"; break; default: assert(0); } fprintf(docgen->type_file, "* %s %s: ", ftype, name); doc_type(docgen, type, true); fprintf(docgen->type_file, "\n\n---\n\n"); } }
// 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 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); } }