static CFCParcel* S_audition_parcel(const char *version_dir, const char *vstring, CFCVersion *min_version, CFCParcel *best) { CFCVersion *version = CFCVersion_new(vstring); CFCVersion *best_version = best ? CFCParcel_get_version(best) : NULL; // Version must match min_version and be greater than the previous best. if (CFCVersion_compare_to(version, min_version) >= 0 && (best_version == NULL || CFCVersion_compare_to(version, best_version) > 0) ) { // Parse parcel JSON for major version check. CFCFileSpec *file_spec = CFCFileSpec_new(version_dir, "parcel", ".json", true); CFCParcel *parcel = CFCParcel_new_from_file(file_spec); CFCVersion *major_version = CFCParcel_get_major_version(parcel); if (CFCVersion_compare_to(major_version, min_version) <= 0) { CFCBase_decref((CFCBase*)best); best = parcel; } else { CFCBase_decref((CFCBase*)parcel); } CFCBase_decref((CFCBase*)file_spec); } CFCBase_decref((CFCBase*)version); return best; }
static void S_run_tests(CFCTest *test) { { CFCFileSpec *file_spec = CFCFileSpec_new("Clownfish/_include", "Stuff/Thing", ".cfh", 0); STR_EQ(test, CFCFileSpec_get_source_dir(file_spec), "Clownfish/_include", "get_source_dir"); STR_EQ(test, CFCFileSpec_get_path_part(file_spec), "Stuff/Thing", "get_path_part"); OK(test, !CFCFileSpec_included(file_spec), "not included"); CFCBase_decref((CFCBase*)file_spec); } { CFCFileSpec *file_spec = CFCFileSpec_new("Clownfish/_include", "Stuff/Thing", ".cfh", 1); OK(test, CFCFileSpec_included(file_spec), "included"); CFCBase_decref((CFCBase*)file_spec); } }
static void S_parse_cf_files(CFCHierarchy *self, const char *source_dir, int is_included) { CFCFindFilesContext context; context.ext = ".cfh"; context.paths = (char**)CALLOCATE(1, sizeof(char*)); context.num_paths = 0; CFCUtil_walk(source_dir, S_find_files, &context); // Process any file that has at least one class declaration. for (int i = 0; context.paths[i] != NULL; i++) { // Derive the name of the class that owns the module file. char *source_path = context.paths[i]; char *path_part = S_extract_path_part(source_path, source_dir, ".cfh"); // Ignore hidden files. if (path_part[0] == '.' || strstr(path_part, CHY_DIR_SEP ".") != NULL) { continue; } CFCFileSpec *file_spec = CFCFileSpec_new(source_dir, path_part, ".cfh", is_included); // Slurp and parse file. size_t unused; char *content = CFCUtil_slurp_text(source_path, &unused); CFCFile *file = CFCParser_parse_file(self->parser, content, file_spec); FREEMEM(content); if (!file) { int lineno = CFCParser_get_lineno(self->parser); CFCUtil_die("%s:%d: parser error", source_path, lineno); } // Make sure path_part is unique because the name of the generated // C header is derived from it. CFCFile *existing = S_fetch_file(self, path_part); if (existing) { CFCUtil_die("File %s.cfh found twice in %s and %s", path_part, CFCFile_get_source_dir(existing), source_dir); } S_add_file(self, file); CFCBase_decref((CFCBase*)file); CFCBase_decref((CFCBase*)file_spec); FREEMEM(path_part); } CFCUtil_free_string_array(context.paths); }
static void S_parse_source_cfp_files(CFCHierarchy *self, const char *source_dir) { CFCFindFilesContext context; context.ext = ".cfp"; context.paths = (char**)CALLOCATE(1, sizeof(char*)); context.num_paths = 0; CFCUtil_walk(source_dir, S_find_files, &context); // Parse .cfp files and register the parcels they define. for (int i = 0; context.paths[i] != NULL; i++) { const char *path = context.paths[i]; char *path_part = S_extract_path_part(path, source_dir, ".cfp"); CFCFileSpec *file_spec = CFCFileSpec_new(source_dir, path_part, ".cfp", false); CFCParcel *parcel = CFCParcel_new_from_file(file_spec); const char *name = CFCParcel_get_name(parcel); CFCParcel *existing = CFCParcel_fetch(name); if (existing) { CFCUtil_die("Parcel '%s' defined twice in %s and %s", CFCParcel_get_name(parcel), CFCParcel_get_cfp_path(existing), path); } // 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); CFCBase_decref((CFCBase*)file_spec); FREEMEM(path_part); } CFCUtil_free_string_array(context.paths); }
static void S_run_tests(CFCTest *test) { #define STUFF_THING "Stuff" CHY_DIR_SEP "Thing" CFCParser *parser = CFCParser_new(); CFCFileSpec *file_spec = CFCFileSpec_new(".", STUFF_THING, 0); { const char *string = "parcel Stuff;\n" "class Stuff::Thing {\n" " Foo *foo;\n" " Bar *bar;\n" "}\n" "class Foo {}\n" "class Bar {}\n" "__C__\n" "int foo;\n" "__END_C__\n"; CFCFile *file = CFCParser_parse_file(parser, string, file_spec); STR_EQ(test, CFCFile_get_source_dir(file), ".", "get_source_dir"); STR_EQ(test, CFCFile_get_path_part(file), STUFF_THING, "get_path_part"); OK(test, !CFCFile_included(file), "included"); STR_EQ(test, CFCFile_guard_name(file), "H_STUFF_THING", "guard_name"); STR_EQ(test, CFCFile_guard_start(file), "#ifndef H_STUFF_THING\n#define H_STUFF_THING 1\n", "guard_start"); STR_EQ(test, CFCFile_guard_close(file), "#endif /* H_STUFF_THING */\n", "guard_close"); OK(test, !CFCFile_get_modified(file), "modified false at start"); CFCFile_set_modified(file, 1); OK(test, CFCFile_get_modified(file), "set_modified, get_modified"); #define PATH_TO_STUFF_THING \ "path" CHY_DIR_SEP \ "to" CHY_DIR_SEP \ "Stuff" CHY_DIR_SEP \ "Thing" char *cfh_path = CFCFile_cfh_path(file, "path/to"); STR_EQ(test, cfh_path, PATH_TO_STUFF_THING ".cfh", "cfh_path"); FREEMEM(cfh_path); char *c_path = CFCFile_c_path(file, "path/to"); STR_EQ(test, c_path, PATH_TO_STUFF_THING ".c", "c_path"); FREEMEM(c_path); char *h_path = CFCFile_h_path(file, "path/to"); STR_EQ(test, h_path, PATH_TO_STUFF_THING ".h", "h_path"); FREEMEM(h_path); CFCClass **classes = CFCFile_classes(file); OK(test, classes[0] != NULL && classes[1] != NULL && classes[2] != NULL && classes[3] == NULL, "classes() filters blocks"); CFCVariable **member_vars = CFCClass_fresh_member_vars(classes[0]); CFCType *foo_type = CFCVariable_get_type(member_vars[0]); CFCType_resolve(foo_type); STR_EQ(test, CFCType_get_specifier(foo_type), "stuff_Foo", "file production picked up parcel def"); CFCType *bar_type = CFCVariable_get_type(member_vars[1]); CFCType_resolve(bar_type); STR_EQ(test, CFCType_get_specifier(bar_type), "stuff_Bar", "parcel def is sticky"); CFCParcel *parcel = CFCFile_get_parcel(file); STR_EQ(test, CFCParcel_get_name(parcel), "Stuff", "get_parcel"); CFCBase **blocks = CFCFile_blocks(file); STR_EQ(test, CFCBase_get_cfc_class(blocks[0]), "Clownfish::CFC::Model::Class", "blocks[0]"); STR_EQ(test, CFCBase_get_cfc_class(blocks[1]), "Clownfish::CFC::Model::Class", "blocks[1]"); STR_EQ(test, CFCBase_get_cfc_class(blocks[2]), "Clownfish::CFC::Model::Class", "blocks[2]"); STR_EQ(test, CFCBase_get_cfc_class(blocks[3]), "Clownfish::CFC::Model::CBlock", "blocks[3]"); OK(test, blocks[4] == NULL, "blocks[4]"); CFCBase_decref((CFCBase*)file); CFCClass_clear_registry(); } CFCBase_decref((CFCBase*)file_spec); CFCBase_decref((CFCBase*)parser); CFCParcel_reap_singletons(); }
static void S_run_parcel_tests(CFCTest *test) { { CFCParcel *parcel = CFCParcel_new("Foo", NULL, NULL, NULL); OK(test, parcel != NULL, "new"); OK(test, !CFCParcel_included(parcel), "not included"); CFCBase_decref((CFCBase*)parcel); } { CFCFileSpec *file_spec = CFCFileSpec_new(".", "Parcel", true); CFCParcel *parcel = CFCParcel_new("Foo", NULL, NULL, file_spec); OK(test, CFCParcel_included(parcel), "included"); CFCBase_decref((CFCBase*)parcel); CFCBase_decref((CFCBase*)file_spec); } { const char *json = " {\n" " \"name\": \"Crustacean\",\n" " \"nickname\": \"Crust\",\n" " \"version\": \"v0.1.0\"\n" " }\n"; CFCParcel *parcel = CFCParcel_new_from_json(json, NULL); OK(test, parcel != NULL, "new_from_json"); CFCBase_decref((CFCBase*)parcel); } { const char *path = "t" CHY_DIR_SEP "cfbase" CHY_DIR_SEP "Animal.cfp"; CFCParcel *parcel = CFCParcel_new_from_file(path, NULL); OK(test, parcel != NULL, "new_from_file"); CFCBase_decref((CFCBase*)parcel); } { CFCParcel *parcel = CFCParcel_new("Crustacean", "Crust", NULL, NULL); CFCParcel_register(parcel); STR_EQ(test, CFCVersion_get_vstring(CFCParcel_get_version(parcel)), "v0", "get_version"); CFCBase_decref((CFCBase*)parcel); CFCParcel_reap_singletons(); } { const char *json = " {\n" " \"name\": \"Crustacean\",\n" " \"version\": \"v0.1.0\",\n" " \"prerequisites\": {\n" " \"Clownfish\": null,\n" " \"Arthropod\": \"v30.104.5\"\n" " }\n" " }\n"; CFCParcel *parcel = CFCParcel_new_from_json(json, NULL); CFCPrereq **prereqs = CFCParcel_get_prereqs(parcel); OK(test, prereqs != NULL, "prereqs"); CFCPrereq *cfish = prereqs[0]; OK(test, cfish != NULL, "prereqs[0]"); const char *cfish_name = CFCPrereq_get_name(cfish); STR_EQ(test, cfish_name, "Clownfish", "prereqs[0] name"); CFCVersion *v0 = CFCVersion_new("v0"); CFCVersion *cfish_version = CFCPrereq_get_version(cfish); INT_EQ(test, CFCVersion_compare_to(cfish_version, v0), 0, "prereqs[0] version"); CFCPrereq *apod = prereqs[1]; OK(test, apod != NULL, "prereqs[1]"); const char *apod_name = CFCPrereq_get_name(apod); STR_EQ(test, apod_name, "Arthropod", "prereqs[1] name"); CFCVersion *v30_104_5 = CFCVersion_new("v30.104.5"); CFCVersion *apod_version = CFCPrereq_get_version(apod); INT_EQ(test, CFCVersion_compare_to(apod_version, v30_104_5), 0, "prereqs[1] version"); OK(test, prereqs[2] == NULL, "prereqs[2]"); CFCBase_decref((CFCBase*)v30_104_5); CFCBase_decref((CFCBase*)v0); CFCBase_decref((CFCBase*)parcel); } { CFCFileSpec *foo_file_spec = CFCFileSpec_new(".", "Foo", true); CFCParcel *foo = CFCParcel_new("Foo", NULL, NULL, foo_file_spec); CFCParcel_register(foo); CFCVersion *cfish_version = CFCVersion_new("v0.8.7"); CFCFileSpec *cfish_file_spec = CFCFileSpec_new(".", "Clownfish", true); CFCParcel *cfish = CFCParcel_new("Clownfish", NULL, cfish_version, cfish_file_spec); CFCParcel_register(cfish); const char *crust_json = " {\n" " \"name\": \"Crustacean\",\n" " \"version\": \"v0.1.0\",\n" " \"prerequisites\": {\n" " \"Clownfish\": \"v0.8.5\",\n" " }\n" " }\n"; CFCParcel *crust = CFCParcel_new_from_json(crust_json, NULL); CFCParcel_register(crust); CFCParcel_check_prereqs(crust); INT_EQ(test, CFCParcel_required(foo), false, "parcel not required"); INT_EQ(test, CFCParcel_required(cfish), true, "prereq required"); INT_EQ(test, CFCParcel_required(crust), true, "self required"); CFCParcel **prereq_parcels = CFCParcel_prereq_parcels(crust); OK(test, prereq_parcels[0] != NULL, "prereq_parcels[0]"); const char *name = CFCParcel_get_name(prereq_parcels[0]); STR_EQ(test, name, "Clownfish", "prereq_parcels[0] name"); OK(test, prereq_parcels[1] == NULL, "prereq_parcels[0]"); OK(test, CFCParcel_has_prereq(crust, cfish), "has_prereq"); OK(test, CFCParcel_has_prereq(crust, crust), "has_prereq self"); OK(test, !CFCParcel_has_prereq(crust, foo), "has_prereq false"); CFCParcel_add_struct_sym(cfish, "Swim"); CFCParcel_add_struct_sym(crust, "Pinch"); CFCParcel_add_struct_sym(foo, "Bar"); CFCParcel *found; found = CFCParcel_lookup_struct_sym(crust, "Swim"); OK(test, found == cfish, "lookup_struct_sym prereq"); found = CFCParcel_lookup_struct_sym(crust, "Pinch"); OK(test, found == crust, "lookup_struct_sym self"); found = CFCParcel_lookup_struct_sym(crust, "Bar"); OK(test, found == NULL, "lookup_struct_sym other"); FREEMEM(prereq_parcels); CFCBase_decref((CFCBase*)crust); CFCBase_decref((CFCBase*)cfish_version); CFCBase_decref((CFCBase*)cfish_file_spec); CFCBase_decref((CFCBase*)cfish); CFCBase_decref((CFCBase*)foo_file_spec); CFCBase_decref((CFCBase*)foo); CFCParcel_reap_singletons(); } }