Convert Markdown to POD

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

Branch: refs/heads/markdown
Commit: f57df2fc21e610e82a39a6d37f85b327f8bf420b
Parents: 12d994b
Author: Nick Wellnhofer <[email protected]>
Authored: Mon Nov 10 17:35:22 2014 +0100
Committer: Nick Wellnhofer <[email protected]>
Committed: Sun Nov 23 18:35:49 2014 +0100

----------------------------------------------------------------------
 compiler/perl/lib/Clownfish/CFC.xs |   4 +-
 compiler/src/CFCClass.c            |   8 +
 compiler/src/CFCClass.h            |   8 +
 compiler/src/CFCPerlClass.c        |  34 ++--
 compiler/src/CFCPerlPod.c          | 307 ++++++++++++++++++++++++++++++--
 compiler/src/CFCPerlPod.h          |   2 +-
 6 files changed, 327 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/f57df2fc/compiler/perl/lib/Clownfish/CFC.xs
----------------------------------------------------------------------
diff --git a/compiler/perl/lib/Clownfish/CFC.xs 
b/compiler/perl/lib/Clownfish/CFC.xs
index a6ea249..1732653 100644
--- a/compiler/perl/lib/Clownfish/CFC.xs
+++ b/compiler/perl/lib/Clownfish/CFC.xs
@@ -2463,11 +2463,11 @@ PPCODE:
 
 
 SV*
-_perlify_doc_text(self, source)
+_md_to_pod(self, source)
     CFCPerlPod   *self;
     const char   *source;
 CODE:
-    RETVAL = S_sv_eat_c_string(CFCPerlPod_perlify_doc_text(self, source));
+    RETVAL = S_sv_eat_c_string(CFCPerlPod_md_to_pod(self, source));
 OUTPUT: RETVAL
 
 SV*

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/f57df2fc/compiler/src/CFCClass.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCClass.c b/compiler/src/CFCClass.c
index 59ec8c3..6a23183 100644
--- a/compiler/src/CFCClass.c
+++ b/compiler/src/CFCClass.c
@@ -314,6 +314,14 @@ CFCClass_fetch_singleton(CFCParcel *parcel, const char 
*class_name) {
     }
     char key[MAX_SINGLETON_LEN + 1];
     sprintf(key, "%s%s", prefix, struct_sym);
