Пример #1
0
void
CFCParcel_add_class(CFCParcel *self, CFCClass *klass) {
    // Ensure unique class name.
    const char *class_name = CFCClass_get_name(klass);
    CFCClass *other = S_fetch_class(self, class_name, 2);
    if (other) {
        CFCUtil_die("Two classes with name %s", class_name);
    }

    const char *struct_sym = CFCClass_get_struct_sym(klass);
    const char *nickname   = CFCClass_get_nickname(klass);

    for (size_t i = 0; self->classes[i]; ++i) {
        CFCClass *other = self->classes[i];

        // Ensure unique struct symbol and nickname in parcel.
        if (strcmp(struct_sym, CFCClass_get_struct_sym(other)) == 0) {
            CFCUtil_die("Class name conflict between %s and %s",
                        CFCClass_get_name(klass), CFCClass_get_name(other));
        }
        if (strcmp(nickname, CFCClass_get_nickname(other)) == 0) {
            CFCUtil_die("Class nickname conflict between %s and %s",
                        CFCClass_get_name(klass), CFCClass_get_name(other));
        }
    }

    size_t num_classes = self->num_classes;
    size_t size = (num_classes + 2) * sizeof(CFCClass*);
    CFCClass **classes = (CFCClass**)REALLOCATE(self->classes, size);
    classes[num_classes]   = (CFCClass*)CFCBase_incref((CFCBase*)klass);
    classes[num_classes+1] = NULL;
    self->classes     = classes;
    self->num_classes = num_classes + 1;
}
Пример #2
0
char*
CFCGoClass_boilerplate_funcs(CFCGoClass *self) {
    char *content = NULL;
    if (!self->client) {
        CFCUtil_die("Can't find class for %s", self->class_name);
    }
    else if (CFCClass_inert(self->client)) {
        content = CFCUtil_strdup("");
    } else {
        const char *clownfish_dot = CFCParcel_is_cfish(self->parcel)
                                    ? "" : "clownfish.";
        const char *short_struct = CFCClass_get_struct_sym(self->client);
        char pattern[] =
            "func WRAP%s(ptr unsafe.Pointer) %s {\n"
            "\tobj := &%sIMP{}\n"
            "\tobj.INITOBJ(ptr)\n"
            "\treturn obj\n"
            "}\n"
            "\n"
            "func WRAP%sASOBJ(ptr unsafe.Pointer) %sObj {\n"
            "\treturn WRAP%s(ptr)\n"
            "}\n"
            ;

        content = CFCUtil_sprintf(pattern, short_struct, short_struct,
                                  short_struct, short_struct, clownfish_dot,
                                  short_struct);
    }
    return content;
}
Пример #3
0
static char*
S_gen_code_sample(CFCFunction *func, const char *alias, CFCClass *klass,
                  int is_constructor) {
    char *invocant = NULL;

    if (is_constructor) {
        invocant = CFCUtil_strdup(CFCClass_get_name(klass));
    }
    else {
        char *lower = S_camel_to_lower(CFCClass_get_struct_sym(klass));
        invocant = CFCUtil_sprintf("$%s", lower);
        FREEMEM(lower);
    }

    CFCParamList *param_list = CFCFunction_get_param_list(func);
    size_t        num_vars   = CFCParamList_num_vars(param_list);
    size_t        start      = is_constructor ? 0 : 1;
    char         *sample     = NULL;

    if (start == num_vars) {
        sample = CFCUtil_sprintf("    %s->%s();\n", invocant, alias);
    }
    else if (is_constructor || num_vars - start >= 2) {
        sample = S_gen_labeled_sample(invocant, alias, param_list, start);
    }
    else {
        sample = S_gen_positional_sample(invocant, alias, param_list, start);
    }

    return sample;
}
Пример #4
0
static CFCClass*
S_class_by_struct_sym(CFCParcel *self, const char *struct_sym,
                      size_t prefix_len) {
    // If prefix_len is 0, struct_sym is a short symbol without prefix.
    // Search for a class and check for ambiguity.
    //
    // If prefix_len is greater than 0, struct_sym is a full symbol with prefix.
    // Return a matching class as soon as it's found.

    if (prefix_len != 0
        && strncmp(self->prefix, struct_sym, prefix_len) != 0
       ) {
        return NULL;
    }

    const char *short_struct_sym = struct_sym + prefix_len;
    for (size_t i = 0; self->classes[i]; ++i) {
        CFCClass *klass = self->classes[i];
        if (strcmp(CFCClass_get_struct_sym(klass), short_struct_sym) == 0) {
            return klass;
        }
    }

    return NULL;
}
Пример #5
0
void
CFCType_resolve(CFCType *self, CFCClass **classes) {
    if (CFCType_is_composite(self)) {
        CFCType_resolve(self->child, classes);
        return;
    }
    if (!CFCType_is_object(self)) {
        return;
    }

    CFCClass *klass     = NULL;
    char     *specifier = self->specifier;

    if (isupper(self->specifier[0])) {
        // Try to find class from class list.
        for (size_t i = 0; classes[i]; ++i) {
            CFCClass   *maybe_class = classes[i];
            const char *struct_sym  = CFCClass_get_struct_sym(maybe_class);

            if (strcmp(specifier, struct_sym) == 0) {
                if (klass) {
                    CFCUtil_die("Type '%s' is ambigious", specifier);
                }
                klass = maybe_class;
            }
        }

        if (!klass) {
            CFCUtil_die("No class found for type '%s'", specifier);
        }

        // Create actual specifier with prefix.
        const char *prefix = CFCClass_get_prefix(klass);
        self->specifier = CFCUtil_sprintf("%s%s", prefix, specifier);
        FREEMEM(specifier);
    }
    else {
        // Try to find class from class list.
        for (size_t i = 0; classes[i]; ++i) {
            CFCClass *maybe_class = classes[i];
            const char *full_struct_sym
                = CFCClass_full_struct_sym(maybe_class);

            if (strcmp(specifier, full_struct_sym) == 0) {
                klass = maybe_class;
                break;
            }
        }
    }

    // Add parcel dependency.
    if (klass) {
        CFCParcel *class_parcel = CFCClass_get_parcel(klass);
        CFCParcel_add_dependent_parcel(self->parcel, class_parcel);
    }
}
Пример #6
0
char*
CFCGoClass_gen_wrap_func_reg(CFCGoClass *self) {
    if (CFCClass_inert(self->client)) {
        return CFCUtil_strdup("");
    }
    char pattern[] =
        "\t\tunsafe.Pointer(C.%s): WRAP%sASOBJ,\n";

    const char *short_struct = CFCClass_get_struct_sym(self->client);
    const char *class_var = CFCClass_full_class_var(self->client);
    return CFCUtil_sprintf(pattern, class_var, short_struct);
}
Пример #7
0
static char*
S_gen_code_sample(CFCCallable *func, const char *alias, CFCClass *klass,
                  int is_constructor) {
    char *prologue       = CFCUtil_sprintf("");
    char *class_var_name = S_camel_to_lower(CFCClass_get_struct_sym(klass));

    CFCType *ret_type = CFCCallable_get_return_type(func);
    if (!CFCType_is_void(ret_type)) {
        char *ret_name = S_perl_var_name(ret_type, is_constructor);

        if (!is_constructor && strcmp(ret_name, class_var_name) == 0) {
            // Return type equals `klass`. Use a generic variable name
            // to avoid confusing code samples like
            // `my $string = $string->trim`.
            prologue = CFCUtil_cat(prologue, "my $result = ", NULL);
        }
        else {
            prologue = CFCUtil_cat(prologue, "my $", ret_name, " = ", NULL);
        }

        FREEMEM(ret_name);
    }

    if (is_constructor) {
        const char *invocant = CFCClass_get_name(klass);
        prologue = CFCUtil_cat(prologue, invocant, NULL);
    }
    else {
        prologue = CFCUtil_cat(prologue, "$", class_var_name, NULL);
    }

    prologue = CFCUtil_cat(prologue, "->", alias, NULL);

    CFCParamList *param_list = CFCCallable_get_param_list(func);
    int           num_vars   = CFCParamList_num_vars(param_list);
    int           start      = is_constructor ? 0 : 1;
    char         *sample     = NULL;

    if (start == num_vars) {
        sample = CFCUtil_sprintf("    %s();\n", prologue);
    }
    else if (is_constructor || num_vars - start >= 2) {
        sample = S_gen_labeled_sample(prologue, param_list, start);
    }
    else {
        sample = S_gen_positional_sample(prologue, param_list, start);
    }

    FREEMEM(class_var_name);
    FREEMEM(prologue);
    return sample;
}
Пример #8
0
static char*
S_gen_type_linkups(CFCPython *self, CFCParcel *parcel, CFCClass **ordered) {
    char *handles  = CFCUtil_strdup("");
    char *py_types = CFCUtil_strdup("");
    int num_items = 0;

    for (size_t i = 0; ordered[i] != NULL; i++) {
        CFCClass *klass = ordered[i];
        if (CFCClass_included(klass) || CFCClass_inert(klass)) {
            continue;
        }
        const char *class_var = CFCClass_full_class_var(klass);
        const char *struct_sym = CFCClass_get_struct_sym(klass);
        char *handles_temp
            = CFCUtil_sprintf("%s    handles[%d]  = &%s;\n",
                              handles, num_items, class_var);
        char *py_types_temp
            = CFCUtil_sprintf("%s    py_types[%d] = &%s_pytype_struct;\n",
                              py_types, num_items, struct_sym);
        FREEMEM(handles);
        FREEMEM(py_types);
        handles  = handles_temp;
        py_types = py_types_temp;
        num_items++;
    }

    char pattern[] =
        "static void\n"
        "S_link_py_types(void) {\n"
        "    const int num_items = %d;\n"
        "    size_t handles_size  = num_items * sizeof(cfish_Class**);\n"
        "    size_t py_types_size = num_items * sizeof(PyTypeObject*);\n"
        "    cfish_Class ***handles  = (cfish_Class***)CFISH_MALLOCATE(handles_size);\n"
        "    PyTypeObject **py_types = (PyTypeObject**)CFISH_MALLOCATE(py_types_size);\n"
        "%s\n"
        "%s\n"
        "    CFBind_assoc_py_types(handles, py_types, num_items);\n"
        "    CFISH_FREEMEM(handles);\n"
        "    CFISH_FREEMEM(py_types);\n"
        "}\n"
        ;
    char *content = CFCUtil_sprintf(pattern, num_items, handles, py_types);

    FREEMEM(handles);
    FREEMEM(py_types);
    return content;
}
Пример #9
0
char*
CFCC_link_text(CFCUri *uri_obj) {
    char *link_text = NULL;
    CFCUriType type = CFCUri_get_type(uri_obj);

    switch (type) {
        case CFC_URI_ERROR: {
            const char *error = CFCUri_get_error(uri_obj);
            link_text = CFCUtil_sprintf("[%s]", error);
            break;
        }

        case CFC_URI_NULL:
            link_text = CFCUtil_strdup("NULL");
            break;

        case CFC_URI_CLASS: {
            CFCClass *klass = CFCUri_get_class(uri_obj);
            const char *src = CFCClass_included(klass)
                              ? CFCClass_get_name(klass)
                              : CFCClass_get_struct_sym(klass);
            link_text = CFCUtil_strdup(src);
            break;
        }

        case CFC_URI_FUNCTION:
        case CFC_URI_METHOD: {
            const char *name = CFCUri_get_callable_name(uri_obj);
            link_text = CFCUtil_sprintf("%s()", name);
            break;
        }

        case CFC_URI_DOCUMENT: {
            CFCDocument *doc = CFCUri_get_document(uri_obj);
            const char *name = CFCDocument_get_name(doc);
            link_text = CFCUtil_strdup(name);
            break;
        }

        default:
            CFCUtil_die("Unsupported node type: %d", type);
            break;
    }

    return link_text;
}
Пример #10
0
char*
CFCGoClass_gen_ctors(CFCGoClass *self) {
    CFCFunction *ctor_func = CFCClass_function(self->client, "new");
    if (self->suppress_ctor
        || !ctor_func
        || !CFCFunction_can_be_bound(ctor_func)
       ) {
        return CFCUtil_strdup("");
    }
    CFCParcel    *parcel     = CFCClass_get_parcel(self->client);
    CFCParamList *param_list = CFCFunction_get_param_list(ctor_func);
    CFCType      *ret_type   = CFCFunction_get_return_type(ctor_func);
    const char   *struct_sym = CFCClass_get_struct_sym(self->client);
    char         *name       = CFCUtil_sprintf("New%s", struct_sym);
    char         *cfunc  = CFCFunction_full_func_sym(ctor_func, self->client);
    char         *cfargs = CFCGoFunc_ctor_cfargs(parcel, param_list);
    char *first_line
        = CFCGoFunc_ctor_start(parcel, name, param_list, ret_type);
    char *ret_statement
        = CFCGoFunc_return_statement(parcel, ret_type, "retvalCF");

    char pattern[] =
        "%s"
        "\tretvalCF := C.%s(%s)\n"
        "%s"
        "}\n"
        ;
    char *content = CFCUtil_sprintf(pattern, first_line, cfunc,
                                    cfargs, ret_statement);

    FREEMEM(ret_statement);
    FREEMEM(cfargs);
    FREEMEM(cfunc);
    FREEMEM(first_line);
    FREEMEM(name);
    return content;
}
Пример #11
0
char*
CFCGoClass_go_typing(CFCGoClass *self) {
    char *content = NULL;
    if (!self->client) {
        CFCUtil_die("Can't find class for %s", self->class_name);
    }
    else if (CFCClass_inert(self->client)) {
        content = CFCUtil_strdup("");
    } else {
        const char *short_struct = CFCClass_get_struct_sym(self->client);

        CFCClass *parent = CFCClass_get_parent(self->client);
        char *parent_type_str = NULL;
        if (parent) {
            const char *parent_struct = CFCClass_get_struct_sym(parent);
            CFCParcel *parent_parcel = CFCClass_get_parcel(parent);
            if (parent_parcel == self->parcel) {
                parent_type_str = CFCUtil_strdup(parent_struct);
            }
            else {
                char *parent_package
                    = CFCGoTypeMap_go_short_package(parent_parcel);
                parent_type_str = CFCUtil_sprintf("%s.%s", parent_package,
                                                  parent_struct);
                FREEMEM(parent_package);
            }
        }

        char *go_struct_def;
        if (parent && !self->suppress_struct) {
            go_struct_def
                = CFCUtil_sprintf("type %sIMP struct {\n\t%sIMP\n}\n",
                                  short_struct, parent_type_str);
        }
        else {
            go_struct_def = CFCUtil_strdup("");
        }

        char *parent_iface;
        if (parent) {
            parent_iface = CFCUtil_sprintf("\t%s\n", parent_type_str);
        }
        else {
            parent_iface = CFCUtil_strdup("");
        }

        char *novel_iface = CFCUtil_strdup("");
        S_lazy_init_method_bindings(self);
        for (int i = 0; self->method_bindings[i] != NULL; i++) {
            CFCGoMethod *meth_binding = self->method_bindings[i];
            CFCMethod *method = CFCGoMethod_get_client(meth_binding);
            if (method) {
                if (!CFCMethod_novel(method)) {
                    continue;
                }
                const char *sym = CFCMethod_get_name(method);
                if (!CFCClass_fresh_method(self->client, sym)) {
                    continue;
                }
            }

            const char *sig = CFCGoMethod_get_sig(meth_binding, self->client);
            novel_iface = CFCUtil_cat(novel_iface, "\t", sig, "\n", NULL);
        }

        char pattern[] =
            "type %s interface {\n"
            "%s"
            "%s"
            "}\n"
            "\n"
            "%s"
            ;
        content = CFCUtil_sprintf(pattern, short_struct, parent_iface,
                                  novel_iface, go_struct_def);
        FREEMEM(parent_type_str);
        FREEMEM(go_struct_def);
        FREEMEM(parent_iface);
    }
    return content;
}
Пример #12
0
static void
S_write_module_file(CFCPython *self, CFCParcel *parcel, const char *dest) {
    const char *parcel_name = CFCParcel_get_name(parcel);
    char *pymod_name = CFCUtil_strdup(parcel_name);
    // TODO: Stop lowercasing when parcels are restricted to lowercase.
    for (int i = 0; pymod_name[i] != '\0'; i++) {
        pymod_name[i] = tolower(pymod_name[i]);
    }
    const char *last_dot = strrchr(pymod_name, '.');
    const char *last_component = last_dot != NULL
                                 ? last_dot + 1
                                 : pymod_name;
    char *helper_mod_name = CFCUtil_sprintf("%s._%s", pymod_name, last_component);
    for (int i = 0; helper_mod_name[i] != '\0'; i++) {
        helper_mod_name[i] = tolower(helper_mod_name[i]);
    }

    CFCClass  **ordered = CFCHierarchy_ordered_classes(self->hierarchy);
    CFCParcel **parcels = CFCParcel_all_parcels();
    char *callbacks          = S_gen_callbacks(self, parcel, ordered);
    char *type_linkups       = S_gen_type_linkups(self, parcel, ordered);
    char *pound_includes     = CFCUtil_strdup("");
    char *class_bindings     = S_gen_class_bindings(self, parcel, pymod_name, ordered);
    char *parcel_boots       = CFCUtil_strdup("");
    char *pytype_ready_calls = CFCUtil_strdup("");
    char *module_adds        = CFCUtil_strdup("");

    // Add parcel bootstrapping calls.
    for (size_t i = 0; parcels[i]; ++i) {
        if (!CFCParcel_included(parcels[i])) {
            const char *prefix = CFCParcel_get_prefix(parcels[i]);
            parcel_boots = CFCUtil_cat(parcel_boots, "    ", prefix,
                                       "bootstrap_parcel();\n", NULL);
        }
    }

    for (size_t i = 0; ordered[i] != NULL; i++) {
        CFCClass *klass = ordered[i];
        if (CFCClass_included(klass)) { continue; }
        const char *struct_sym = CFCClass_get_struct_sym(klass);

        const char *include_h  = CFCClass_include_h(klass);
        pound_includes = CFCUtil_cat(pound_includes, "#include \"",
                                     include_h, "\"\n", NULL);

        // The PyType_Ready invocations for instantiable classes are handled
        // via bootstrapping of Clownfish Class objects.  Since inert classes
        // do not at present have Class objects, we need to handle their
        // PyType_Ready calls independently.
        if (CFCClass_inert(klass)) {
            pytype_ready_calls = CFCUtil_cat(pytype_ready_calls,
                "    if (PyType_Ready(&", struct_sym,
                "_pytype_struct) < 0) { return NULL; }\n", NULL);
        }

        module_adds = CFCUtil_cat(module_adds, "    PyModule_AddObject(module, \"",
                                  struct_sym, "\", (PyObject*)&", struct_sym,
                                  "_pytype_struct);\n", NULL);
    }

    const char pattern[] =
        "%s\n"
        "\n"
        "#include \"Python.h\"\n"
        "#include \"cfish_parcel.h\"\n"
        "#include \"CFBind.h\"\n"
        "%s\n"
        "\n"
        "%s\n" // callbacks
        "\n"
        "static PyModuleDef module_def = {\n"
        "    PyModuleDef_HEAD_INIT,\n"
        "    \"%s\",\n" // module name
        "    NULL,\n" // docstring
        "    -1,\n"
        "    NULL, NULL, NULL, NULL, NULL\n"
        "};\n"
        "\n"
        "%s" // class bindings
        "\n"
        "%s" // S_link_py_types function
        "\n"
        "PyMODINIT_FUNC\n"
        "PyInit__%s(void) {\n"
        "    cfish_Class_bootstrap_hook1 = CFBind_class_bootstrap_hook1;\n"
        "\n"
        "%s\n" // PyType_Ready calls
        "\n"
        "    S_link_py_types();\n"
        "\n"
        "%s\n" // parcel boots
        "\n"
        "    PyObject *module = PyModule_Create(&module_def);\n"
        "%s\n" // Add types to module
        "\n"
        "    return module;\n"
        "}\n"
        "\n"
        "%s\n"
        "\n";

    char *content
        = CFCUtil_sprintf(pattern, self->header, pound_includes, callbacks,
                          helper_mod_name, class_bindings, type_linkups,
                          last_component, pytype_ready_calls, parcel_boots,
                          module_adds, self->footer);

    char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "_%s.c", dest,
                                     last_component);
    CFCUtil_write_if_changed(filepath, content, strlen(content));
    FREEMEM(filepath);

    FREEMEM(content);
    FREEMEM(module_adds);
    FREEMEM(pytype_ready_calls);
    FREEMEM(parcel_boots);
    FREEMEM(class_bindings);
    FREEMEM(helper_mod_name);
    FREEMEM(pymod_name);
    FREEMEM(pound_includes);
    FREEMEM(type_linkups);
    FREEMEM(callbacks);
    FREEMEM(ordered);
}
Пример #13
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;
}
Пример #14
0
static void
S_resolve(CFCUri *self, const char *parcel, const char *struct_sym,
          const char *callable) {

    // Try to find a CFCClass.
    CFCClass *doc_class = self->doc_class;
    CFCClass *klass     = NULL;

    if (parcel) {
        char *full_struct_sym = CFCUtil_sprintf("%s_%s", parcel, struct_sym);
        klass = CFCClass_fetch_by_struct_sym(full_struct_sym);
        FREEMEM(full_struct_sym);
    }
    else if (struct_sym && doc_class) {
        const char *prefix = CFCClass_get_prefix(doc_class);
        char *full_struct_sym = CFCUtil_sprintf("%s%s", prefix, struct_sym);
        klass = CFCClass_fetch_by_struct_sym(full_struct_sym);
        FREEMEM(full_struct_sym);
    }
    else if (callable) {
        klass = doc_class;
    }

    if (klass) {
        if (!CFCClass_public(klass)) {
            CFCUtil_warn("Non-public class '%s' in Clownfish URI: %s",
                          CFCClass_get_struct_sym(klass), self->string);
        }

        self->type  = CFC_URI_CLASS;
        self->klass = klass;
        CFCBase_incref((CFCBase*)klass);

        if (callable) {
            if (islower(callable[0])) {
                CFCFunction *function = CFCClass_function(klass, callable);

                if (!function) {
                    CFCUtil_warn("Unknown function '%s' in Clownfish URI: %s",
                                 callable, self->string);
                }
                else if (!CFCFunction_public(function)) {
                    CFCUtil_warn("Non-public function '%s' in Clownfish URI:"
                                 " %s", callable, self->string);
                }

                self->type     = CFC_URI_FUNCTION;
                self->callable = CFCUtil_strdup(callable);
            }
            else {
                CFCMethod *method = CFCClass_method(klass, callable);

                if (!method) {
                    CFCUtil_warn("Unknown method '%s' in Clownfish URI: %s",
                                 callable, self->string);
                }
                else if (!CFCMethod_public(method)) {
                    CFCUtil_warn("Non-public method '%s' in Clownfish URI:"
                                 " %s", callable, self->string);
                }

                self->type     = CFC_URI_METHOD;
                self->callable = CFCUtil_strdup(callable);
            }
        }

        return;
    }

    // Try to find a CFCDocument.
    if (!parcel && struct_sym && !callable) {
        CFCDocument *doc = CFCDocument_fetch(struct_sym);

        if (doc) {
            self->type     = CFC_URI_DOCUMENT;
            self->document = doc;
            CFCBase_incref((CFCBase*)doc);
            return;
        }
    }

    S_set_error(self, "Couldn't resolve Clownfish URI");
}