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]); } }
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); } } }
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); } } }
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; }
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; }
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); }
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); }
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); }