+
+    return CFCClass_fetch_by_struct_sym(key);
+}
+
+CFCClass*
+CFCClass_fetch_by_struct_sym(const char *key) {
+    CFCUTIL_NULL_CHECK(key);
+
     for (size_t i = 0; i < registry_size; i++) {
         if (strcmp(registry[i].key, key) == 0) {
             return registry[i].klass;

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/f57df2fc/compiler/src/CFCClass.h
----------------------------------------------------------------------
diff --git a/compiler/src/CFCClass.h b/compiler/src/CFCClass.h
index 877a84a..8d7f4d8 100644
--- a/compiler/src/CFCClass.h
+++ b/compiler/src/CFCClass.h
@@ -82,6 +82,14 @@ CFCClass_destroy(CFCClass *self);
 CFCClass*
 CFCClass_fetch_singleton(struct CFCParcel *parcel, const char *class_name);
 
+/** Retrieve a Class by its struct sym.
+ *
+ * @param A Clownfish::CFC::Model::Parcel.
+ * @param full_struct_sym The Class's full struct sym.
+ */
+CFCClass*
+CFCClass_fetch_by_struct_sym(const char *full_struct_sym);
+
 /** Empty out the registry, decrementing the refcount of all Class singleton
  * objects.
  */

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/f57df2fc/compiler/src/CFCPerlClass.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCPerlClass.c b/compiler/src/CFCPerlClass.c
index aa3d738..57561c3 100644
--- a/compiler/src/CFCPerlClass.c
+++ b/compiler/src/CFCPerlClass.c
@@ -361,14 +361,18 @@ CFCPerlClass_create_pod(CFCPerlClass *self) {
 
     // Get the class's brief description.
     const char *raw_brief = CFCDocuComment_get_brief(docucom);
-    char *brief = CFCPerlPod_perlify_doc_text(pod_spec, raw_brief);
+    char *brief = CFCPerlPod_md_to_pod(pod_spec, raw_brief);
 
     // Get the class's long description.
-    const char *raw_description = CFCPerlPod_get_description(pod_spec);
-    if (!raw_description || !strlen(raw_description)) {
-        raw_description = CFCDocuComment_get_long(docucom);
+    char *description;
+    const char *pod_description = CFCPerlPod_get_description(pod_spec);
+    if (pod_description && strlen(pod_description)) {
+        description = CFCUtil_sprintf("%s\n", pod_description);
+    }
+    else {
+        const char *raw_description = CFCDocuComment_get_long(docucom);
+        description = CFCPerlPod_md_to_pod(pod_spec, raw_description);
     }
-    char *description = CFCPerlPod_perlify_doc_text(pod_spec, raw_description);
 
     // Create SYNOPSIS.
     const char *raw_synopsis = CFCPerlPod_get_synopsis(pod_spec);
@@ -401,27 +405,21 @@ CFCPerlClass_create_pod(CFCPerlClass *self) {
                                           ancestor_klass, NULL);
             }
         }
-        inheritance = CFCUtil_cat(inheritance, ".\n", NULL);
+        inheritance = CFCUtil_cat(inheritance, ".\n\n", NULL);
     }
 
     // Put it all together.
     const char pattern[] =
         "=head1 NAME\n"
         "\n"
-        "%s - %s\n"
-        "\n"
-        "%s\n"
-        "\n"
+        "%s - %s"
+        "%s"
         "=head1 DESCRIPTION\n"
         "\n"
-        "%s\n"
-        "\n"
-        "%s\n"
-        "\n"
-        "%s\n"
-        "\n"
-        "%s\n"
-        "\n"
+        "%s"
+        "%s"
+        "%s"
+        "%s"
         "=cut\n"
         "\n";
     char *pod

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/f57df2fc/compiler/src/CFCPerlPod.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCPerlPod.c b/compiler/src/CFCPerlPod.c
index b864fcb..28b98db 100644
--- a/compiler/src/CFCPerlPod.c
+++ b/compiler/src/CFCPerlPod.c
@@ -16,6 +16,9 @@
 
 #include <string.h>
 #include <ctype.h>
+
+#include <cmark.h>
+
 #define CFC_NEED_BASE_STRUCT_DEF
 #include "CFCBase.h"
 #include "CFCPerlPod.h"
@@ -55,6 +58,18 @@ static const CFCMeta CFCPERLPOD_META = {
     (CFCBase_destroy_t)CFCPerlPod_destroy
 };
 
+static char*
+S_nodes_to_pod(cmark_node *node);
+
+static char*
+S_pod_escape(const char *content);
+
+static char*
+S_convert_uri(const char *uri);
+
+static char*
+S_perlify_pod(const char *source);
+
 CFCPerlPod*
 CFCPerlPod_new(void) {
     CFCPerlPod *self
@@ -161,16 +176,14 @@ CFCPerlPod_methods_pod(CFCPerlPod *self, CFCClass *klass) 
{
         }
         char *meth_pod;
         if (meth_spec.pod) {
-            meth_pod = CFCPerlPod_perlify_doc_text(self, meth_spec.pod);
+            meth_pod = CFCUtil_sprintf("%s\n", meth_spec.pod);
         }
         else {
-            char *raw
+            meth_pod
                 = CFCPerlPod_gen_subroutine_pod(self, (CFCFunction*)method,
                                                 meth_spec.alias, klass,
                                                 meth_spec.sample, class_name,
                                                 false);
-            meth_pod = CFCPerlPod_perlify_doc_text(self, raw);
-            FREEMEM(raw);
         }
         if (CFCMethod_abstract(method)) {
             abstract_pod = CFCUtil_cat(abstract_pod, meth_pod, NULL);
@@ -204,19 +217,15 @@ CFCPerlPod_constructors_pod(CFCPerlPod *self, CFCClass 
*klass) {
     for (size_t i = 0; i < self->num_constructors; i++) {
         NamePod slot = self->constructors[i];
         if (slot.pod) {
-            char *perlified = CFCPerlPod_perlify_doc_text(self, slot.pod);
-            pod = CFCUtil_cat(pod, perlified, NULL);
-            FREEMEM(perlified);
+            pod = CFCUtil_cat(pod, slot.pod, "\n", NULL);
         }
         else {
             CFCFunction *init_func = CFCClass_function(klass, slot.func);
             char *sub_pod
                 = CFCPerlPod_gen_subroutine_pod(self, init_func, slot.alias, 
klass,
                                                 slot.sample, class_name, true);
-            char *perlified = CFCPerlPod_perlify_doc_text(self, sub_pod);
-            pod = CFCUtil_cat(pod, perlified, NULL);
+            pod = CFCUtil_cat(pod, sub_pod, NULL);
             FREEMEM(sub_pod);
-            FREEMEM(perlified);
         }
     }
     return pod;
@@ -318,8 +327,8 @@ CFCPerlPod_gen_subroutine_pod(CFCPerlPod *self, CFCFunction 
*func,
     // Incorporate "description" text from DocuComment.
     const char *long_doc = CFCDocuComment_get_description(docucomment);
     if (long_doc && strlen(long_doc)) {
-        char *perlified = CFCPerlPod_perlify_doc_text(self, long_doc);
-        pod = CFCUtil_cat(pod, perlified, "\n\n", NULL);
+        char *perlified = CFCPerlPod_md_to_pod(self, long_doc);
+        pod = CFCUtil_cat(pod, perlified, NULL);
         FREEMEM(perlified);
     }
 
@@ -329,8 +338,10 @@ CFCPerlPod_gen_subroutine_pod(CFCPerlPod *self, 
CFCFunction *func,
     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, param_docs[i]);
             pod = CFCUtil_cat(pod, "=item *\n\nB<", param_names[i], "> - ",
-                              param_docs[i], "\n\n", NULL);
+                              perlified, NULL);
+            FREEMEM(perlified);
         }
         pod = CFCUtil_cat(pod, "=back\n\n", NULL);
     }
@@ -338,16 +349,282 @@ CFCPerlPod_gen_subroutine_pod(CFCPerlPod *self, 
CFCFunction *func,
     // Add return value description, if any.
     const char *retval_doc = CFCDocuComment_get_retval(docucomment);
     if (retval_doc && strlen(retval_doc)) {
-        pod = CFCUtil_cat(pod, "Returns: ", retval_doc, "\n\n", NULL);
+        char *perlified = CFCPerlPod_md_to_pod(self, retval_doc);
+        pod = CFCUtil_cat(pod, "Returns: ", perlified, NULL);
+        FREEMEM(perlified);
     }
 
     return pod;
 }
 
 char*
-CFCPerlPod_perlify_doc_text(CFCPerlPod *self, const char *source) {
+CFCPerlPod_md_to_pod(CFCPerlPod *self, const char *md) {
     (void)self; // unused
 
+    cmark_node *doc = cmark_parse_document(md, strlen(md));
+    char *pod = S_nodes_to_pod(doc);
+    cmark_node_destroy(doc);
+    char *perlified = S_perlify_pod(pod);
+
+    FREEMEM(pod);
+    return perlified;
+}
+
+static char*
+S_nodes_to_pod(cmark_node *node) {
+    char *result = CFCUtil_strdup("");
+
+    while (node) {
+        cmark_node_type type = cmark_node_get_type(node);
+
+        switch (type) {
+            case NODE_DOCUMENT: {
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_pod = S_nodes_to_pod(child);
+                result = CFCUtil_cat(result, children_pod, NULL);
+                FREEMEM(children_pod);
+                break;
+            }
+
+            case NODE_PARAGRAPH: {
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_pod = S_nodes_to_pod(child);
+                result = CFCUtil_cat(result, children_pod, "\n\n", NULL);
+                FREEMEM(children_pod);
+                break;
+            }
+
+            case NODE_BLOCK_QUOTE: {
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_pod = S_nodes_to_pod(child);
+                result = CFCUtil_cat(result, "=over\n\n", children_pod,
+                                     "\n=back\n\n", NULL);
+                FREEMEM(children_pod);
+                break;
+            }
+
+            case NODE_LIST_ITEM: {
+                // TODO: Ordered lists.
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_pod = S_nodes_to_pod(child);
+                result = CFCUtil_cat(result, "=item *\n\n", children_pod,
+                                     NULL);
+                FREEMEM(children_pod);
+                break;
+            }
+
+            case NODE_LIST: {
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_pod = S_nodes_to_pod(child);
+                result = CFCUtil_cat(result, "=over\n\n", children_pod,
+                                     "=back\n\n", NULL);
+                FREEMEM(children_pod);
+                break;
+            }
+
+            case NODE_ATX_HEADER:
+            case NODE_SETEXT_HEADER: {
+                cmark_node *child = cmark_node_first_child(node);
+                int header_level = cmark_node_get_header_level(node);
+                char *children_pod = S_nodes_to_pod(child);
+                char *header = CFCUtil_sprintf("=head%d %s\n\n",
+                                               header_level + 2, children_pod);
+                result = CFCUtil_cat(result, header, NULL);
+                FREEMEM(header);
+                FREEMEM(children_pod);
+                break;
+            }
+
+            case NODE_INDENTED_CODE:
+            case NODE_FENCED_CODE: {
+                const char *content = cmark_node_get_string_content(node);
+                char *escaped = S_pod_escape(content);
+                // Chomp trailing newline.
+                size_t len = strlen(escaped);
+                if (len > 0 && escaped[len-1] == '\n') {
+                    escaped[len-1] = '\0';
+                }
+                char *indented = S_global_replace(escaped, "\n", "\n    ");
+                result = CFCUtil_cat(result, "    ", indented, "\n\n", NULL);
+                FREEMEM(indented);
+                FREEMEM(escaped);
+                break;
+            }
+
+            case NODE_HTML: {
+                const char *html = cmark_node_get_string_content(node);
+                result = CFCUtil_cat(result, "=begin html\n\n", html,
+                                     "\n=end\n\n", NULL);
+                break;
+            }
+
+            case NODE_HRULE:
+                break;
+
+            case NODE_REFERENCE_DEF:
+                break;
+
+            case NODE_STRING: {
+                const char *content = cmark_node_get_string_content(node);
+                char *escaped = S_pod_escape(content);
+                result = CFCUtil_cat(result, escaped, NULL);
+                FREEMEM(escaped);
+                break;
+            }
+
+            case NODE_LINEBREAK:
+                // POD doesn't support line breaks. Start a new paragraph.
+                result = CFCUtil_cat(result, "\n\n", NULL);
+                break;
+
+            case NODE_SOFTBREAK:
+                result = CFCUtil_cat(result, "\n", NULL);
+                break;
+
+            case NODE_INLINE_CODE: {
+                const char *content = cmark_node_get_string_content(node);
+                char *escaped = S_pod_escape(content);
+                result = CFCUtil_cat(result, "C<", escaped, ">", NULL);
+                FREEMEM(escaped);
+                break;
+            }
+
+            case NODE_INLINE_HTML: {
+                const char *html = cmark_node_get_string_content(node);
+                CFCUtil_warn("Inline HTML not supported in POD: %s", html);
+                break;
+            }
+
+            case NODE_LINK: {
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_pod = S_nodes_to_pod(child);
+                char *uri = S_convert_uri(cmark_node_get_url(node));
+                if (children_pod[0] == '\0'
+                    || strcmp(children_pod, uri) == 0
+                ) {
+                    result = CFCUtil_cat(result, "L<", uri, ">", NULL);
+                }
+                else {
+                    result = CFCUtil_cat(result, "L<", children_pod, "|", uri,
+                                         ">", NULL);
+                }
+                FREEMEM(children_pod);
+                FREEMEM(uri);
+                break;
+            }
+
+            case NODE_IMAGE:
+                CFCUtil_warn("Images not supported in POD");
+                break;
+
+            case NODE_STRONG: {
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_pod = S_nodes_to_pod(child);
+                result = CFCUtil_cat(result, "B<", children_pod, ">", NULL);
+                FREEMEM(children_pod);
+                break;
+            }
+
+            case NODE_EMPH: {
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_pod = S_nodes_to_pod(child);
+                result = CFCUtil_cat(result, "I<", children_pod, ">", NULL);
+                FREEMEM(children_pod);
+                break;
+            }
+
+            default:
+                CFCUtil_die("Invalid cmark node type: %d", type);
+                break;
+        }
+
+        node = cmark_node_next(node);
+    }
+
+    return result;
+}
+
+static char*
+S_pod_escape(const char *content) {
+    size_t  len        = strlen(content);
+    size_t  result_len = 0;
+    size_t  result_cap = len + 256;
+    char   *result     = (char*)MALLOCATE(result_cap + 1);
+
+    for (size_t i = 0; i < len; i++) {
+        const char *subst      = content + i;
+        size_t      subst_size = 1;
+
+        switch (content[i]) {
+            case '<':
+                // Escape "less than".
+                subst      = "E<lt>";
+                subst_size = 5;
+                break;
+            case '>':
+                // Escape "greater than".
+                subst      = "E<gt>";
+                subst_size = 5;
+                break;
+            case '|':
+                // Escape vertical bar.
+                subst      = "E<verbar>";
+                subst_size = 9;
+                break;
+            case '=':
+                // Escape equal sign at start of line.
+                if (i == 0 || content[i-1] == '\n') {
+                    subst      = "E<61>";
+                    subst_size = 5;
+                }
+                break;
+            default:
+                break;
+        }
+
+        if (result_len + subst_size > result_cap) {
+            result_cap += 256;
+            result = (char*)REALLOCATE(result, result_cap + 1);
+        }
+
+        memcpy(result + result_len, subst, subst_size);
+        result_len += subst_size;
+    }
+
+    result[result_len] = '\0';
+
+    return result;
+}
+
+static char*
+S_convert_uri(const char *uri) {
+    if (memcmp(uri, "clownfish:", 10) != 0) {
+        return CFCUtil_strdup(uri);
+    }
+    if (memcmp(uri, "clownfish:class:", 16) != 0) {
+        CFCUtil_die("Invalid clownfish URI: %s", uri);
+    }
+
+    // Convert 'clownfish:class:{parcel}:{name}' to '{class_name}'
+    const char *src   = uri + 16;
+    const char *colon = strchr(src, ':');
+    if (!colon) {
+        CFCUtil_die("Invalid clownfish URI: %s", uri);
+    }
+    char *struct_sym = CFCUtil_strdup(src);
+    struct_sym[colon-src] = '_';
+    CFCClass *klass = CFCClass_fetch_by_struct_sym(struct_sym);
+    FREEMEM(struct_sym);
+    if (!klass) {
+        CFCUtil_warn("No class found for URI %s\n", uri);
+        return CFCUtil_strdup(uri);
+    }
+
+    return CFCUtil_strdup(CFCClass_get_class_name(klass));
+}
+
+static char*
+S_perlify_pod(const char *source) {
     // Change `foo` to C<< foo >>.
     char *copy = CFCUtil_strdup(source);
     char *orig = copy;

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/f57df2fc/compiler/src/CFCPerlPod.h
----------------------------------------------------------------------
diff --git a/compiler/src/CFCPerlPod.h b/compiler/src/CFCPerlPod.h
index 38bb0c1..1ebac1a 100644
--- a/compiler/src/CFCPerlPod.h
+++ b/compiler/src/CFCPerlPod.h
@@ -99,7 +99,7 @@ const char*
 CFCPerlPod_get_description(CFCPerlPod *self);
 
 char*
-CFCPerlPod_perlify_doc_text(CFCPerlPod *self, const char *source);
+CFCPerlPod_md_to_pod(CFCPerlPod *self, const char *source);
 
 /** Autogenerate pod for either a Clownfish::CFC::Model::Method or a
  * Clownfish::CFC::Model::Function.

Reply via email to