Пример #1
0
static CFCPerlPodFile*
S_write_standalone_pod(CFCPerl *self) {
    CFCDocument **docs = CFCDocument_get_registry();
    size_t num_pod_files = 0;
    while (docs[num_pod_files]) { num_pod_files++; }
    size_t alloc_size = (num_pod_files + 1) * sizeof(CFCPerlPodFile);
    CFCPerlPodFile *pod_files = (CFCPerlPodFile*)MALLOCATE(alloc_size);

    for (size_t i = 0; i < num_pod_files; i++) {
        CFCDocument *doc = docs[i];
        const char *path_part = CFCDocument_get_path_part(doc);
        char *module  = CFCUtil_global_replace(path_part, CHY_DIR_SEP, "::");
        char *md      = CFCDocument_get_contents(doc);
        char *raw_pod = CFCPerlPod_md_doc_to_pod(module, md);

        const char *pattern =
            "%s"
            "\n"
            "=encoding utf8\n"
            "\n"
            "%s"
            "%s";
        char *pod = CFCUtil_sprintf(pattern, self->pod_header, raw_pod,
                                    self->pod_footer);

        char *pod_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s.pod",
                                         self->lib_dir, path_part);

        pod_files[i].contents = pod;
        pod_files[i].path     = pod_path;

        FREEMEM(raw_pod);
        FREEMEM(md);
        FREEMEM(module);
    }

    pod_files[num_pod_files].contents = NULL;
    pod_files[num_pod_files].path     = NULL;

    return pod_files;
}
Пример #2
0
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;
}
Пример #3
0
// Convert a single node.
static char*
S_node_to_pod(cmark_node *node, CFCClass *klass, int header_level) {
    char *result = CFCUtil_strdup("");
    if (node == NULL) {
        return result;
    }

    int found_matching_code_block = false;
    cmark_iter *iter = cmark_iter_new(node);
    cmark_event_type ev_type;

    while (CMARK_EVENT_DONE != (ev_type = cmark_iter_next(iter))) {
        cmark_node *node = cmark_iter_get_node(iter);
        cmark_node_type type = cmark_node_get_type(node);

        switch (type) {
            case CMARK_NODE_DOCUMENT:
                break;

            case CMARK_NODE_PARAGRAPH:
                if (ev_type == CMARK_EVENT_EXIT) {
                    result = CFCUtil_cat(result, "\n\n", NULL);
                }
                break;

            case CMARK_NODE_BLOCK_QUOTE:
            case CMARK_NODE_LIST:
                if (ev_type == CMARK_EVENT_ENTER) {
                    result = CFCUtil_cat(result, "=over\n\n", NULL);
                }
                else {
                    result = CFCUtil_cat(result, "=back\n\n", NULL);
                }
                break;

            case CMARK_NODE_ITEM:
                // TODO: Ordered lists.
                if (ev_type == CMARK_EVENT_ENTER) {
                    result = CFCUtil_cat(result, "=item *\n\n", NULL);
                }
                break;

            case CMARK_NODE_HEADER:
                if (ev_type == CMARK_EVENT_ENTER) {
                    int extra_level = cmark_node_get_header_level(node) - 1;
                    char *header = CFCUtil_sprintf("=head%d ",
                                                   header_level + extra_level);
                    result = CFCUtil_cat(result, header, NULL);
                    FREEMEM(header);
                }
                else {
                    result = CFCUtil_cat(result, "\n\n", NULL);
                }
                break;

            case CMARK_NODE_CODE_BLOCK: {
                int is_host = CFCMarkdown_code_block_is_host(node, "perl");

                if (is_host) {
                    found_matching_code_block = true;

                    const char *content = cmark_node_get_literal(node);
                    char *copy = CFCUtil_strdup(content);
                    // Chomp trailing newline.
                    size_t len = strlen(copy);
                    if (len > 0 && copy[len-1] == '\n') {
                        copy[len-1] = '\0';
                    }
                    char *indented
                        = CFCUtil_global_replace(copy, "\n", "\n    ");
                    result
                        = CFCUtil_cat(result, "    ", indented, "\n\n", NULL);
                    FREEMEM(indented);
                    FREEMEM(copy);
                }

                if (CFCMarkdown_code_block_is_last(node)) {
                    if (!found_matching_code_block) {
                        result = CFCUtil_cat(result,
                            "    Code example for Perl is missing\n\n");
                    }
                    else {
                        // Reset.
                        found_matching_code_block = false;
                    }
                }

                break;
            }

            case CMARK_NODE_HTML: {
                const char *html = cmark_node_get_literal(node);
                result = CFCUtil_cat(result, "=begin html\n\n", html,
                                     "\n=end\n\n", NULL);
                break;
            }

            case CMARK_NODE_HRULE:
                break;

            case CMARK_NODE_TEXT: {
                const char *content = cmark_node_get_literal(node);
                char *escaped = S_pod_escape(content);
                result = CFCUtil_cat(result, escaped, NULL);
                FREEMEM(escaped);
                break;
            }

            case CMARK_NODE_LINEBREAK:
                // POD doesn't support line breaks. Start a new paragraph.
                result = CFCUtil_cat(result, "\n\n", NULL);
                break;

            case CMARK_NODE_SOFTBREAK:
                result = CFCUtil_cat(result, "\n", NULL);
                break;

            case CMARK_NODE_CODE: {
                const char *content = cmark_node_get_literal(node);
                char *escaped = S_pod_escape(content);
                result = CFCUtil_cat(result, "C<", escaped, ">", NULL);
                FREEMEM(escaped);
                break;
            }

            case CMARK_NODE_INLINE_HTML: {
                const char *html = cmark_node_get_literal(node);
                CFCUtil_warn("Inline HTML not supported in POD: %s", html);
                break;
            }

            case CMARK_NODE_LINK:
                if (ev_type == CMARK_EVENT_ENTER) {
                    char *pod = S_convert_link(node, klass, header_level);
                    result = CFCUtil_cat(result, pod, NULL);
                    FREEMEM(pod);
                    cmark_iter_reset(iter, node, CMARK_EVENT_EXIT);
                }
                break;

            case CMARK_NODE_IMAGE:
                CFCUtil_warn("Images not supported in POD");
                break;

            case CMARK_NODE_STRONG:
                if (ev_type == CMARK_EVENT_ENTER) {
                    result = CFCUtil_cat(result, "B<", NULL);
                }
                else {
                    result = CFCUtil_cat(result, ">", NULL);
                }
                break;

            case CMARK_NODE_EMPH:
                if (ev_type == CMARK_EVENT_ENTER) {
                    result = CFCUtil_cat(result, "I<", NULL);
                }
                else {
                    result = CFCUtil_cat(result, ">", NULL);
                }
                break;

            default:
                CFCUtil_die("Invalid cmark node type: %d", (int)type);
                break;
        }
    }

    cmark_iter_free(iter);
    return result;
}