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/c2a78e23
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/c2a78e23
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/c2a78e23

Branch: refs/heads/master
Commit: c2a78e237fd3ba30557be12c6a646e778c276944
Parents: 7d9a311
Author: Nick Wellnhofer <[email protected]>
Authored: Mon Nov 10 17:35:22 2014 +0100
Committer: Nick Wellnhofer <[email protected]>
Committed: Wed Dec 24 16:02:04 2014 +0100

----------------------------------------------------------------------
 compiler/perl/lib/Clownfish/CFC.xs |   9 +-
 compiler/src/CFCClass.c            |   8 +
 compiler/src/CFCClass.h            |   8 +
 compiler/src/CFCPerlClass.c        |  34 ++-
 compiler/src/CFCPerlPod.c          | 429 ++++++++++++++++++++++++++++----
 compiler/src/CFCPerlPod.h          |   3 +-
 6 files changed, 421 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c2a78e23/compiler/perl/lib/Clownfish/CFC.xs
----------------------------------------------------------------------
diff --git a/compiler/perl/lib/Clownfish/CFC.xs 
b/compiler/perl/lib/Clownfish/CFC.xs
index 408c058..a27e193 100644
--- a/compiler/perl/lib/Clownfish/CFC.xs
+++ b/compiler/perl/lib/Clownfish/CFC.xs
@@ -2461,11 +2461,12 @@ PPCODE:
 
 
 SV*
-_perlify_doc_text(self, source)
-    CFCPerlPod   *self;
-    const char   *source;
+_md_to_pod(self, klass, source)
+    CFCPerlPod *self;
+    CFCClass   *klass;
+    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, klass, source));
 OUTPUT: RETVAL
 
 SV*

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c2a78e23/compiler/src/CFCClass.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCClass.c b/compiler/src/CFCClass.c
index 37abb3c..2184f57 100644
--- a/compiler/src/CFCClass.c
+++ b/compiler/src/CFCClass.c
@@ -319,6 +319,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/c2a78e23/compiler/src/CFCClass.h
----------------------------------------------------------------------
diff --git a/compiler/src/CFCClass.h b/compiler/src/CFCClass.h
index 1aa654e..b960134 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/c2a78e23/compiler/src/CFCPerlClass.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCPerlClass.c b/compiler/src/CFCPerlClass.c
index f9a3e20..782f719 100644
--- a/compiler/src/CFCPerlClass.c
+++ b/compiler/src/CFCPerlClass.c
@@ -359,14 +359,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, client, 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, client, raw_description);
     }
-    char *description = CFCPerlPod_perlify_doc_text(pod_spec, raw_description);
 
     // Create SYNOPSIS.
     const char *raw_synopsis = CFCPerlPod_get_synopsis(pod_spec);
@@ -399,27 +403,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/c2a78e23/compiler/src/CFCPerlPod.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCPerlPod.c b/compiler/src/CFCPerlPod.c
index 329142b..08d8bda 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"
@@ -26,6 +29,7 @@
 #include "CFCParamList.h"
 #include "CFCFunction.h"
 #include "CFCDocuComment.h"
+#include "CFCUri.h"
 
 #ifndef true
   #define true 1
@@ -55,6 +59,21 @@ static const CFCMeta CFCPERLPOD_META = {
     (CFCBase_destroy_t)CFCPerlPod_destroy
 };
 
+static char*
+S_nodes_to_pod(CFCClass *klass, cmark_node *node);
+
+static char*
+S_pod_escape(const char *content);
+
+static char*
+S_convert_link(CFCClass *klass, cmark_node *link);
+
+static char*
+S_pod_link(const char *text, const char *name);
+
+static char*
+S_perlify_pod(const char *source);
+
 CFCPerlPod*
 CFCPerlPod_new(void) {
     CFCPerlPod *self
@@ -161,16 +180,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 +221,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;
@@ -277,8 +290,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, klass, long_doc);
+        pod = CFCUtil_cat(pod, perlified, NULL);
         FREEMEM(perlified);
     }
 
@@ -288,8 +301,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, klass, 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);
     }
