Create HTML documentation for C API

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

Branch: refs/heads/markdown
Commit: fca91dcec74c99046a94ee9072f5525c7fc36e2b
Parents: a4c0106
Author: Nick Wellnhofer <[email protected]>
Authored: Sun Nov 9 23:32:16 2014 +0100
Committer: Nick Wellnhofer <[email protected]>
Committed: Sun Nov 23 18:35:49 2014 +0100

----------------------------------------------------------------------
 compiler/c/cfc.c        |   1 +
 compiler/src/CFCC.c     | 186 ++++++++++---
 compiler/src/CFCC.h     |   5 +
 compiler/src/CFCCHTML.c | 602 +++++++++++++++++++++++++++++++++++++++++++
 compiler/src/CFCCHTML.h |  42 +++
 5 files changed, 807 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/fca91dce/compiler/c/cfc.c
----------------------------------------------------------------------
diff --git a/compiler/c/cfc.c b/compiler/c/cfc.c
index ae8828f..6460e53 100644
--- a/compiler/c/cfc.c
+++ b/compiler/c/cfc.c
@@ -239,6 +239,7 @@ main(int argc, char **argv) {
     CFCC_write_hostdefs(c_binding);
     if (args.num_source_dirs != 0) {
         CFCC_write_callbacks(c_binding);
+        CFCC_write_html_docs(c_binding);
         CFCC_write_man_pages(c_binding);
     }
 

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/fca91dce/compiler/src/CFCC.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCC.c b/compiler/src/CFCC.c
index 067e84e..6bd7165 100644
--- a/compiler/src/CFCC.c
+++ b/compiler/src/CFCC.c
@@ -16,16 +16,21 @@
 
 #include "charmony.h"
 
+#include <ctype.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 
 #define CFC_NEED_BASE_STRUCT_DEF
 #include "CFCBase.h"
 #include "CFCC.h"
+#include "CFCCHTML.h"
 #include "CFCCMan.h"
 #include "CFCClass.h"
 #include "CFCHierarchy.h"
 #include "CFCMethod.h"
+#include "CFCParcel.h"
+#include "CFCSymbol.h"
 #include "CFCUtil.h"
 
 struct CFCC {
@@ -46,6 +51,12 @@ static const CFCMeta CFCC_META = {
 static char*
 S_callback_decs(CFCClass *klass);
 
+static int
+S_compare_class_name(const void *va, const void *vb);
+
+static void
+S_write_html_docs(CFCC *self, CFCParcel *parcel, CFCClass **classes);
+
 CFCC*
 CFCC_new(CFCHierarchy *hierarchy, const char *header, const char *footer) {
     CFCC *self = (CFCC*)CFCBase_allocate(&CFCC_META);
@@ -145,6 +156,151 @@ S_callback_decs(CFCClass *klass) {
 }
 
 void
+CFCC_write_hostdefs(CFCC *self) {
+    const char pattern[] =
+        "%s\n"
+        "\n"
+        "#ifndef H_CFISH_HOSTDEFS\n"
+        "#define H_CFISH_HOSTDEFS 1\n"
+        "\n"
+        "#define CFISH_OBJ_HEAD \\\n"
+        "    size_t refcount;\n"
+        "\n"
+        "#endif /* H_CFISH_HOSTDEFS */\n"
+        "\n"
+        "%s\n";
+    char *content
+        = CFCUtil_sprintf(pattern, self->c_header, self->c_footer);
+
+    // Unlink then write file.
+    const char *inc_dest = CFCHierarchy_get_include_dest(self->hierarchy);
+    char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "cfish_hostdefs.h",
+                                     inc_dest);
+    remove(filepath);
+    CFCUtil_write_file(filepath, content, strlen(content));
+    FREEMEM(filepath);
+
+    FREEMEM(content);
+}
+
+void
+CFCC_write_html_docs(CFCC *self) {
+    CFCHierarchy  *hierarchy = self->hierarchy;
+    CFCClass     **ordered   = CFCHierarchy_ordered_classes(hierarchy);
+    CFCParcel    **parcels   = CFCParcel_all_parcels();
+
+    size_t num_classes = 0;
+    for (size_t i = 0; ordered[i] != NULL; i++) {
+        ++num_classes;
+    }
+
+    qsort(ordered, num_classes, sizeof(*ordered), S_compare_class_name);
+
+    for (size_t i = 0; parcels[i] != NULL; ++i) {
+        CFCParcel *parcel = parcels[i];
+        if (!CFCParcel_included(parcel)) {
+            S_write_html_docs(self, parcel, ordered);
+        }
+    }
+
+    FREEMEM(ordered);
+}
+
+static int
+S_compare_class_name(const void *va, const void *vb) {
+    const char *a = CFCClass_get_class_name(*(CFCClass**)va);
+    const char *b = CFCClass_get_class_name(*(CFCClass**)vb);
+
+    return strcmp(a, b);
+}
+
+static void
+S_write_html_docs(CFCC *self, CFCParcel *parcel, CFCClass **classes) {
+    CFCHierarchy *hierarchy = self->hierarchy;
+    const char   *prefix    = CFCParcel_get_prefix(parcel);
+
+    size_t num_classes = 0;
+    for (size_t i = 0; classes[i] != NULL; i++) {
+        CFCClass *klass = classes[i];
+        if (strcmp(CFCClass_get_prefix(klass), prefix) != 0
+            || !CFCSymbol_public((CFCSymbol*)klass)
+        ) {
+            continue;
+        }
+        ++num_classes;
+    }
+
+    if (num_classes == 0) { return; }
+
+    size_t max_docs = num_classes + 1;
+    char **html_docs = (char**)CALLOCATE(max_docs, sizeof(char*));
+    size_t num_docs = 0;
+
+    html_docs[num_docs++] = CFCCHTML_create_index_doc(parcel, classes);
+
+    // Generate HTML docs, 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; classes[i] != NULL; i++) {
+        CFCClass *klass = classes[i];
+        if (strcmp(CFCClass_get_prefix(klass), prefix) != 0
+            || !CFCSymbol_public((CFCSymbol*)klass)
+        ) {
+            continue;
+        }
+        html_docs[num_docs++] = CFCCHTML_create_html_doc(klass);
+    }
+
+    const char *dest = CFCHierarchy_get_dest(hierarchy);
+    char *doc_path
+        = CFCUtil_sprintf("%s" CHY_DIR_SEP "share" CHY_DIR_SEP "doc"
+                          CHY_DIR_SEP "clownfish", dest);
+    if (!CFCUtil_is_dir(doc_path)) {
+        CFCUtil_make_path(doc_path);
+        if (!CFCUtil_is_dir(doc_path)) {
+            CFCUtil_die("Can't make path %s", doc_path);
+        }
+    }
+
+    num_docs = 0;
+
+    {
+        char *html_doc = html_docs[num_docs++];
+        char *nickname = CFCUtil_strdup(CFCParcel_get_nickname(parcel));
+        for (size_t i = 0; nickname[i]; ++i) {
+            nickname[i] = tolower(nickname[i]);
+        }
+        char *filename = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s.html", doc_path,
+                                         nickname);
+        CFCUtil_write_if_changed(filename, html_doc, strlen(html_doc));
+        FREEMEM(filename);
+        FREEMEM(nickname);
+        FREEMEM(html_doc);
+    }
+
+    // Write out any HTML docs that have changed.
+    for (size_t i = 0; classes[i] != NULL; i++) {
+        CFCClass *klass = classes[i];
+        if (strcmp(CFCClass_get_prefix(klass), prefix) != 0
+            || !CFCSymbol_public((CFCSymbol*)klass)
+        ) {
+            continue;
+        }
+
+        char *html_doc = html_docs[num_docs++];
+        const char *full_struct_sym = CFCClass_full_struct_sym(klass);
+        char *filename = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s.html", doc_path,
+                                         full_struct_sym);
+        CFCUtil_write_if_changed(filename, html_doc, strlen(html_doc));
+        FREEMEM(filename);
+        FREEMEM(html_doc);
+    }
+
+    FREEMEM(doc_path);
+    FREEMEM(html_docs);
+}
+
+void
 CFCC_write_man_pages(CFCC *self) {
     CFCHierarchy  *hierarchy = self->hierarchy;
     CFCClass     **ordered   = CFCHierarchy_ordered_classes(hierarchy);
@@ -157,7 +313,7 @@ CFCC_write_man_pages(CFCC *self) {
     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 
+    // 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];
@@ -201,32 +357,4 @@ CFCC_write_man_pages(CFCC *self) {
     FREEMEM(ordered);
 }
 
-void
-CFCC_write_hostdefs(CFCC *self) {
-    const char pattern[] =
-        "%s\n"
-        "\n"
-        "#ifndef H_CFISH_HOSTDEFS\n"
-        "#define H_CFISH_HOSTDEFS 1\n"
-        "\n"
-        "#define CFISH_OBJ_HEAD \\\n"
-        "    size_t refcount;\n"
-        "\n"
-        "#endif /* H_CFISH_HOSTDEFS */\n"
-        "\n"
-        "%s\n";
-    char *content
-        = CFCUtil_sprintf(pattern, self->c_header, self->c_footer);
-
-    // Unlink then write file.
-    const char *inc_dest = CFCHierarchy_get_include_dest(self->hierarchy);
-    char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "cfish_hostdefs.h",
-                                     inc_dest);
-    remove(filepath);
-    CFCUtil_write_file(filepath, content, strlen(content));
-    FREEMEM(filepath);
-
-    FREEMEM(content);
-}
-
 

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/fca91dce/compiler/src/CFCC.h
----------------------------------------------------------------------
diff --git a/compiler/src/CFCC.h b/compiler/src/CFCC.h
index 6f665ed..c52ebae 100644
--- a/compiler/src/CFCC.h
+++ b/compiler/src/CFCC.h
@@ -54,6 +54,11 @@ CFCC_write_callbacks(CFCC *self);
 void
 CFCC_write_hostdefs(CFCC *self);
 
+/** Write the HTML documentation.
+ */
+void
+CFCC_write_html_docs(CFCC *self);
+
 /** Write all man pages.
  */
 void

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/fca91dce/compiler/src/CFCCHTML.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCCHTML.c b/compiler/src/CFCCHTML.c
new file mode 100644
index 0000000..12ebbab
--- /dev/null
+++ b/compiler/src/CFCCHTML.c
@@ -0,0 +1,602 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+
+#include <cmark.h>
+
+#include "charmony.h"
+#include "CFCCHTML.h"
+#include "CFCClass.h"
+#include "CFCDocuComment.h"
+#include "CFCFunction.h"
+#include "CFCMethod.h"
+#include "CFCParamList.h"
+#include "CFCParcel.h"
+#include "CFCSymbol.h"
+#include "CFCType.h"
+#include "CFCUtil.h"
+#include "CFCVariable.h"
+
+#ifndef true
+    #define true 1
+    #define false 0
+#endif
+
+// TODO: Pass HTML header and footer as option.
+
+static const char html_header[] =
+    "<!DOCTYPE html>\n"
+    "<html>\n"
+    "<head>\n"
+    "<meta name=\"viewport\" content=\"width=device-width\" />\n"
+    "<style type=\"text/css\">\n"
+    "body {\n"
+    "    font-family: sans-serif;\n"
+    "    font-size: 0.85em;\n"
+    "    max-width: 640px;\n"
+    "}\n"
+    "dt {\n"
+    "    font-weight: bold;\n"
+    "}\n"
+    "pre {\n"
+    "    border: 1px solid #000;\n"
+    "    padding: 0.2em 0.4em;\n"
+    "    background: #f6f6f6;\n"
+    "    font-size: 1.2em;\n"
+    "}\n"
+    "code {\n"
+    "    font-family: \"Consolas\", \"Menlo\", monospace;\n"
+    "}\n"
+    "</style>\n"
+    "</head>\n"
+    "<body>\n";
+
+static const char html_footer[] =
+    "</body>\n"
+    "</html>\n";
+
+static char*
+S_html_create_name(CFCClass *klass);
+
+static char*
+S_html_create_synopsis(CFCClass *klass);
+
+static char*
+S_html_create_description(CFCClass *klass);
+
+static char*
+S_html_create_functions(CFCClass *klass);
+
+static char*
+S_html_create_methods(CFCClass *klass);
+
+static char*
+S_html_create_inherited_methods(CFCClass *klass);
+
+static char*
+S_html_create_func(CFCClass *klass, CFCFunction *func, const char *short_sym,
+                  const char *full_sym);
+
+static char*
+S_html_create_param_list(CFCClass *klass, CFCFunction *func,
+                         const char *full_sym);
+
+static char*
+S_html_create_inheritance(CFCClass *klass);
+
+static char*
+S_md_to_html(const char *md);
+
+static void
+S_convert_uris(cmark_node *node);
+
+static void
+S_convert_uri(cmark_node *link);
+
+static char*
+S_type_to_html(CFCClass *klass, CFCType *type);
+
+char*
+CFCCHTML_create_index_doc(CFCParcel *parcel, CFCClass **classes) {
+    const char *prefix      = CFCParcel_get_prefix(parcel);
+    const char *parcel_name = CFCParcel_get_name(parcel);
+    char *class_list = CFCUtil_strdup("");
+
+    for (size_t i = 0; classes[i] != NULL; i++) {
+        CFCClass *klass = classes[i];
+        if (strcmp(CFCClass_get_prefix(klass), prefix) != 0
+            || !CFCSymbol_public((CFCSymbol*)klass)
+        ) {
+            continue;
+        }
+
+        const char *struct_sym = CFCClass_full_struct_sym(klass);
+        const char *class_name = CFCClass_get_class_name(klass);
+        class_list
+            = CFCUtil_cat(class_list, "<li><a href=\"", struct_sym, ".html\">",
+                          class_name, "</a></li>\n", NULL);
+    }
+
+    const char pattern[] =
+        "%s"
+        "<h1>C API documentation for %s</h1>\n"
+        "<ul>\n"
+        "%s"
+        "</ul>\n"
+        "%s";
+    char *html_doc
+        = CFCUtil_sprintf(pattern, html_header, parcel_name, class_list,
+                          html_footer);
+
+    FREEMEM(class_list);
+
+    return html_doc;
+}
+
+char*
+CFCCHTML_create_html_doc(CFCClass *klass) {
+    const char *class_name = CFCClass_get_class_name(klass);
+
+    // Create NAME.
+    char *name = S_html_create_name(klass);
+
+    // Create SYNOPSIS.
+    char *synopsis = S_html_create_synopsis(klass);
+
+    // Create DESCRIPTION.
+    char *description = S_html_create_description(klass);
+
+    // Create CONSTRUCTORS.
+    char *functions_html = S_html_create_functions(klass);
+
+    // Create METHODS, possibly including an ABSTRACT METHODS section.
+    char *methods_html = S_html_create_methods(klass);
+
+    // Build an INHERITANCE section describing class ancestry.
+    char *inheritance = S_html_create_inheritance(klass);
+
+    // Put it all together.
+    const char pattern[] =
+        "%s"
+        "<h1>%s</h1>\n"
+        "%s"
+        "%s"
+        "%s"
+        "%s"
+        "%s"
+        "%s"
+        "%s";
+    char *html_doc
+        = CFCUtil_sprintf(pattern, html_header, class_name, name, synopsis,
+                          description, functions_html, methods_html,
+                          inheritance, html_footer);
+
+    FREEMEM(name);
+    FREEMEM(synopsis);
+    FREEMEM(description);
+    FREEMEM(functions_html);
+    FREEMEM(methods_html);
+    FREEMEM(inheritance);
+
+    return html_doc;
+}
+
+static char*
+S_html_create_name(CFCClass *klass) {
+    const char     *class_name = CFCClass_get_class_name(klass);
+    char           *md         = CFCUtil_strdup(class_name);;
+    CFCDocuComment *docucom    = CFCClass_get_docucomment(klass);
+
+    if (docucom) {
+        const char *raw_brief = CFCDocuComment_get_brief(docucom);
+        if (raw_brief && raw_brief[0] != '\0') {
+            md = CFCUtil_cat(md, " - ", raw_brief, NULL);
+        }
+    }
+
+    char *html = S_md_to_html(md);
+
+    const char *format =
+        "<h2>Name</h2>\n"
+        "%s";
+    char *result = CFCUtil_sprintf(format, html);
+
+    FREEMEM(html);
+    return result;
+}
+
+static char*
+S_html_create_synopsis(CFCClass *klass) {
+    CHY_UNUSED_VAR(klass);
+    return CFCUtil_strdup("");
+}
+
+static char*
+S_html_create_description(CFCClass *klass) {
+    CFCDocuComment *docucom = CFCClass_get_docucomment(klass);
+    char           *desc    = NULL;
+
+    if (docucom) {
+        const char *raw_desc = CFCDocuComment_get_long(docucom);
+        if (raw_desc && raw_desc[0] != '\0') {
+            desc = S_md_to_html(raw_desc);
+        }
+    }
+
+    if (!desc) { return CFCUtil_strdup(""); }
+
+    char *result = CFCUtil_sprintf("<h2>Description</h2>\n%s", desc);
+
+    FREEMEM(desc);
+    return result;
+}
+
+static char*
+S_html_create_functions(CFCClass *klass) {
+    CFCFunction **functions = CFCClass_functions(klass);
+    char         *result    = CFCUtil_strdup("");
+
+    for (int func_num = 0; functions[func_num] != NULL; func_num++) {
+        CFCFunction *func = functions[func_num];
+        if (!CFCFunction_public(func)) { continue; }
+
+        if (result[0] == '\0') {
+            result = CFCUtil_cat(result, "<h2>Functions</h2>\n<dl>\n", NULL);
+        }
+
+        const char *micro_sym     = CFCFunction_micro_sym(func);
+        const char *full_func_sym = CFCFunction_full_func_sym(func);
+
+        char *func_html = S_html_create_func(klass, func, micro_sym,
+                                             full_func_sym);
+        result = CFCUtil_cat(result, func_html, NULL);
+        FREEMEM(func_html);
+    }
+
+    if (result[0] != '\0') {
+        result = CFCUtil_cat(result, "</dl>\n", NULL);
+    }
+
+    return result;
+}
+
+static char*
+S_html_create_methods(CFCClass *klass) {
+    CFCMethod **fresh_methods = CFCClass_fresh_methods(klass);
+    char       *methods_html   = CFCUtil_strdup("");
+    char       *novel_html     = CFCUtil_strdup("");
+    char       *result;
+
+    for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) {
+        CFCMethod *method = fresh_methods[meth_num];
+        if (!CFCMethod_public(method) || !CFCMethod_novel(method)) {
+            continue;
+        }
+
+        const char *macro_sym = CFCMethod_get_macro_sym(method);
+        char *full_method_sym = CFCMethod_full_method_sym(method, NULL);
+        char *method_html = S_html_create_func(klass, (CFCFunction*)method,
+                                               macro_sym, full_method_sym);
+
+        if (CFCMethod_abstract(method)) {
+            if (methods_html[0] == '\0') {
+                methods_html = CFCUtil_cat(methods_html,
+                                          "<h3>Abstract methods</h3>\n<dl>\n",
+                                          NULL);
+            }
+            methods_html = CFCUtil_cat(methods_html, method_html, NULL);
+        }
+        else {
+            if (novel_html[0] == '\0') {
+                novel_html = CFCUtil_cat(novel_html,
+                                        "<h3>Novel methods</h3>\n<dl>\n",
+                                        NULL);
+            }
+            novel_html = CFCUtil_cat(novel_html, method_html, NULL);
+        }
+
+        FREEMEM(method_html);
+        FREEMEM(full_method_sym);
+    }
+
+    if (methods_html[0] != '\0') {
+        methods_html = CFCUtil_cat(methods_html, "</dl>\n", NULL);
+    }
+    if (novel_html[0] != '\0') {
+        novel_html = CFCUtil_cat(novel_html, "</dl>\n", NULL);
+    }
+
+    methods_html = CFCUtil_cat(methods_html, novel_html, NULL);
+
+    // Add methods from parent classes excluding Clownfish::Obj
+    CFCClass *parent = CFCClass_get_parent(klass);
+    while (parent) {
+        if (strcmp(CFCClass_get_class_name(parent), "Clownfish::Obj") == 0) {
+            break;
+        }
+        char *inherited_html = S_html_create_inherited_methods(parent);
+        methods_html = CFCUtil_cat(methods_html, inherited_html, NULL);
+        FREEMEM(inherited_html);
+        parent = CFCClass_get_parent(parent);
+    }
+
+    if (methods_html[0] == '\0') {
+        result = CFCUtil_strdup("");
+    }
+    else {
+        result = CFCUtil_sprintf("<h2>Methods</h2>\n%s", methods_html);
+    }
+
+    FREEMEM(methods_html);
+    FREEMEM(novel_html);
+    FREEMEM(fresh_methods);
+    return result;
+}
+
+static char*
+S_html_create_inherited_methods(CFCClass *klass) {
+    CFCMethod **fresh_methods = CFCClass_fresh_methods(klass);
+    char       *result        = CFCUtil_strdup("");
+
+    for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) {
+        CFCMethod *method = fresh_methods[meth_num];
+        if (!CFCMethod_public(method) || !CFCMethod_novel(method)) {
+            continue;
+        }
+
+        if (result[0] == '\0') {
+            result = CFCUtil_cat(result, "<h3>Methods inherited from ",
+                                 CFCClass_get_class_name(klass),
+                                 "</h3><dl>\n",
+                                 NULL);
+        }
+
+        const char *macro_sym = CFCMethod_get_macro_sym(method);
+        char *full_method_sym = CFCMethod_full_method_sym(method, NULL);
+        char *method_html = S_html_create_func(klass, (CFCFunction*)method,
+                                               macro_sym, full_method_sym);
+        result = CFCUtil_cat(result, method_html, NULL);
+
+        FREEMEM(method_html);
+        FREEMEM(full_method_sym);
+    }
+
+    if (result[0] != '\0') {
+        result = CFCUtil_cat(result, "</dl>\n", NULL);
+    }
+
+    FREEMEM(fresh_methods);
+    return result;
+}
+
+static char*
+S_html_create_func(CFCClass *klass, CFCFunction *func, const char *short_sym,
+                  const char *full_sym) {
+    CFCType    *return_type      = CFCFunction_get_return_type(func);
+    char       *return_type_html = S_type_to_html(klass, return_type);
+    const char *incremented      = "";
+
+    if (CFCType_incremented(return_type)) {
+        incremented = " // incremented";
+    }
+
+    char *param_list = S_html_create_param_list(klass, func, full_sym);
+
+    const char *pattern =
+        "<dt>%s</dt>\n"
+        "<dd>\n"
+        "<pre><code>%s%s\n"
+        "%s</code></pre>\n";
+    char *result = CFCUtil_sprintf(pattern, short_sym, return_type_html,
+                                   incremented, param_list);
+
+    FREEMEM(param_list);
+
+    // 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) {
+        // Description
+        const char *raw_desc = CFCDocuComment_get_description(docucomment);
+        char *desc = S_md_to_html(raw_desc);
+        result = CFCUtil_cat(result, desc, 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, "<dl>\n", NULL);
+            for (size_t i = 0; param_names[i] != NULL; i++) {
+                char *doc = S_md_to_html(param_docs[i]);
+                result = CFCUtil_cat(result, "<dt><emph>", param_names[i],
+                                     "</emph></dt>\n<dd>", doc, "</dd>\n",
+                                     NULL);
+                FREEMEM(doc);
+            }
+            result = CFCUtil_cat(result, "</dl>\n", NULL);
+        }
+
+        // Return value
+        const char *retval_doc = CFCDocuComment_get_retval(docucomment);
+        if (retval_doc && strlen(retval_doc)) {
+            char *md = CFCUtil_sprintf("**Returns:** %s", retval_doc);
+            char *html = S_md_to_html(md);
+            result = CFCUtil_cat(result, html, NULL);
+            FREEMEM(html);
+            FREEMEM(md);
+        }
+    }
+
+    result = CFCUtil_cat(result, "</dd>\n", NULL);
+
+    FREEMEM(return_type_html);
+    return result;
+}
+
+static char*
+S_html_create_param_list(CFCClass *klass, CFCFunction *func,
+                         const char *full_sym) {
+    CFCParamList  *param_list = CFCFunction_get_param_list(func);
+    CFCVariable  **variables  = CFCParamList_get_variables(param_list);
+
+    if (!variables[0]) {
+        return CFCUtil_sprintf("<strong>%s</strong>(void);\n", full_sym);
+    }
+
+    char *result = CFCUtil_sprintf("<strong>%s</strong>(", full_sym);
+
+    for (int i = 0; variables[i]; ++i) {
+        CFCVariable *variable  = variables[i];
+        CFCType     *type      = CFCVariable_get_type(variable);
+        char        *type_html = S_type_to_html(klass, type);
+        const char  *name      = CFCVariable_micro_sym(variable);
+
+        result = CFCUtil_cat(result, "\n    ", type_html, " <strong>", name,
+                             "</strong>", NULL);
+
+        if (variables[i+1]) {
+            result = CFCUtil_cat(result, ",", NULL);
+        }
+        if (CFCType_decremented(type)) {
+            result = CFCUtil_cat(result, " // decremented", NULL);
+        }
+
+        FREEMEM(type_html);
+    }
+
+    result = CFCUtil_cat(result, "\n);\n", NULL);
+
+    return result;
+}
+
+static char*
+S_html_create_inheritance(CFCClass *klass) {
+    CFCClass *ancestor = CFCClass_get_parent(klass);
+    char     *result   = CFCUtil_strdup("");
+
+    if (!ancestor) { return result; }
+
+    const char *class_name = CFCClass_get_class_name(klass);
+    result = CFCUtil_cat(result, "<h2>Inheritance</h2>\n<p>", class_name,
+                         NULL);
+    while (ancestor) {
+        const char *ancestor_name = CFCClass_get_class_name(ancestor);
+        const char *ancestor_sym  = CFCClass_full_struct_sym(ancestor);
+        result = CFCUtil_cat(result, " is a <a href=\"", ancestor_sym,
+                             ".html\">", ancestor_name, "</a>", NULL);
+        ancestor = CFCClass_get_parent(ancestor);
+    }
+    result = CFCUtil_cat(result, ".</p>\n", NULL);
+
+    return result;
+}
+
+static char*
+S_md_to_html(const char *md) {
+    cmark_node *doc = cmark_parse_document(md, strlen(md));
+    S_convert_uris(doc);
+    char *html = cmark_render_html(doc);
+    cmark_node_destroy(doc);
+
+    return html;
+}
+
+static void
+S_convert_uris(cmark_node *node) {
+    cmark_node *cur = node;
+
+    while (true) {
+        cmark_node_type type = cmark_node_get_type(cur);
+
+        if (type == NODE_LINK) {
+            S_convert_uri(cur);
+        }
+
+        cmark_node *child = cmark_node_first_child(cur);
+        if (child) {
+            cur = child;
+            continue;
+        }
+
+    next_sibling:
+        if (cur == node) { break; }
+        cmark_node *next = cmark_node_next(cur);
+        if (next) {
+            cur = next;
+            continue;
+        }
+        cur = cmark_node_parent(cur);
+        goto next_sibling;
+    }
+}
+
+static void
+S_convert_uri(cmark_node *link) {
+    const char *uri = cmark_node_get_url(link);
+    if (!uri || memcmp(uri, "clownfish:", 10) != 0) {
+        return;
+    }
+    if (memcmp(uri, "clownfish:class:", 16) != 0) {
+        CFCUtil_die("Invalid clownfish URI: %s", uri);
+    }
+
+    // Convert 'clownfish:class:{parcel}:{name}' to '{parcel}_{name}.html'
+    const char *src   = uri + 16;
+    const char *colon = strchr(src, ':');
+    if (!colon) {
+        CFCUtil_die("Invalid clownfish URI: %s", uri);
+    }
+    size_t src_len = strlen(src);
+    char *new_uri = (char*)MALLOCATE(src_len + 5 + 1);
+    strcpy(new_uri, src);
+    new_uri[colon-src] = '_';
+    strcpy(new_uri + src_len, ".html");
+
+    cmark_node_set_url(link, new_uri);
+    FREEMEM(new_uri);
+}
+
+static char*
+S_type_to_html(CFCClass *klass, CFCType *type) {
+    const char *type_c = CFCType_to_c(type);
+
+    if (CFCType_is_object(type)) {
+        const char *struct_sym = CFCClass_full_struct_sym(klass);
+        const char *specifier  = CFCType_get_specifier(type);
+        if (strcmp(specifier, struct_sym) != 0) {
+            return CFCUtil_sprintf("<a href=\"%s.html\">%s</a>", specifier,
+                                   type_c);
+        }
+    }
+
+    return CFCUtil_strdup(type_c);
+}
+

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/fca91dce/compiler/src/CFCCHTML.h
----------------------------------------------------------------------
diff --git a/compiler/src/CFCCHTML.h b/compiler/src/CFCCHTML.h
new file mode 100644
index 0000000..f340321
--- /dev/null
+++ b/compiler/src/CFCCHTML.h
@@ -0,0 +1,42 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef H_CFCCHTML
+#define H_CFCCHTML
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct CFCClass;
+struct CFCParcel;
+
+/** Return the index document of the HTML documentation.
+ */
+char*
+CFCCHTML_create_index_doc(struct CFCParcel *parcel, struct CFCClass **classes);
+
+/** Return the HTML documentation for the class.
+ */
+char*
+CFCCHTML_create_html_doc(struct CFCClass *klass);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_CFCCHTML */
+

Reply via email to