Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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);
    }
}
Ejemplo n.º 3
0
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);
}
Ejemplo n.º 4
0
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);
}
Ejemplo n.º 5
0
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();
}
Ejemplo n.º 6
0
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();
    }
}