Updated Branches:
  refs/heads/c-bindings-wip1 b9b16b0a2 -> fd1e1c40b

Generate man pages from DocuComments

Initial implementation in the CFCBind classes. Might be moved to
separate files.


Project: http://git-wip-us.apache.org/repos/asf/lucy/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy/commit/fd1e1c40
Tree: http://git-wip-us.apache.org/repos/asf/lucy/tree/fd1e1c40
Diff: http://git-wip-us.apache.org/repos/asf/lucy/diff/fd1e1c40

Branch: refs/heads/c-bindings-wip1
Commit: fd1e1c40b72bdbe73f3d8632739957cfc14e3087
Parents: c6ef5a7
Author: Nick Wellnhofer <[email protected]>
Authored: Sun Dec 23 16:39:57 2012 +0100
Committer: Nick Wellnhofer <[email protected]>
Committed: Sun Dec 23 22:30:57 2012 +0100

----------------------------------------------------------------------
 clownfish/compiler/c/cfc.c            |    2 +
 clownfish/compiler/src/CFCBindClass.c |  291 ++++++++++++++++++++++++++++
 clownfish/compiler/src/CFCBindClass.h |    5 +
 clownfish/compiler/src/CFCBindCore.c  |   57 ++++++
 clownfish/compiler/src/CFCBindCore.h  |    5 +
 5 files changed, 360 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy/blob/fd1e1c40/clownfish/compiler/c/cfc.c