@@ -297,54 +312,374 @@ 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, klass, 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, CFCClass *klass, const char *md) {
     (void)self; // unused
 
-    // Change <code>foo</code> to C<< foo >>.
-    char *copy = CFCUtil_strdup(source);
-    char *orig = copy;
-    copy = CFCUtil_global_replace(orig, "<code>", "C<< ");
-    FREEMEM(orig);
-    orig = copy;
-    copy = CFCUtil_global_replace(orig, "</code>", " >>");
-    FREEMEM(orig);
-
-    // Lowercase all method names: Open_In() => open_in()
-    for (size_t i = 0, max = strlen(copy); i < max; i++) {
-        if (isupper(copy[i])) {
-            size_t mark = i;
-            for (; i < max; i++) {
-                char c = copy[i];
-                if (!(isalpha(c) || c == '_')) {
-                    if (memcmp(copy + i, "()", 2) == 0) {
-                        for (size_t j = mark; j < i; j++) {
-                            copy[j] = tolower(copy[j]);
-                        }
-                        i += 2; // go past parens.
-                    }
-                    break;
+    cmark_node *doc = cmark_parse_document(md, strlen(md));
+    char *pod = S_nodes_to_pod(klass, doc);
+    cmark_node_free(doc);
+    char *perlified = S_perlify_pod(pod);
+
+    FREEMEM(pod);
+    return perlified;
+}
+
+static char*
+S_nodes_to_pod(CFCClass *klass, 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(klass, 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(klass, 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(klass, 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(klass, 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(klass, child);
+                result = CFCUtil_cat(result, "=over\n\n", children_pod,
+                                     "=back\n\n", NULL);
+                FREEMEM(children_pod);
+                break;
+            }
+
+            case NODE_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(klass, 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_CODE_BLOCK: {
+                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
+                    = CFCUtil_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_TEXT: {
+                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: {
+                char *pod = S_convert_link(klass, node);
+                result = CFCUtil_cat(result, pod, NULL);
+                FREEMEM(pod);
+                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(klass, 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(klass, 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);
     }
 
-    // Change all instances of NULL to 'undef'
-    orig = copy;
-    copy = CFCUtil_global_replace(orig, "NULL", "undef");
-    FREEMEM(orig);
+    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_link(CFCClass *klass, cmark_node *link) {
+    cmark_node *child = cmark_node_first_child(link);
+    const char *uri   = cmark_node_get_url(link);
+    char       *text  = S_nodes_to_pod(klass, child);
+    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, klass);
+    int     type     = CFCUri_get_type(uri_obj);
+
+    switch (type) {
+        case CFC_URI_NULL:
+            // Change all instances of NULL to 'undef'
+            new_text = CFCUtil_strdup("undef");
+            break;
+
+        case CFC_URI_CLASS: {
+            const char *full_struct_sym = CFCUri_full_struct_sym(uri_obj);
+            CFCClass *uri_class
+                = CFCClass_fetch_by_struct_sym(full_struct_sym);
 
-    // Change "Err_error" to "Clownfish->error".
-    orig = copy;
-    copy = CFCUtil_global_replace(orig, "Err_error", "Clownfish->error");
-    FREEMEM(orig);
+            if (!uri_class) {
+                CFCUtil_warn("URI class not found: %s", full_struct_sym);
+            }
+            else if (uri_class != klass) {
+                const char *class_name = CFCClass_get_class_name(uri_class);
+                new_uri = CFCUtil_strdup(class_name);
+            }
+
+            if (text[0] != '\0') {
+                // Keep text.
+                break;
+            }
+
+            if (strcmp(CFCUri_get_prefix(uri_obj),
+                       CFCClass_get_prefix(klass)) == 0
+            ) {
+                // Same parcel.
+                const char *struct_sym = CFCUri_get_struct_sym(uri_obj);
+                new_text = CFCUtil_strdup(struct_sym);
+            }
+            else {
+                // Other parcel.
+                if (!uri_class) {
+                    new_text = CFCUtil_strdup(full_struct_sym);
+                }
+                else {
+                    const char *class_name
+                        = CFCClass_get_class_name(uri_class);
+                    new_text = CFCUtil_strdup(class_name);
+                }
+            }
+
+            break;
+        }
+
+        case CFC_URI_FUNCTION:
+        case CFC_URI_METHOD: {
+            const char *full_struct_sym = CFCUri_full_struct_sym(uri_obj);
+            const char *func_sym        = CFCUri_get_func_sym(uri_obj);
+
+            // Convert "Err_get_error" to "Clownfish->error".
+            if (strcmp(full_struct_sym, "cfish_Err") == 0
+                && strcmp(func_sym, "get_error") == 0
+            ) {
+                new_text = CFCUtil_strdup("Clownfish->error");
+                break;
+            }
 
-    return copy;
+            CFCClass *uri_class
+                = CFCClass_fetch_by_struct_sym(full_struct_sym);
+
+            // TODO: Link to relevant POD section. This isn't easy because
+            // the section headers for functions also contain a description
+            // of the parameters.
+
+            if (!uri_class) {
+                CFCUtil_warn("URI class not found: %s", full_struct_sym);
+            }
+            else if (uri_class != klass) {
+                const char *class_name = CFCClass_get_class_name(uri_class);
+                new_uri = CFCUtil_strdup(class_name);
+            }
+
+            new_text = CFCUtil_sprintf("%s()", func_sym);
+            for (size_t i = 0; new_text[i] != '\0'; ++i) {
+                new_text[i] = tolower(new_text[i]);
+            }
+
+            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;
+}
+
+static char*
+S_pod_link(const char *text, const char *name) {
+    if (!text || text[0] == '\0' || strcmp(text, name) == 0) {
+        return CFCUtil_sprintf("L<%s>", name);
+    }
+    else {
+        return CFCUtil_sprintf("L<%s|%s>", text, name);
+    }
+}
+
+static char*
+S_perlify_pod(const char *source) {
+    // Change all instances of NULL to 'undef'
+    return CFCUtil_global_replace(source, "NULL", "undef");
 }
 

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c2a78e23/compiler/src/CFCPerlPod.h
----------------------------------------------------------------------
diff --git a/compiler/src/CFCPerlPod.h b/compiler/src/CFCPerlPod.h
index 3fb94eb..fe10faf 100644
--- a/compiler/src/CFCPerlPod.h
+++ b/compiler/src/CFCPerlPod.h
@@ -99,7 +99,8 @@ const char*
 CFCPerlPod_get_description(CFCPerlPod *self);
 
 char*
-CFCPerlPod_perlify_doc_text(CFCPerlPod *self, const char *source);
+CFCPerlPod_md_to_pod(CFCPerlPod *self, struct CFCClass *klass,
+                     const char *source);
 
 /** Autogenerate pod for either a Clownfish::CFC::Model::Method or a
  * Clownfish::CFC::Model::Function.

Reply via email to