char* CFCPerlPod_methods_pod(CFCPerlPod *self, CFCClass *klass) { const char *class_name = CFCClass_get_class_name(klass); char *abstract_pod = CFCUtil_strdup(""); char *methods_pod = CFCUtil_strdup(""); for (size_t i = 0; i < self->num_methods; i++) { NamePod meth_spec = self->methods[i]; CFCMethod *method = CFCClass_method(klass, meth_spec.func); if (!method) { method = CFCClass_method(klass, meth_spec.alias); } if (!method) { CFCUtil_die("Can't find method '%s' in class '%s'", meth_spec.alias, CFCClass_get_class_name(klass)); } char *meth_pod; if (meth_spec.pod) { meth_pod = CFCUtil_sprintf("%s\n", meth_spec.pod); } else { meth_pod = CFCPerlPod_gen_subroutine_pod(self, (CFCFunction*)method, meth_spec.alias, klass, meth_spec.sample, class_name, false); } if (CFCMethod_abstract(method)) { abstract_pod = CFCUtil_cat(abstract_pod, meth_pod, NULL); } else { methods_pod = CFCUtil_cat(methods_pod, meth_pod, NULL); } FREEMEM(meth_pod); } char *pod = CFCUtil_strdup(""); if (strlen(abstract_pod)) { pod = CFCUtil_cat(pod, "=head1 ABSTRACT METHODS\n\n", abstract_pod, NULL); } FREEMEM(abstract_pod); if (strlen(methods_pod)) { pod = CFCUtil_cat(pod, "=head1 METHODS\n\n", methods_pod, NULL); } FREEMEM(methods_pod); return pod; }
CFCMethod* CFCClass_fresh_method(CFCClass *self, const char *sym) { CFCMethod *method = CFCClass_method(self, sym); if (method) { const char *class_name = CFCClass_get_class_name(self); const char *meth_class_name = CFCMethod_get_class_name(method); if (strcmp(class_name, meth_class_name) == 0) { return method; } } return NULL; }
CFCMethod* CFCClass_find_novel_method(CFCClass *self, const char *sym) { if (!self->tree_grown) { CFCUtil_die("Can't call original_method before grow_tree"); } CFCClass *ancestor = self; do { CFCMethod *method = CFCClass_method(ancestor, sym); if (method && CFCMethod_novel(method)) { return method; } } while (NULL != (ancestor = CFCClass_get_parent(ancestor))); return NULL; }
void CFCPerlClass_exclude_method(CFCPerlClass *self, const char *meth_name) { if (!self->client) { CFCUtil_die("Can't exclude_method %s -- can't find client for %s", meth_name, self->class_name); } CFCMethod *method = CFCClass_method(self->client, meth_name); if (!method) { CFCUtil_die("Can't exclude_method %s -- method not found in %s", meth_name, self->class_name); } if (strcmp(CFCMethod_get_class_name(method), self->class_name) != 0) { CFCUtil_die("Can't exclude_method %s -- method not fresh in %s", meth_name, self->class_name); } CFCMethod_exclude_from_host(method); }
void CFCPerlClass_bind_method(CFCPerlClass *self, const char *alias, const char *meth_name) { if (!self->client) { CFCUtil_die("Can't bind_method %s -- can't find client for %s", alias, self->class_name); } CFCMethod *method = CFCClass_method(self->client, meth_name); if (!method) { CFCUtil_die("Can't bind_method %s -- can't find method %s in %s", alias, meth_name, self->class_name); } if (strcmp(CFCMethod_get_class_name(method), self->class_name) != 0) { CFCUtil_die("Can't bind_method %s -- method %s not fresh in %s", alias, meth_name, self->class_name); } CFCMethod_set_host_alias(method, alias); }
static char* S_convert_link(cmark_node *link, CFCClass *doc_class, int header_level) { cmark_node *child = cmark_node_first_child(link); const char *uri = cmark_node_get_url(link); char *text = S_nodes_to_pod(child, doc_class, header_level); char *retval; if (!CFCUri_is_clownfish_uri(uri)) { retval = S_pod_link(text, uri); FREEMEM(text); return retval; } char *new_uri = NULL; char *new_text = NULL; CFCUri *uri_obj = CFCUri_new(uri, doc_class); CFCUriType type = CFCUri_get_type(uri_obj); switch (type) { case CFC_URI_ERROR: { const char *error = CFCUri_get_error(uri_obj); new_text = CFCUtil_sprintf("[%s]", error); break; } case CFC_URI_NULL: // Change all instances of NULL to 'undef' new_text = CFCUtil_strdup("undef"); break; case CFC_URI_CLASS: { CFCClass *klass = CFCUri_get_class(uri_obj); if (klass != doc_class) { const char *class_name = CFCClass_get_name(klass); new_uri = CFCUtil_strdup(class_name); } if (text[0] == '\0') { const char *src = CFCClass_included(klass) ? CFCClass_get_name(klass) : CFCClass_get_struct_sym(klass); new_text = CFCUtil_strdup(src); } break; } case CFC_URI_FUNCTION: case CFC_URI_METHOD: { CFCClass *klass = CFCUri_get_class(uri_obj); const char *name = CFCUri_get_callable_name(uri_obj); // Convert "Err_get_error" to "Clownfish->error". if (strcmp(CFCClass_full_struct_sym(klass), "cfish_Err") == 0 && strcmp(name, "get_error") == 0 ) { new_text = CFCUtil_strdup("Clownfish->error"); break; } char *perl_name = CFCUtil_strdup(name); for (size_t i = 0; perl_name[i] != '\0'; ++i) { perl_name[i] = CFCUtil_tolower(perl_name[i]); } // The Perl POD only contains sections for novel methods. Link // to the class where the method is declared first. if (type == CFC_URI_METHOD) { CFCClass *parent = CFCClass_get_parent(klass); while (parent && CFCClass_method(parent, name)) { klass = parent; parent = CFCClass_get_parent(klass); } } if (klass == doc_class) { new_uri = CFCUtil_sprintf("/%s", perl_name); } else { const char *class_name = CFCClass_get_name(klass); new_uri = CFCUtil_sprintf("%s/%s", class_name, perl_name); } if (text[0] == '\0') { new_text = CFCUtil_sprintf("%s()", perl_name); } FREEMEM(perl_name); break; } case CFC_URI_DOCUMENT: { CFCDocument *doc = CFCUri_get_document(uri_obj); const char *path_part = CFCDocument_get_path_part(doc); new_uri = CFCUtil_global_replace(path_part, CHY_DIR_SEP, "::"); if (text[0] == '\0') { const char *name = CFCDocument_get_name(doc); new_text = CFCUtil_strdup(name); } break; } } if (new_text) { FREEMEM(text); text = new_text; } if (new_uri) { retval = S_pod_link(text, new_uri); FREEMEM(new_uri); FREEMEM(text); } else { retval = text; } CFCBase_decref((CFCBase*)uri_obj); return retval; }
char* CFCPerlPod_gen_subroutine_pod(CFCCallable *func, const char *alias, CFCClass *klass, const char *code_sample, const char *class_name, int is_constructor) { const char *func_name = CFCCallable_get_name(func); // Only allow "public" subs to be exposed as part of the public API. if (!CFCCallable_public(func)) { CFCUtil_die("%s#%s is not public", class_name, func_name); } char *pod = CFCUtil_sprintf("=head2 %s\n\n", alias); // Add code sample. if (!code_sample) { char *auto_sample = S_gen_code_sample(func, alias, klass, is_constructor); pod = CFCUtil_cat(pod, auto_sample, "\n", NULL); FREEMEM(auto_sample); } else { pod = CFCUtil_cat(pod, code_sample, "\n", NULL); } // Get documentation, which may be inherited. CFCDocuComment *docucomment = CFCCallable_get_docucomment(func); if (!docucomment) { CFCClass *parent = klass; while (NULL != (parent = CFCClass_get_parent(parent))) { CFCCallable *parent_func = (CFCCallable*)CFCClass_method(parent, func_name); if (!parent_func) { break; } docucomment = CFCCallable_get_docucomment(parent_func); if (docucomment) { break; } } } if (!docucomment) { return pod; } // Incorporate "description" text from DocuComment. const char *long_doc = CFCDocuComment_get_description(docucomment); if (long_doc && strlen(long_doc)) { char *perlified = CFCPerlPod_md_to_pod(long_doc, klass, 3); pod = CFCUtil_cat(pod, perlified, NULL); FREEMEM(perlified); } // Add params in a list. const char**param_names = CFCDocuComment_get_param_names(docucomment); const char**param_docs = CFCDocuComment_get_param_docs(docucomment); if (param_names[0]) { pod = CFCUtil_cat(pod, "=over\n\n", NULL); for (size_t i = 0; param_names[i] != NULL; i++) { char *perlified = CFCPerlPod_md_to_pod(param_docs[i], klass, 3); pod = CFCUtil_cat(pod, "=item *\n\nB<", param_names[i], "> - ", perlified, NULL); FREEMEM(perlified); } pod = CFCUtil_cat(pod, "=back\n\n", NULL); } // Add return value description, if any. const char *retval_doc = CFCDocuComment_get_retval(docucomment); if (retval_doc && strlen(retval_doc)) { char *perlified = CFCPerlPod_md_to_pod(retval_doc, klass, 3); pod = CFCUtil_cat(pod, "Returns: ", perlified, NULL); FREEMEM(perlified); } return pod; }
char* CFCPerlPod_methods_pod(CFCPerlPod *self, CFCClass *klass) { const char *class_name = CFCClass_get_name(klass); char *abstract_pod = CFCUtil_strdup(""); char *methods_pod = CFCUtil_strdup(""); // Start with methods that don't map to a Clownfish method. for (size_t i = 0; i < self->num_methods; i++) { NamePod meth_spec = self->methods[i]; CFCMethod *method = CFCClass_method(klass, meth_spec.func); if (method) { continue; } if (!meth_spec.pod) { CFCUtil_die("No POD specified for method '%s' in class '%s'", meth_spec.alias, CFCClass_get_name(klass)); } methods_pod = CFCUtil_cat(methods_pod, meth_spec.pod, "\n", NULL); } CFCMethod **fresh_methods = CFCClass_fresh_methods(klass); for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) { CFCMethod *method = fresh_methods[meth_num]; const char *name = CFCMethod_get_name(method); char *meth_pod = NULL; // Try to find custom POD for method. NamePod *meth_spec = NULL; for (size_t j = 0; j < self->num_methods; j++) { NamePod *candidate = &self->methods[j]; const char *other_name = candidate->func; if (other_name && strcmp(other_name, name) == 0) { meth_spec = candidate; break; } } if (meth_spec) { // Found custom POD. if (meth_spec->pod) { meth_pod = CFCUtil_sprintf("%s\n", meth_spec->pod); } else { meth_pod = CFCPerlPod_gen_subroutine_pod((CFCCallable*)method, meth_spec->alias, klass, meth_spec->sample, class_name, false); } } else { // No custom POD found. Add POD for public methods with Perl // bindings. if (!CFCMethod_public(method) || CFCMethod_excluded_from_host(method) || !CFCMethod_can_be_bound(method) ) { continue; } // Only add POD for novel methods and the first implementation // of abstract methods. if (!CFCMethod_novel(method)) { if (CFCMethod_abstract(method)) { continue; } CFCClass *parent = CFCClass_get_parent(klass); CFCMethod *parent_method = CFCClass_method(parent, name); if (!CFCMethod_abstract(parent_method)) { continue; } } char *perl_name = CFCPerlMethod_perl_name(method); meth_pod = CFCPerlPod_gen_subroutine_pod((CFCCallable*)method, perl_name, klass, NULL, class_name, false); FREEMEM(perl_name); } if (CFCMethod_abstract(method)) { abstract_pod = CFCUtil_cat(abstract_pod, meth_pod, NULL); } else { methods_pod = CFCUtil_cat(methods_pod, meth_pod, NULL); } FREEMEM(meth_pod); } char *pod = CFCUtil_strdup(""); if (strlen(abstract_pod)) { pod = CFCUtil_cat(pod, "=head1 ABSTRACT METHODS\n\n", abstract_pod, NULL); } FREEMEM(abstract_pod); if (strlen(methods_pod)) { pod = CFCUtil_cat(pod, "=head1 METHODS\n\n", methods_pod, NULL); } FREEMEM(methods_pod); return pod; }
static void S_resolve(CFCUri *self, const char *parcel, const char *struct_sym, const char *callable) { // Try to find a CFCClass. CFCClass *doc_class = self->doc_class; CFCClass *klass = NULL; if (parcel) { char *full_struct_sym = CFCUtil_sprintf("%s_%s", parcel, struct_sym); klass = CFCClass_fetch_by_struct_sym(full_struct_sym); FREEMEM(full_struct_sym); } else if (struct_sym && doc_class) { const char *prefix = CFCClass_get_prefix(doc_class); char *full_struct_sym = CFCUtil_sprintf("%s%s", prefix, struct_sym); klass = CFCClass_fetch_by_struct_sym(full_struct_sym); FREEMEM(full_struct_sym); } else if (callable) { klass = doc_class; } if (klass) { if (!CFCClass_public(klass)) { CFCUtil_warn("Non-public class '%s' in Clownfish URI: %s", CFCClass_get_struct_sym(klass), self->string); } self->type = CFC_URI_CLASS; self->klass = klass; CFCBase_incref((CFCBase*)klass); if (callable) { if (islower(callable[0])) { CFCFunction *function = CFCClass_function(klass, callable); if (!function) { CFCUtil_warn("Unknown function '%s' in Clownfish URI: %s", callable, self->string); } else if (!CFCFunction_public(function)) { CFCUtil_warn("Non-public function '%s' in Clownfish URI:" " %s", callable, self->string); } self->type = CFC_URI_FUNCTION; self->callable = CFCUtil_strdup(callable); } else { CFCMethod *method = CFCClass_method(klass, callable); if (!method) { CFCUtil_warn("Unknown method '%s' in Clownfish URI: %s", callable, self->string); } else if (!CFCMethod_public(method)) { CFCUtil_warn("Non-public method '%s' in Clownfish URI:" " %s", callable, self->string); } self->type = CFC_URI_METHOD; self->callable = CFCUtil_strdup(callable); } } return; } // Try to find a CFCDocument. if (!parcel && struct_sym && !callable) { CFCDocument *doc = CFCDocument_fetch(struct_sym); if (doc) { self->type = CFC_URI_DOCUMENT; self->document = doc; CFCBase_incref((CFCBase*)doc); return; } } S_set_error(self, "Couldn't resolve Clownfish URI"); }
static void S_bequeath_methods(CFCClass *self) { for (size_t child_num = 0; self->children[child_num] != NULL; child_num++) { CFCClass *child = self->children[child_num]; // Create array of methods, preserving exact order so vtables match up. size_t num_methods = 0; size_t max_methods = self->num_methods + child->num_methods; CFCMethod **methods = (CFCMethod**)MALLOCATE( (max_methods + 1) * sizeof(CFCMethod*)); // Gather methods which child inherits or overrides. for (size_t i = 0; i < self->num_methods; i++) { CFCMethod *method = self->methods[i]; const char *macro_sym = CFCMethod_get_macro_sym(method); CFCMethod *child_method = CFCClass_method(child, macro_sym); if (child_method) { CFCMethod_override(child_method, method); methods[num_methods++] = child_method; } else { methods[num_methods++] = method; } } // Append novel child methods to array. Child methods which were just // marked via CFCMethod_override() a moment ago are skipped. for (size_t i = 0; i < child->num_methods; i++) { CFCMethod *method = child->methods[i]; if (CFCMethod_novel(method)) { methods[num_methods++] = method; } } methods[num_methods] = NULL; // Manage refcounts and assign new array. Transform to final methods // if child class is a final class. if (child->is_final) { for (size_t i = 0; i < num_methods; i++) { if (CFCMethod_final(methods[i])) { CFCBase_incref((CFCBase*)methods[i]); } else { methods[i] = CFCMethod_finalize(methods[i]); } } } else { for (size_t i = 0; i < num_methods; i++) { CFCBase_incref((CFCBase*)methods[i]); } } for (size_t i = 0; i < child->num_methods; i++) { CFCBase_decref((CFCBase*)child->methods[i]); } FREEMEM(child->methods); child->methods = methods; child->num_methods = num_methods; // Pass it all down to the next generation. S_bequeath_methods(child); child->tree_grown = true; } }
char* CFCPerlPod_gen_subroutine_pod(CFCPerlPod *self, CFCFunction *func, const char *alias, CFCClass *klass, const char *code_sample, const char *class_name, int is_constructor) { // Only allow "public" subs to be exposed as part of the public API. if (!CFCFunction_public(func)) { CFCUtil_die("%s#%s is not public", class_name, alias); } CFCParamList *param_list = CFCFunction_get_param_list(func); int num_vars = (int)CFCParamList_num_vars(param_list); char *pod = CFCUtil_sprintf("=head2 %s", alias); // Get documentation, which may be inherited. CFCDocuComment *docucomment = CFCFunction_get_docucomment(func); if (!docucomment) { const char *micro_sym = CFCFunction_micro_sym(func); CFCClass *parent = klass; while (NULL != (parent = CFCClass_get_parent(parent))) { CFCFunction *parent_func = (CFCFunction*)CFCClass_method(parent, micro_sym); if (!parent_func) { break; } docucomment = CFCFunction_get_docucomment(parent_func); if (docucomment) { break; } } } if (!docucomment) { CFCUtil_die("No DocuComment for '%s' in '%s'", alias, class_name); } // Build string summarizing arguments to use in header. if (num_vars > 2 || (is_constructor && num_vars > 1)) { pod = CFCUtil_cat(pod, "( I<[labeled params]> )\n\n", NULL); } else if (num_vars == 2) { // Kill self param. const char *name_list = CFCParamList_name_list(param_list); const char *after_comma = strchr(name_list, ',') + 1; while (isspace(*after_comma)) { after_comma++; } pod = CFCUtil_cat(pod, "(", after_comma, ")\n\n", NULL); } else { // num_args == 1, leave off 'self'. pod = CFCUtil_cat(pod, "()\n\n", NULL); } // Add code sample. if (code_sample && strlen(code_sample)) { pod = CFCUtil_cat(pod, code_sample, "\n", NULL); } // Incorporate "description" text from DocuComment. const char *long_doc = CFCDocuComment_get_description(docucomment); if (long_doc && strlen(long_doc)) { char *perlified = CFCPerlPod_md_to_pod(self, klass, long_doc); pod = CFCUtil_cat(pod, perlified, NULL); FREEMEM(perlified); } // Add params in a list. const char**param_names = CFCDocuComment_get_param_names(docucomment); const char**param_docs = CFCDocuComment_get_param_docs(docucomment); if (param_names[0]) { pod = CFCUtil_cat(pod, "=over\n\n", NULL); for (size_t i = 0; param_names[i] != NULL; i++) { char *perlified = CFCPerlPod_md_to_pod(self, klass, param_docs[i]); pod = CFCUtil_cat(pod, "=item *\n\nB<", param_names[i], "> - ", perlified, NULL); FREEMEM(perlified); } pod = CFCUtil_cat(pod, "=back\n\n", NULL); } // Add return value description, if any. const char *retval_doc = CFCDocuComment_get_retval(docucomment); if (retval_doc && strlen(retval_doc)) { char *perlified = CFCPerlPod_md_to_pod(self, klass, retval_doc); pod = CFCUtil_cat(pod, "Returns: ", perlified, NULL); FREEMEM(perlified); } return pod; }