----------------------------------------------------------------------
diff --git a/clownfish/compiler/c/cfc.c b/clownfish/compiler/c/cfc.c
index cf27569..dbd551b 100644
--- a/clownfish/compiler/c/cfc.c
+++ b/clownfish/compiler/c/cfc.c
@@ -186,6 +186,8 @@ main(int argc, char **argv) {
 
     CFCBindCore_write_all_modified(core_binding, 0);
 
+    CFCBindCore_write_man_pages(core_binding);
+
     CFCBase_decref((CFCBase*)core_binding);
     CFCBase_decref((CFCBase*)hierarchy);
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/fd1e1c40/clownfish/compiler/src/CFCBindClass.c
----------------------------------------------------------------------
diff --git a/clownfish/compiler/src/CFCBindClass.c 
b/clownfish/compiler/src/CFCBindClass.c
index 2fd6105..3ad6178 100644
--- a/clownfish/compiler/src/CFCBindClass.c
+++ b/clownfish/compiler/src/CFCBindClass.c
@@ -23,6 +23,7 @@
 #include "CFCBindMethod.h"
 #include "CFCBase.h"
 #include "CFCClass.h"
+#include "CFCDocuComment.h"
 #include "CFCFunction.h"
 #include "CFCMethod.h"
 #include "CFCParamList.h"
@@ -77,6 +78,25 @@ S_method_defs(CFCBindClass *self);
 static char*
 S_short_names(CFCBindClass *self);
 
+static char*
+S_man_create_synopsis(CFCBindClass *self);
+
+static char*
+S_man_create_functions(CFCBindClass *self);
+
+static char*
+S_man_create_methods(CFCBindClass *self);
+
+static char*
+S_man_create_func(CFCClass *client, CFCFunction *func, const char *short_sym,
+                  const char *full_sym);
+
+static char*
+S_man_create_inheritance(CFCBindClass *self);
+
+static char*
+S_man_escape_content(const char *content);
+
 const static CFCMeta CFCBINDCLASS_META = {
     "Clownfish::CFC::Binding::Core::Class",
     sizeof(CFCBindClass),
@@ -645,3 +665,274 @@ S_short_names(CFCBindClass *self) {
     return short_names;
 }
 
+char*
+CFCBindClass_create_man_page(CFCBindClass *self) {
+    CFCClass   *client     = self->client;
+    const char *class_name = CFCClass_get_class_name(client);
+
+    CFCDocuComment *docucom = CFCClass_get_docucomment(client);
+    if (!docucom) { return NULL; }
+
+    // Get the class's brief description.
+    const char *raw_brief = CFCDocuComment_get_brief(docucom);
+    char *brief = S_man_escape_content(raw_brief);
+
+    // Get the class's long description.
+    const char *raw_description = CFCDocuComment_get_long(docucom);
+    char *description = S_man_escape_content(raw_description);
+
+    // Create SYNOPSIS.
+    char *synopsis = S_man_create_synopsis(self);
+
+    // Create CONSTRUCTORS.
+    char *functions_man = S_man_create_functions(self);
+
+    // Create METHODS, possibly including an ABSTRACT METHODS section.
+    char *methods_man = S_man_create_methods(self);
+
+    // Build an INHERITANCE section describing class ancestry.
+    char *inheritance = S_man_create_inheritance(self);
+
+    // Put it all together.
+    const char pattern[] =
+    ".\\\" Licensed to the Apache Software Foundation (ASF) under one or 
more\n"
+    ".\\\" contributor license agreements.  See the NOTICE file distributed 
with\n"
+    ".\\\" this work for additional information regarding copyright 
ownership.\n"
+    ".\\\" The ASF licenses this file to You under the Apache License, Version 
2.0\n"
+    ".\\\" (the \"License\"); you may not use this file except in compliance 
with\n"
+    ".\\\" the License.  You may obtain a copy of the License at\n"
+    ".\\\"\n"
+    ".\\\"     http://www.apache.org/licenses/LICENSE-2.0\n";
+    ".\\\"\n"
+    ".\\\" Unless required by applicable law or agreed to in writing, 
software\n"
+    ".\\\" distributed under the License is distributed on an \"AS IS\" 
BASIS,\n"
+    ".\\\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
implied.\n"
+    ".\\\" See the License for the specific language governing permissions 
and\n"
+    ".\\\" limitations under the License.\n"
+    ".TH %s 3\n"
+    ".SH NAME\n"
+    "%s - %s\n"
+    "%s"
+    ".SH DESCRIPTION\n"
+    "%s\n"
+    "%s"
+    "%s"
+    "%s";
+
+    size_t size = sizeof(pattern)
+                  + strlen(class_name)
+                  + strlen(class_name)
+                  + strlen(brief)
+                  + strlen(synopsis)
+                  + strlen(description)
+                  + strlen(functions_man)
+                  + strlen(methods_man)
+                  + strlen(inheritance)
+                  + 20;
+
+    char *pod = (char*)MALLOCATE(size);
+    sprintf(pod, pattern, class_name, class_name, brief, synopsis, description,
+            functions_man, methods_man, inheritance);
+
+    FREEMEM(brief);
+    FREEMEM(synopsis);
+    FREEMEM(description);
+    FREEMEM(functions_man);
+    FREEMEM(methods_man);
+    FREEMEM(inheritance);
+
+    return pod;
+}
+
+static char*
+S_man_create_synopsis(CFCBindClass *self) {
+    return CFCUtil_strdup("");
+}
+
+static char*
+S_man_create_functions(CFCBindClass *self) {
+    CFCClass     *client    = self->client;
+    CFCFunction **functions = CFCClass_functions(client);
+    const char  *class_name = CFCClass_get_class_name(client);
+    char        *result     = CFCUtil_strdup(".SH FUNCTIONS\n");
+
+    for (int func_num = 0; functions[func_num] != NULL; func_num++) {
+        CFCFunction *func = functions[func_num];
+        if (!CFCFunction_public(func)) { continue; }
+
+        const char *micro_sym     = CFCFunction_micro_sym(func);
+        const char *full_func_sym = CFCFunction_full_func_sym(func);
+
+        char *redman = S_man_create_func(client, func, micro_sym,
+                                         full_func_sym);
+        result = CFCUtil_cat(result, redman, NULL);
+        FREEMEM(redman);
+    }
+
+    return result;
+}
+
+static char*
+S_man_create_methods(CFCBindClass *self) {
+    CFCClass    *client        = self->client;
+    CFCMethod  **fresh_methods = CFCClass_fresh_methods(client);
+    const char  *class_name    = CFCClass_get_class_name(client);
+    char        *result        = CFCUtil_strdup(".SH METHODS\n");
+
+    for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) {
+        CFCMethod *method = fresh_methods[meth_num];
+        if (!CFCMethod_public(method)) { continue; }
+
+        const char *macro_sym = CFCMethod_get_macro_sym(method);
+
+        size_t sym_len = CFCMethod_full_method_sym(method, NULL, NULL, 0);
+        char *full_method_sym = (char*)MALLOCATE(sym_len);
+        CFCMethod_full_method_sym(method, NULL, full_method_sym, sym_len);
+
+        char *method_man = S_man_create_func(client, (CFCFunction*)method,
+                                             macro_sym, full_method_sym);
+        result = CFCUtil_cat(result, method_man, NULL);
+
+        FREEMEM(method_man);
+        FREEMEM(full_method_sym);
+    }
+
+    FREEMEM(fresh_methods);
+
+    return result;
+}
+
+static char*
+S_man_create_func(CFCClass *client, CFCFunction *func, const char *short_sym,
+                  const char *full_sym) {
+    CFCType *return_type
+        = CFCFunction_get_return_type(func);
+    const char *return_type_c = CFCType_to_c(return_type);
+
+    CFCParamList *param_list
+        = CFCFunction_get_param_list(func);
+    const char *param_list_c = CFCParamList_to_c(param_list);
+
+    char *result
+        = CFCUtil_cat(CFCUtil_strdup(".TP\n.B "), short_sym, "\n.na\n",
+                      return_type_c, "\n.br\n.BR \"", full_sym, "\" \"(",
+                      param_list_c, ");\"\n.ad\n", NULL);
+
+    // Get documentation, which may be inherited.
+    CFCDocuComment *docucomment = CFCFunction_get_docucomment(func);
+    if (!docucomment) {
+        const char *micro_sym = CFCFunction_micro_sym(func);
+        CFCClass *parent = client;
+        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) {
+        // Description
+        const char *raw_desc = CFCDocuComment_get_description(docucomment);
+        char *desc = S_man_escape_content(raw_desc);
+        result = CFCUtil_cat(result, ".IP\n", desc, "\n", NULL);
+        FREEMEM(desc);
+
+        // Params
+        const char **param_names
+            = CFCDocuComment_get_param_names(docucomment);
+        const char **param_docs
+            = CFCDocuComment_get_param_docs(docucomment);
+        if (param_names[0]) {
+            result = CFCUtil_cat(result, ".RS\n", NULL);
+            for (size_t i = 0; param_names[i] != NULL; i++) {
+                char *doc = S_man_escape_content(param_docs[i]);
+                result = CFCUtil_cat(result, ".TP\n.I ", param_names[i],
+                                     "\n", doc, "\n", NULL);
+                FREEMEM(doc);
+            }
+            result = CFCUtil_cat(result, ".RE\n", NULL);
+        }
+
+        // Return value
+        const char *retval_doc = CFCDocuComment_get_retval(docucomment);
+        if (retval_doc && strlen(retval_doc)) {
+            char *doc = S_man_escape_content(retval_doc);
+            result = CFCUtil_cat(result, ".IP\n.B Returns:\n", doc, "\n",
+                                 NULL);
+            FREEMEM(doc);
+        }
+    }
+
+    return result;
+}
+
+static char*
+S_man_create_inheritance(CFCBindClass *self) {
+    CFCClass *client   = self->client;
+    CFCClass *ancestor = CFCClass_get_parent(client);
+    char     *result   = CFCUtil_strdup("");
+
+    if (!ancestor) { return result; }
+
+    const char *class_name = CFCClass_get_class_name(client);
+    result = CFCUtil_cat(result, ".SH INHERITANCE\n", class_name, NULL);
+    while (ancestor) {
+        const char *ancestor_name = CFCClass_get_class_name(ancestor);
+        result = CFCUtil_cat(result, " is a ", ancestor_name, NULL);
+        ancestor = CFCClass_get_parent(ancestor);
+    }
+    result = CFCUtil_cat(result, ".\n", NULL);
+
+    return result;
+}
+
+static char*
+S_man_escape_content(const char *content) {
+    size_t  result_len = 0;
+    size_t  result_cap = strlen(content) + 20;
+    char   *result     = (char*)MALLOCATE(result_cap + 1);
+    int     fill       = 1;
+
+    for (size_t i = 0; content[i]; i++) {
+        char c[8];
+        int  num_chars = 1;
+
+        c[0] = content[i];
+
+        switch (c[0]) {
+            case '\\':
+                c[1] = 'e';
+                num_chars = 2;
+                break;
+            case '-':
+                c[0] = '\\';
+                c[1] = '-';
+                num_chars = 2;
+                break;
+            case '\n':
+                if (content[i+1] == '.') {
+                    memcpy(c + num_chars, "\\&", 2);
+                    num_chars += 2;
+                }
+                break;
+            default:
+                break;
+        }
+
+        if (result_len + num_chars > result_cap) {
+            result_cap += 50;
+            result = (char*)REALLOCATE(result, result_cap + 1);
+        }
+
+        memcpy(result + result_len, c, num_chars);
+        result_len += num_chars;
+    }
+
+    result[result_len] = '\0';
+
+    return result;
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/fd1e1c40/clownfish/compiler/src/CFCBindClass.h
----------------------------------------------------------------------
diff --git a/clownfish/compiler/src/CFCBindClass.h 
b/clownfish/compiler/src/CFCBindClass.h
index c84954e..e14c94e 100644
--- a/clownfish/compiler/src/CFCBindClass.h
+++ b/clownfish/compiler/src/CFCBindClass.h
@@ -59,6 +59,11 @@ CFCBindClass_to_c_data(CFCBindClass *self);
 char*
 CFCBindClass_spec_def(CFCBindClass *self);
 
+/** Return the man page for the class.
+ */
+char*
+CFCBindClass_create_man_page(CFCBindClass *self);
+
 #ifdef __cplusplus
 }
 #endif

http://git-wip-us.apache.org/repos/asf/lucy/blob/fd1e1c40/clownfish/compiler/src/CFCBindCore.c
----------------------------------------------------------------------
diff --git a/clownfish/compiler/src/CFCBindCore.c 
b/clownfish/compiler/src/CFCBindCore.c
index 998dada..84c6165 100644
--- a/clownfish/compiler/src/CFCBindCore.c
+++ b/clownfish/compiler/src/CFCBindCore.c
@@ -436,3 +436,60 @@ S_write_parcel_c(CFCBindCore *self) {
     FREEMEM(vt_specs);
 }
 
+void
+CFCBindCore_write_man_pages(CFCBindCore *self) {
+    CFCHierarchy  *hierarchy = self->hierarchy;
+    CFCClass     **ordered   = CFCHierarchy_ordered_classes(hierarchy);
+
+    size_t num_classes = 0;
+    for (size_t i = 0; ordered[i] != NULL; i++) {
+        CFCClass *klass = ordered[i];
+        if (!CFCClass_included(klass)) { ++num_classes; }
+    }
+    char **man_pages = (char**)CALLOCATE(num_classes, sizeof(char*));
+
+    // Generate man pages, but don't write.  That way, if there's an error
+    // while generating the pages, we leak memory but don't clutter up the 
file 
+    // system.
+    for (size_t i = 0, j = 0; ordered[i] != NULL; i++) {
+        CFCClass *klass = ordered[i];
+        if (CFCClass_included(klass)) { continue; }
+
+        CFCBindClass *class_binding = CFCBindClass_new(klass);
+        char *man_page = CFCBindClass_create_man_page(class_binding);
+        man_pages[j++] = man_page;
+        CFCBase_decref((CFCBase*)class_binding);
+    }
+
+    const char *dest = CFCHierarchy_get_dest(hierarchy);
+    char *man3_path
+        = CFCUtil_cat(CFCUtil_strdup(dest), CFCUTIL_PATH_SEP, "man",
+                      CFCUTIL_PATH_SEP, "man3", NULL);
+    if (!CFCUtil_is_dir(man3_path)) {
+        CFCUtil_make_path(man3_path);
+        if (!CFCUtil_is_dir(man3_path)) {
+            CFCUtil_die("Can't make path %s", man3_path);
+        }
+    }
+
+    // Write out any man pages that have changed.
+    for (size_t i = 0, j = 0; ordered[i] != NULL; i++) {
+        CFCClass *klass = ordered[i];
+        if (CFCClass_included(klass)) { continue; }
+
+        char *man_page = man_pages[j++];
+        if (!man_page) { continue; }
+
+        const char *class_name = CFCClass_get_class_name(klass);
+        char *filename
+            = CFCUtil_cat(CFCUtil_strdup(man3_path), CFCUTIL_PATH_SEP,
+                          class_name, ".3", NULL);
+        CFCUtil_write_if_changed(filename, man_page, strlen(man_page));
+        FREEMEM(filename);
+        FREEMEM(man_page);
+    }
+
+    FREEMEM(man3_path);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/fd1e1c40/clownfish/compiler/src/CFCBindCore.h
----------------------------------------------------------------------
diff --git a/clownfish/compiler/src/CFCBindCore.h 
b/clownfish/compiler/src/CFCBindCore.h
index 2b667f7..963c74e 100644
--- a/clownfish/compiler/src/CFCBindCore.h
+++ b/clownfish/compiler/src/CFCBindCore.h
@@ -57,6 +57,11 @@ CFCBindCore_destroy(CFCBindCore *self);
 int
 CFCBindCore_write_all_modified(CFCBindCore *self, int modified);
 
+/** Write all man pages.
+ */
+void
+CFCBindCore_write_man_pages(CFCBindCore *self);
+
 #ifdef __cplusplus
 }
 #endif

Reply via email to