예제 #1
0
파일: CFCClass.c 프로젝트: hernan604/lucy
static void
S_register(CFCClass *self) {
    if (registry_size == registry_cap) {
        size_t new_cap = registry_cap + 10;
        registry = (CFCClassRegEntry*)REALLOCATE(
                       registry,
                       (new_cap + 1) * sizeof(CFCClassRegEntry));
        for (size_t i = registry_cap; i <= new_cap; i++) {
            registry[i].key = NULL;
            registry[i].klass = NULL;
        }
        registry_cap = new_cap;
    }

    CFCParcel  *parcel     = CFCClass_get_parcel(self);
    const char *prefix     = CFCParcel_get_prefix(parcel);
    const char *class_name = CFCClass_get_class_name(self);
    const char *cnick      = CFCClass_get_cnick(self);
    const char *key        = self->full_struct_sym;

    for (size_t i = 0; i < registry_size; i++) {
        CFCClass   *other            = registry[i].klass;
        CFCParcel  *other_parcel     = CFCClass_get_parcel(other);
        const char *other_prefix     = CFCParcel_get_prefix(other_parcel);
        const char *other_class_name = CFCClass_get_class_name(other);
        const char *other_cnick      = CFCClass_get_cnick(other);

        if (strcmp(class_name, other_class_name) == 0) {
            CFCUtil_die("Two classes with name %s", class_name);
        }
        if (strcmp(registry[i].key, key) == 0) {
            CFCUtil_die("Class name conflict between %s and %s",
                        class_name, other_class_name);
        }
        if (strcmp(prefix, other_prefix) == 0
            && strcmp(cnick, other_cnick) == 0
           ) {
            CFCUtil_die("Class nickname conflict between %s and %s",
                        class_name, other_class_name);
        }
    }

    registry[registry_size].key   = CFCUtil_strdup(key);
    registry[registry_size].klass = (CFCClass*)CFCBase_incref((CFCBase*)self);
    registry_size++;
}
예제 #2
0
파일: CFCMethod.c 프로젝트: hernan604/lucy
static char*
S_short_method_sym(CFCMethod *self, CFCClass *invoker, const char *postfix) {
    const char *cnick;
    if (invoker) {
        cnick = CFCClass_get_cnick(invoker);
    }
    else {
        cnick = CFCMethod_get_class_cnick(self);
    }
    return CFCUtil_sprintf("%s_%s%s", cnick, self->macro_sym, postfix);
}
예제 #3
0
파일: CFCMethod.c 프로젝트: hernan604/lucy
static char*
S_full_method_sym(CFCMethod *self, CFCClass *invoker, const char *postfix) {
    const char *PREFIX;
    const char *cnick;
    if (invoker) {
        PREFIX = CFCClass_get_PREFIX(invoker);
        cnick  = CFCClass_get_cnick(invoker);
    }
    else {
        PREFIX = CFCMethod_get_PREFIX(self);
        cnick  = CFCMethod_get_class_cnick(self);
    }
    return CFCUtil_sprintf("%s%s_%s%s", PREFIX, cnick, self->macro_sym,
                           postfix);
}
예제 #4
0
파일: CFCDumpable.c 프로젝트: theory/lucy
static CFCMethod*
S_make_method_obj(CFCClass *klass, const char *method_name) {
    const char *klass_full_struct_sym = CFCClass_full_struct_sym(klass);
    const char *klass_name   = CFCClass_get_class_name(klass);
    const char *klass_cnick  = CFCClass_get_cnick(klass);
    CFCParcel  *klass_parcel = CFCClass_get_parcel(klass);

    CFCType *return_type = CFCType_new_object(CFCTYPE_INCREMENTED,
                                              klass_parcel, "cfish_Obj", 1);
    CFCType *self_type = CFCType_new_object(0, klass_parcel,
                                            klass_full_struct_sym, 1);
    CFCVariable *self_var = CFCVariable_new(NULL, NULL, NULL, NULL, "self",
                                            self_type, false);
    CFCParamList *param_list = NULL;

    if (strcmp(method_name, "Dump") == 0) {
        param_list = CFCParamList_new(false);
        CFCParamList_add_param(param_list, self_var, NULL);
    }
    else if (strcmp(method_name, "Load") == 0) {
        CFCType *dump_type = CFCType_new_object(0, klass_parcel, "cfish_Obj",
                                                1);
        CFCVariable *dump_var = CFCVariable_new(NULL, NULL, NULL, NULL, "dump",
                                                dump_type, false);
        param_list = CFCParamList_new(false);
        CFCParamList_add_param(param_list, self_var, NULL);
        CFCParamList_add_param(param_list, dump_var, NULL);
        CFCBase_decref((CFCBase*)dump_var);
        CFCBase_decref((CFCBase*)dump_type);
    }
    else {
        CFCUtil_die("Unexpected method_name: '%s'", method_name);
    }

    CFCMethod *method = CFCMethod_new(klass_parcel, "public", klass_name,
                                      klass_cnick, method_name, return_type,
                                      param_list, NULL, false, false);

    CFCBase_decref((CFCBase*)param_list);
    CFCBase_decref((CFCBase*)self_type);
    CFCBase_decref((CFCBase*)self_var);
    CFCBase_decref((CFCBase*)return_type);

    return method;
}
예제 #5
0
파일: CFCDumpable.c 프로젝트: theory/lucy
static void
S_add_load_method(CFCClass *klass) {
    CFCMethod *method = S_make_method_obj(klass, "Load");
    CFCClass_add_method(klass, method);
    CFCBase_decref((CFCBase*)method);
    const char *full_func_sym = CFCMethod_implementing_func_sym(method);
    const char *full_struct   = CFCClass_full_struct_sym(klass);
    const char *vtable_var    = CFCClass_full_vtable_var(klass);
    CFCClass   *parent        = CFCClass_get_parent(klass);
    const char *prefix        = CFCClass_get_prefix(klass);
    const char *class_cnick   = CFCClass_get_cnick(klass);
    char buf[BUF_SIZE];

    if (parent && CFCClass_has_attribute(parent, "dumpable")) {
        char *full_typedef = CFCMethod_full_typedef(method, klass);
        char *full_meth    = CFCMethod_full_method_sym(method, klass);

        const char pattern[] =
            "cfish_Obj*\n"
            "%s(%s *self, cfish_Obj *dump)\n"
            "{\n"
            "    cfish_Hash *source = (cfish_Hash*)CFISH_CERTIFY(dump, CFISH_HASH);\n"
            "    %s super_load = CFISH_SUPER_METHOD_PTR(%s, %s);\n"
            "    %s *loaded = (%s*)super_load(self, dump);\n"
            "    %sIVARS *ivars = %s%s_IVARS(loaded);\n";
        char *autocode
            = CFCUtil_sprintf(pattern, full_func_sym, full_struct,
                              full_typedef, vtable_var, full_meth, full_struct,
                              full_struct, full_struct, prefix, class_cnick);
        CFCClass_append_autocode(klass, autocode);
        FREEMEM(full_meth);
        FREEMEM(full_typedef);
        FREEMEM(autocode);

        CFCVariable **fresh = CFCClass_fresh_member_vars(klass);
        for (size_t i = 0; fresh[i] != NULL; i++) {
            S_process_load_member(klass, fresh[i], buf, BUF_SIZE);
        }
        FREEMEM(fresh);
    }
    else {
        const char pattern[] =
            "cfish_Obj*\n"
            "%s(%s *self, cfish_Obj *dump)\n"
            "{\n"
            "    cfish_Hash *source = (cfish_Hash*)CFISH_CERTIFY(dump, CFISH_HASH);\n"
            "    cfish_CharBuf *class_name = (cfish_CharBuf*)CFISH_CERTIFY(\n"
            "        Cfish_Hash_Fetch_Str(source, \"_class\", 6), CFISH_CHARBUF);\n"
            "    cfish_VTable *vtable = cfish_VTable_singleton(class_name, NULL);\n"
            "    %s *loaded = (%s*)Cfish_VTable_Make_Obj(vtable);\n"
            "    %sIVARS *ivars = %s%s_IVARS(loaded);\n"
            "    CHY_UNUSED_VAR(self);\n";
        char *autocode
            = CFCUtil_sprintf(pattern, full_func_sym, full_struct, full_struct,
                              full_struct,
                              full_struct, prefix, class_cnick);
        CFCClass_append_autocode(klass, autocode);
        FREEMEM(autocode);
        CFCVariable **members = CFCClass_member_vars(klass);
        for (size_t i = 0; members[i] != NULL; i++) {
            S_process_load_member(klass, members[i], buf, BUF_SIZE);
        }
    }

    CFCClass_append_autocode(klass, "    return (cfish_Obj*)loaded;\n}\n\n");
}
예제 #6
0
파일: CFCDumpable.c 프로젝트: theory/lucy
static void
S_add_dump_method(CFCClass *klass) {
    CFCMethod *method = S_make_method_obj(klass, "Dump");
    CFCClass_add_method(klass, method);
    CFCBase_decref((CFCBase*)method);
    const char *full_func_sym = CFCMethod_implementing_func_sym(method);
    const char *full_struct   = CFCClass_full_struct_sym(klass);
    const char *vtable_var    = CFCClass_full_vtable_var(klass);
    const char *prefix        = CFCClass_get_prefix(klass);
    const char *class_cnick   = CFCClass_get_cnick(klass);
    CFCClass   *parent        = CFCClass_get_parent(klass);
    char buf[BUF_SIZE];

    if (parent && CFCClass_has_attribute(parent, "dumpable")) {
        char *full_typedef = CFCMethod_full_typedef(method, klass);
        char *full_meth    = CFCMethod_full_method_sym(method, klass);

        const char pattern[] =
            "cfish_Obj*\n"
            "%s(%s *self)\n"
            "{\n"
            "    %sIVARS *ivars = %s%s_IVARS(self);\n"
            "    %s super_dump = CFISH_SUPER_METHOD_PTR(%s, %s);\n"
            "    cfish_Hash *dump = (cfish_Hash*)super_dump(self);\n";
        char *autocode
            = CFCUtil_sprintf(pattern, full_func_sym, full_struct,
                              full_struct, prefix, class_cnick,
                              full_typedef, vtable_var, full_meth);
        CFCClass_append_autocode(klass, autocode);
        FREEMEM(full_meth);
        FREEMEM(full_typedef);
        FREEMEM(autocode);

        CFCVariable **fresh = CFCClass_fresh_member_vars(klass);
        for (size_t i = 0; fresh[i] != NULL; i++) {
            S_process_dump_member(klass, fresh[i], buf, BUF_SIZE);
        }
        FREEMEM(fresh);
    }
    else {
        const char pattern[] =
            "cfish_Obj*\n"
            "%s(%s *self)\n"
            "{\n"
            "    %sIVARS *ivars = %s%s_IVARS(self);\n"
            "    cfish_Hash *dump = cfish_Hash_new(0);\n"
            "    Cfish_Hash_Store_Str(dump, \"_class\", 6,\n"
            "        (cfish_Obj*)Cfish_CB_Clone(Cfish_Obj_Get_Class_Name((cfish_Obj*)self)));\n";
        char *autocode
            = CFCUtil_sprintf(pattern, full_func_sym, full_struct,
                              full_struct, prefix, class_cnick);
        CFCClass_append_autocode(klass, autocode);
        FREEMEM(autocode);
        CFCVariable **members = CFCClass_member_vars(klass);
        for (size_t i = 0; members[i] != NULL; i++) {
            S_process_dump_member(klass, members[i], buf, BUF_SIZE);
        }
    }

    CFCClass_append_autocode(klass, "    return (cfish_Obj*)dump;\n}\n\n");
}
예제 #7
0
파일: CFCClass.c 프로젝트: hernan604/lucy
CFCClass*
CFCClass_do_create(CFCClass *self, struct CFCParcel *parcel,
                   const char *exposure, const char *class_name,
                   const char *cnick, const char *micro_sym,
                   CFCDocuComment *docucomment, CFCFileSpec *file_spec,
                   const char *parent_class_name, int is_final, int is_inert) {
    CFCUTIL_NULL_CHECK(class_name);
    exposure  = exposure  ? exposure  : "parcel";
    micro_sym = micro_sym ? micro_sym : "class";
    parcel    = parcel    ? parcel    : CFCParcel_default_parcel();
    CFCSymbol_init((CFCSymbol*)self, parcel, exposure, class_name, cnick,
                   micro_sym);
    if (!is_inert
        && !parent_class_name
        && strcmp(class_name, "Clownfish::Obj") != 0
       ) {
        parent_class_name = "Clownfish::Obj";
    }
    self->parent     = NULL;
    self->tree_grown = false;
    self->children        = (CFCClass**)CALLOCATE(1, sizeof(CFCClass*));
    self->num_kids        = 0;
    self->functions       = (CFCFunction**)CALLOCATE(1, sizeof(CFCFunction*));
    self->num_functions   = 0;
    self->methods         = (CFCMethod**)CALLOCATE(1, sizeof(CFCMethod*));
    self->num_methods     = 0;
    self->member_vars     = (CFCVariable**)CALLOCATE(1, sizeof(CFCVariable*));
    self->num_member_vars = 0;
    self->inert_vars      = (CFCVariable**)CALLOCATE(1, sizeof(CFCVariable*));
    self->num_inert_vars  = 0;
    self->parent_class_name = CFCUtil_strdup(parent_class_name);
    self->docucomment
        = (CFCDocuComment*)CFCBase_incref((CFCBase*)docucomment);
    self->file_spec = (CFCFileSpec*)CFCBase_incref((CFCBase*)file_spec);

    // Cache several derived symbols.
    const char *last_colon = strrchr(class_name, ':');
    self->struct_sym = last_colon
                       ? CFCUtil_strdup(last_colon + 1)
                       : CFCUtil_strdup(class_name);
    const char *prefix = CFCClass_get_prefix(self);
    size_t struct_sym_len = strlen(self->struct_sym);
    self->short_vtable_var = (char*)MALLOCATE(struct_sym_len + 1);
    size_t i;
    for (i = 0; i < struct_sym_len; i++) {
        self->short_vtable_var[i] = toupper(self->struct_sym[i]);
    }
    self->short_vtable_var[struct_sym_len] = '\0';
    self->full_struct_sym   = CFCUtil_sprintf("%s%s", prefix, self->struct_sym);
    self->ivars_struct      = CFCUtil_sprintf("%sIVARS", self->struct_sym);
    self->full_ivars_struct = CFCUtil_sprintf("%sIVARS", self->full_struct_sym);
    self->ivars_func        = CFCUtil_sprintf("%s_IVARS", CFCClass_get_cnick(self));
    self->full_ivars_func   = CFCUtil_sprintf("%s%s_IVARS", prefix,
                                              CFCClass_get_cnick(self));
    self->full_ivars_offset = CFCUtil_sprintf("%s_OFFSET", self->full_ivars_func);
    size_t full_struct_sym_len = strlen(self->full_struct_sym);
    self->full_vtable_var = (char*)MALLOCATE(full_struct_sym_len + 1);
    for (i = 0; self->full_struct_sym[i] != '\0'; i++) {
        self->full_vtable_var[i] = toupper(self->full_struct_sym[i]);
    }
    self->full_vtable_var[i] = '\0';
    self->privacy_symbol = CFCUtil_sprintf("C_%s", self->full_vtable_var);

    // Build the relative path to the autogenerated C header file.
    if (file_spec) {
        const char *path_part = CFCFileSpec_get_path_part(self->file_spec);
        self->include_h = CFCUtil_sprintf("%s.h", path_part);
    }
    else {
        self->include_h = CFCUtil_strdup("class.h");
    }

    self->is_final    = !!is_final;
    self->is_inert    = !!is_inert;

    if (file_spec && CFCFileSpec_included(file_spec)) {
        if (!CFCParcel_included(parcel)) {
            CFCUtil_die("Class %s from include dir found in parcel %s from"
                        " source dir",
                        class_name, CFCParcel_get_name(parcel));
        }
    }
    else {
        if (CFCParcel_included(parcel)) {
            CFCUtil_die("Class %s from source dir found in parcel %s from"
                        " include dir",
                        class_name, CFCParcel_get_name(parcel));
        }
    }

    // Store in registry.
    S_register(self);

    return self;
}
예제 #8
0
static void
S_run_tests(CFCTest *test) {
    CFCParser *parser = CFCParser_new();
    OK(test, parser != NULL, "new");

    {
        CFCParcel *fish = CFCTest_parse_parcel(test, parser, "parcel Fish;");

        CFCParcel *registered
            = CFCParcel_new("Crustacean", "Crust", NULL, false);
        CFCParcel_register(registered);
        CFCParcel *parcel
            = CFCTest_parse_parcel(test, parser, "parcel Crustacean;");
        OK(test, parcel == registered, "Fetch registered parcel");
        OK(test, CFCParser_get_parcel(parser) == parcel,
           "parcel_definition sets internal var");

        CFCBase_decref((CFCBase*)fish);
        CFCBase_decref((CFCBase*)registered);
        CFCBase_decref((CFCBase*)parcel);
    }

    {
        static const char *const specifiers[8] = {
            "foo", "_foo", "foo_yoo", "FOO", "Foo", "fOO", "f00", "foo_foo_foo"
        };
        for (int i = 0; i < 8; ++i) {
            const char *specifier = specifiers[i];
            char *src = CFCUtil_sprintf("int32_t %s;", specifier);
            CFCVariable *var = CFCTest_parse_variable(test, parser, src);
            STR_EQ(test, CFCVariable_micro_sym(var), specifier,
                   "identifier/declarator: %s", specifier);
            FREEMEM(src);
            CFCBase_decref((CFCBase*)var);
        }
    }

    {
        static const char *const specifiers[6] = {
            "void", "float", "uint32_t", "int64_t", "uint8_t", "bool"
        };
        for (int i = 0; i < 6; ++i) {
            const char *specifier = specifiers[i];
            char *src = CFCUtil_sprintf("int32_t %s;", specifier);
            CFCBase *result = CFCParser_parse(parser, src);
            OK(test, result == NULL,
               "reserved word not parsed as identifier: %s", specifier);
            FREEMEM(src);
            CFCBase_decref(result);
        }
    }

    {
        static const char *const type_strings[7] = {
            "bool", "const char *", "Obj*", "i32_t", "char[]", "long[1]",
            "i64_t[30]"
        };
        for (int i = 0; i < 7; ++i) {
            const char *type_string = type_strings[i];
            CFCType *type = CFCTest_parse_type(test, parser, type_string);
            CFCBase_decref((CFCBase*)type);
        }
    }

    {
        static const char *const class_names[7] = {
            "ByteBuf", "Obj", "ANDMatcher", "Foo", "FooJr", "FooIII", "Foo4th"
        };
        CFCClass *class_list[8];
        for (int i = 0; i < 7; ++i) {
            char *class_code = CFCUtil_sprintf("class %s {}", class_names[i]);
            CFCClass *klass = CFCTest_parse_class(test, parser, class_code);
            class_list[i] = klass;
            FREEMEM(class_code);
        }
        class_list[7] = NULL;
        for (int i = 0; i < 7; ++i) {
            const char *class_name = class_names[i];
            char *src      = CFCUtil_sprintf("%s*", class_name);
            char *expected = CFCUtil_sprintf("crust_%s", class_name);
            CFCType *type = CFCTest_parse_type(test, parser, src);
            CFCType_resolve(type, class_list);
            STR_EQ(test, CFCType_get_specifier(type), expected,
                   "object_type_specifier: %s", class_name);
            FREEMEM(src);
            FREEMEM(expected);
            CFCBase_decref((CFCBase*)type);
        }
        for (int i = 0; i < 7; ++i) {
            CFCBase_decref((CFCBase*)class_list[i]);
        }
        CFCClass_clear_registry();
    }

    {
        CFCType *type = CFCTest_parse_type(test, parser, "const char");
        OK(test, CFCType_const(type), "type_qualifier const");
        CFCBase_decref((CFCBase*)type);
    }

    {
        static const char *const exposures[2] = {
            "public", ""
        };
        static int (*const accessors[2])(CFCSymbol *sym) = {
            CFCSymbol_public, CFCSymbol_parcel
        };
        for (int i = 0; i < 2; ++i) {
            const char *exposure = exposures[i];
            char *src = CFCUtil_sprintf("%s inert int32_t foo;", exposure);
            CFCVariable *var = CFCTest_parse_variable(test, parser, src);
            OK(test, accessors[i]((CFCSymbol*)var), "exposure_specifier %s",
               exposure);
            FREEMEM(src);
            CFCBase_decref((CFCBase*)var);
        }
    }

    {
        static const char *const hex_constants[] = {
            "0x1", "0x0a", "0xFFFFFFFF", "-0xFC", NULL
        };
        S_test_initial_value(test, parser, hex_constants, "int32_t",
                             "hex_constant:");
    }

    {
        static const char *const integer_constants[] = {
            "1", "-9999", "0", "10000", NULL
        };
        S_test_initial_value(test, parser, integer_constants, "int32_t",
                             "integer_constant:");
    }

    {
        static const char *const float_constants[] = {
            "1.0", "-9999.999", "0.1", "0.0", NULL
        };
        S_test_initial_value(test, parser, float_constants, "double",
                             "float_constant:");
    }

    {
        static const char *const string_literals[] = {
            "\"blah\"", "\"blah blah\"", "\"\\\"blah\\\" \\\"blah\\\"\"", NULL
        };
        S_test_initial_value(test, parser, string_literals, "String*",
                             "string_literal:");
    }

    {
        static const char *const composites[5] = {
            "int[]", "i32_t **", "Foo **", "Foo ***", "const void *"
        };
        for (int i = 0; i < 5; ++i) {
            const char *composite = composites[i];
            CFCType *type = CFCTest_parse_type(test, parser, composite);
            OK(test, CFCType_is_composite(type), "composite_type: %s",
               composite);
            CFCBase_decref((CFCBase*)type);
        }
    }

    {
        static const char *const object_types[5] = {
            "Obj *", "incremented Foo*", "decremented String *"
        };
        for (int i = 0; i < 3; ++i) {
            const char *object_type = object_types[i];
            CFCType *type = CFCTest_parse_type(test, parser, object_type);
            OK(test, CFCType_is_object(type), "object_type: %s",
               object_type);
            CFCBase_decref((CFCBase*)type);
        }
    }

    {
        static const char *const param_list_strings[3] = {
            "()",
            "(int foo)",
            "(Obj *foo, Foo **foo_ptr)"
        };
        for (int i = 0; i < 3; ++i) {
            const char *param_list_string = param_list_strings[i];
            CFCParamList *param_list
                = CFCTest_parse_param_list(test, parser, param_list_string);
            INT_EQ(test, CFCParamList_num_vars(param_list), i,
                   "param list num_vars: %d", i);
            CFCBase_decref((CFCBase*)param_list);
        }
    }

    {
        CFCParamList *param_list
            = CFCTest_parse_param_list(test, parser, "(int foo, ...)");
        OK(test, CFCParamList_variadic(param_list), "variadic param list");
        CFCBase_decref((CFCBase*)param_list);
    }

    {
        const char *param_list_string =
            "(int foo = 0xFF, char *bar =\"blah\")";
        CFCParamList *param_list
            = CFCTest_parse_param_list(test, parser, param_list_string);
        const char **initial_values
            = CFCParamList_get_initial_values(param_list);
        STR_EQ(test, initial_values[0], "0xFF",
               "param list initial_values[0]");
        STR_EQ(test, initial_values[1], "\"blah\"",
               "param list initial_values[1]");
        OK(test, initial_values[2] == NULL, "param list initial_values[2]");
        CFCBase_decref((CFCBase*)param_list);
    }

    {
        CFCParser_set_class_name(parser, "Stuff::Obj");
        CFCParser_set_class_cnick(parser, "Obj");

        const char *method_string =
            "public Foo* Spew_Foo(Obj *self, uint32_t *how_many);";
        CFCMethod *method = CFCTest_parse_method(test, parser, method_string);
        CFCBase_decref((CFCBase*)method);

        const char *var_string =
            "public inert Hash *hash;";
        CFCVariable *var = CFCTest_parse_variable(test, parser, var_string);
        CFCBase_decref((CFCBase*)var);
    }

    {
        static const char *const class_names[4] = {
            "Foo", "Foo::FooJr", "Foo::FooJr::FooIII",
            "Foo::FooJr::FooIII::Foo4th"
        };
        for (int i = 0; i < 4; ++i) {
            const char *class_name = class_names[i];
            char *class_string = CFCUtil_sprintf("class %s { }", class_name);
            CFCClass *klass
                = CFCTest_parse_class(test, parser, class_string);
            STR_EQ(test, CFCClass_get_class_name(klass), class_name,
                   "class_name: %s", class_name);
            FREEMEM(class_string);
            CFCBase_decref((CFCBase*)klass);
        }
    }

    {
        static const char *const cnicks[2] =  { "Food", "FF" };
        for (int i = 0; i < 2; ++i) {
            const char *cnick = cnicks[i];
            char *class_string
                = CFCUtil_sprintf("class Foodie%s cnick %s { }", cnick, cnick);
            CFCClass *klass
                = CFCTest_parse_class(test, parser, class_string);
            STR_EQ(test, CFCClass_get_cnick(klass), cnick, "cnick: %s", cnick);
            FREEMEM(class_string);
            CFCBase_decref((CFCBase*)klass);
        }
    }

    CFCBase_decref((CFCBase*)parser);

    CFCClass_clear_registry();
    CFCParcel_reap_singletons();
}