Beispiel #1
0
void
CFCHierarchy_build(CFCHierarchy *self) {
    // Read .cfp files.
    for (size_t i = 0; self->sources[i] != NULL; i++) {
        S_parse_source_cfp_files(self, self->sources[i]);
    }

    CFCParcel **parcels = CFCParcel_all_parcels();

    // Read .cfh files of included parcels.
    for (size_t i = 0; parcels[i] != NULL; i++) {
        CFCParcel *parcel = parcels[i];
        if (CFCParcel_included(parcel)) {
            const char *source_dir = CFCParcel_get_source_dir(parcel);
            S_parse_cf_files(self, source_dir, true);
        }
    }

    // Read .cfh and .md files of source parcels.
    for (size_t i = 0; self->sources[i] != NULL; i++) {
        S_parse_cf_files(self, self->sources[i], false);
        S_find_doc_files(self->sources[i]);
    }

    // It's important that prereq parcels are processed first.
    for (size_t i = 0; parcels[i] != NULL; i++) {
        CFCParcel_connect_and_sort_classes(parcels[i]);
    }

    for (size_t i = 0; self->trees[i] != NULL; i++) {
        CFCClass_grow_tree(self->trees[i]);
    }
}
Beispiel #2
0
void
CFCHierarchy_read_host_data_json(CFCHierarchy *self, const char *host_lang) {
    CHY_UNUSED_VAR(self);
    CFCParcel **parcels = CFCParcel_all_parcels();

    for (int i = 0; parcels[i]; ++i) {
        CFCParcel *parcel = parcels[i];
        if (CFCParcel_included(parcel)) {
            CFCParcel_read_host_data_json(parcel, host_lang);
        }
    }
}
Beispiel #3
0
void
CFCPerl_write_host_code(CFCPerl *self) {
    CFCParcel **parcels = CFCParcel_all_parcels();

    for (size_t i = 0; parcels[i]; ++i) {
        CFCParcel *parcel = parcels[i];

        if (!CFCParcel_included(parcel)) {
            S_write_host_h(self, parcel);
            S_write_host_c(self, parcel);
        }
    }
}
Beispiel #4
0
int
CFCBindCore_write_all_modified(CFCBindCore *self, int modified) {
    CFCHierarchy *hierarchy = self->hierarchy;
    const char   *header    = self->c_header;
    const char   *footer    = self->c_footer;

    // Discover whether files need to be regenerated.
    modified = CFCHierarchy_propagate_modified(hierarchy, modified);

    // Iterate over all File objects, writing out those which don't have
    // up-to-date auto-generated files.
    const char *inc_dest = CFCHierarchy_get_include_dest(hierarchy);
    CFCFile **files = CFCHierarchy_files(hierarchy);
    for (int i = 0; files[i] != NULL; i++) {
        if (CFCFile_get_modified(files[i])) {
            CFCBindFile_write_h(files[i], inc_dest, header, footer);
        }
    }

    // If any class definition has changed, rewrite the parcel.h and parcel.c
    // files.
    if (modified) {
        S_write_platform_h(self);

        CFCParcel **parcels = CFCParcel_all_parcels();
        for (size_t i = 0; parcels[i]; ++i) {
            CFCParcel *parcel = parcels[i];
            if (CFCParcel_required(parcel)) {
                S_write_parcel_h(self, parcel);
                if (!CFCParcel_included(parcel)) {
                    S_write_parcel_c(self, parcel);
                }
            }
        }
    }

    return modified;
}
Beispiel #5
0
char*
CFCGoTypeMap_go_type_name(CFCType *type, CFCParcel *current_parcel) {
    if (CFCType_cfish_obj(type)) {
        return CFCUtil_strdup("interface{}");
    }
    else if (CFCType_cfish_string(type)) {
        return CFCUtil_strdup("string");
    }
    else if (CFCType_cfish_blob(type)) {
        return CFCUtil_strdup("[]byte");
    }
    else if (CFCType_cfish_vector(type)) {
        return CFCUtil_strdup("[]interface{}");
    }
    else if (CFCType_cfish_hash(type)) {
        return CFCUtil_strdup("map[string]interface{}");
    }
    else if (CFCType_is_object(type)) {
        // Divide the specifier into prefix and struct name.
        const char *specifier  = CFCType_get_specifier(type);
        size_t      prefix_len = 0;
        for (size_t max = strlen(specifier); prefix_len < max; prefix_len++) {
            if (isupper(specifier[prefix_len])) {
                break;
            }
        }
        if (!prefix_len) {
            CFCUtil_die("Can't convert object type name '%s'", specifier);
        }
        const char *struct_sym = specifier + prefix_len;

        // Find the parcel that the type lives in.
        CFCParcel** all_parcels = CFCParcel_all_parcels();
        CFCParcel *parcel = NULL;
        for (int i = 0; all_parcels[i] != NULL; i++) {
            const char *candidate = CFCParcel_get_prefix(all_parcels[i]);
            if (strncmp(candidate, specifier, prefix_len) == 0
                && strlen(candidate) == prefix_len
               ) {
                parcel = all_parcels[i];
                break;
            }
        }
        if (!parcel) {
            CFCUtil_die("Can't find parcel for type '%s'", specifier);
        }

        // If the type lives in this parcel, return only the struct sym
        // without a go package prefix.
        if (parcel == current_parcel) {
            return CFCUtil_strdup(struct_sym);
        }

        // The type lives in another parcel, so prefix its Go package name.
        // TODO: Stop downcasing once Clownfish parcel names are constrained
        // to lower case.
        const char *package_name = CFCParcel_get_name(parcel);
        if (strrchr(package_name, '.')) {
            package_name = strrchr(package_name, '.') + 1;
        }
        char *result = CFCUtil_sprintf("%s.%s", package_name, struct_sym);
        for (int i = 0; result[i] != '.'; i++) {
            result[i] = tolower(result[i]);
        }
        return result;
    }
    else if (CFCType_is_primitive(type)) {
        const char *specifier = CFCType_get_specifier(type);
        for (int i = 0; i < num_conversions; i++) {
            if (strcmp(specifier, conversions[i].c) == 0) {
                return CFCUtil_strdup(conversions[i].go);
            }
        }
    }

    return NULL;
}
Beispiel #6
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);
}
Beispiel #7
0
static void
S_find_prereq(CFCHierarchy *self, CFCParcel *parent, CFCPrereq *prereq) {
    const char *name        = CFCPrereq_get_name(prereq);
    CFCVersion *min_version = CFCPrereq_get_version(prereq);

    // Check whether prereq was processed already.
    CFCParcel **parcels = CFCParcel_all_parcels();
    for (int i = 0; parcels[i]; ++i) {
        CFCParcel *parcel = parcels[i];
        const char *other_name = CFCParcel_get_name(parcel);

        if (strcmp(other_name, name) == 0) {
            CFCVersion *other_version = CFCParcel_get_version(parcel);
            CFCVersion *major_version = CFCParcel_get_major_version(parcel);

            if (CFCVersion_compare_to(major_version, min_version) <= 0
                && CFCVersion_compare_to(min_version, other_version) <= 0
               ) {
                // Compatible version found.
                return;
            }
            else {
                CFCUtil_die("Parcel %s %s required by %s not compatible with"
                            " version %s required by %s",
                            name, other_version, "[TODO]",
                            CFCVersion_get_vstring(min_version),
                            CFCParcel_get_name(parent));
            }
        }
    }

    CFCParcel *parcel = NULL;

    // TODO: Decide whether to prefer higher versions from directories
    // that come later in the list of include dirs or stop processing once
    // a suitable version was found in a dir.
    for (size_t i = 0; self->includes[i] != NULL; i++) {
        char *name_dir = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s",
                                         self->includes[i], name);

        if (CFCUtil_is_dir(name_dir)) {
            void *dirhandle = CFCUtil_opendir(name_dir);
            const char *entry = NULL;

            while (NULL != (entry = CFCUtil_dirnext(dirhandle))) {
                if (!CFCVersion_is_vstring(entry)) { continue; }

                char *version_dir = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s",
                                                    name_dir, entry);

                if (CFCUtil_is_dir(version_dir)) {
                    parcel = S_audition_parcel(version_dir, entry, min_version,
                                               parcel);
                }

                FREEMEM(version_dir);
            }

            CFCUtil_closedir(dirhandle, name_dir);
        }

        FREEMEM(name_dir);
    }

    if (parcel == NULL) {
        CFCUtil_die("Parcel %s %s required by %s not found",
                    name, CFCVersion_get_vstring(min_version),
                    CFCParcel_get_name(parent));
    }

    // Make sure to register prereq parcels first, so that prereq
    // parent classes precede subclasses.
    S_find_prereqs(self, parcel);

    CFCParcel_register(parcel);

    CFCBase_decref((CFCBase*)parcel);
}
Beispiel #8
0
static void
S_write_boot_c(CFCPerl *self) {
    CFCClass  **ordered   = CFCHierarchy_ordered_classes(self->hierarchy);
    CFCParcel **parcels   = CFCParcel_all_parcels();
    char *pound_includes  = CFCUtil_strdup("");
    char *bootstrap_code  = CFCUtil_strdup("");
    char *alias_adds      = CFCUtil_strdup("");
    char *isa_pushes      = CFCUtil_strdup("");

    for (size_t i = 0; parcels[i]; ++i) {
        if (!CFCParcel_included(parcels[i])) {
            const char *prefix = CFCParcel_get_prefix(parcels[i]);
            bootstrap_code = CFCUtil_cat(bootstrap_code, "    ", 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 *class_name = CFCClass_get_class_name(klass);
        const char *include_h  = CFCClass_include_h(klass);
        pound_includes = CFCUtil_cat(pound_includes, "#include \"",
                                     include_h, "\"\n", NULL);

        if (CFCClass_inert(klass)) { continue; }

        // Add aliases for selected KinoSearch classes which allow old indexes
        // to be read.
        CFCPerlClass *class_binding = CFCPerlClass_singleton(class_name);
        if (class_binding) {
            const char *vtable_var = CFCClass_full_vtable_var(klass);
            const char **aliases
                = CFCPerlClass_get_class_aliases(class_binding);
            for (size_t j = 0; aliases[j] != NULL; j++) {
                const char *alias = aliases[j];
                size_t alias_len  = strlen(alias);
                const char pattern[] =
                    "    cfish_VTable_add_alias_to_registry("
                    "%s, \"%s\", %u);\n";
                char *alias_add
                    = CFCUtil_sprintf(pattern, vtable_var, alias,
                                      (unsigned)alias_len);
                alias_adds = CFCUtil_cat(alias_adds, alias_add, NULL);
                FREEMEM(alias_add);
            }

            char *metadata_code
                = CFCPerlClass_method_metadata_code(class_binding);
            alias_adds = CFCUtil_cat(alias_adds, metadata_code, NULL);
            FREEMEM(metadata_code);
        }

        CFCClass *parent = CFCClass_get_parent(klass);
        if (parent) {
            const char *parent_class_name = CFCClass_get_class_name(parent);
            isa_pushes
                = CFCUtil_cat(isa_pushes, "    isa = get_av(\"",
                              class_name, "::ISA\", 1);\n", NULL);
            isa_pushes
                = CFCUtil_cat(isa_pushes, "    av_push(isa, newSVpv(\"", 
                              parent_class_name, "\", 0));\n", NULL);
        }
    }

    const char pattern[] =
        "%s\n"
        "\n"
        "#include \"cfish_parcel.h\"\n"
        "#include \"EXTERN.h\"\n"
        "#include \"perl.h\"\n"
        "#include \"XSUB.h\"\n"
        "#include \"boot.h\"\n"
        "#include \"Clownfish/String.h\"\n"
        "#include \"Clownfish/VTable.h\"\n"
        "%s\n"
        "\n"
        "void\n"
        "%s() {\n"
        "%s"
        "\n"
        "%s"
        "\n"
        "    AV *isa;\n"
        "%s"
        "}\n"
        "\n"
        "%s\n"
        "\n";
    char *content
        = CFCUtil_sprintf(pattern, self->header, pound_includes,
                          self->boot_func, bootstrap_code, alias_adds,
                          isa_pushes, self->footer);

    const char *src_dest = CFCHierarchy_get_source_dest(self->hierarchy);
    char *boot_c_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "boot.c", src_dest);
    CFCUtil_write_file(boot_c_path, content, strlen(content));
    FREEMEM(boot_c_path);

    FREEMEM(content);
    FREEMEM(isa_pushes);
    FREEMEM(alias_adds);
    FREEMEM(bootstrap_code);
    FREEMEM(pound_includes);
    FREEMEM(parcels);
    FREEMEM(ordered);
}