static char* S_gen_positional_sample(const char *prologue, CFCParamList *param_list, int start) { int num_vars = CFCParamList_num_vars(param_list); CFCVariable **vars = CFCParamList_get_variables(param_list); const char **inits = CFCParamList_get_initial_values(param_list); if (num_vars - start != 1) { CFCUtil_die("Code samples with multiple positional parameters" " are not supported yet."); } const char *name = CFCVariable_get_name(vars[start]); char *sample = CFCUtil_sprintf(" %s($%s);\n", prologue, name); const char *init = inits[start]; if (init) { if (strcmp(init, "NULL") == 0) { init = "undef"; } char *def_sample = CFCUtil_sprintf(" %s(); # default: %s\n", prologue, init); sample = CFCUtil_cat(sample, def_sample, NULL); FREEMEM(def_sample); } return sample; }
int CFCMethod_compatible(CFCMethod *self, CFCMethod *other) { if (!other) { return false; } if (strcmp(self->macro_sym, other->macro_sym)) { return false; } int my_public = CFCMethod_public(self); int other_public = CFCMethod_public(other); if (!!my_public != !!other_public) { return false; } // Check arguments and initial values. CFCParamList *my_param_list = self->function.param_list; CFCParamList *other_param_list = other->function.param_list; CFCVariable **my_args = CFCParamList_get_variables(my_param_list); CFCVariable **other_args = CFCParamList_get_variables(other_param_list); const char **my_vals = CFCParamList_get_initial_values(my_param_list); const char **other_vals = CFCParamList_get_initial_values(other_param_list); for (size_t i = 1; ; i++) { // start at 1, skipping self if (!!my_args[i] != !!other_args[i]) { return false; } if (!!my_vals[i] != !!other_vals[i]) { return false; } if (my_vals[i]) { if (strcmp(my_vals[i], other_vals[i])) { return false; } } if (my_args[i]) { if (!CFCVariable_equals(my_args[i], other_args[i])) { return false; } } else { break; } } // Check return types. CFCType *type = CFCMethod_get_return_type(self); CFCType *other_type = CFCMethod_get_return_type(other); if (CFCType_is_object(type)) { // Weak validation to allow covariant object return types. if (!CFCType_is_object(other_type)) { return false; } if (!CFCType_similar(type, other_type)) { return false; } } else { if (!CFCType_equals(type, other_type)) { return false; } } return true; }
static char* S_gen_labeled_sample(const char *prologue, CFCParamList *param_list, int start) { int num_vars = CFCParamList_num_vars(param_list); CFCVariable **vars = CFCParamList_get_variables(param_list); const char **inits = CFCParamList_get_initial_values(param_list); size_t max_name_len = 0; // Find maximum length of parameter name. for (int i = start; i < num_vars; i++) { const char *name = CFCVariable_get_name(vars[i]); size_t name_len = strlen(name); if (name_len > max_name_len) { max_name_len = name_len; } } char *params = CFCUtil_strdup(""); for (int i = start; i < num_vars; i++) { const char *name = CFCVariable_get_name(vars[i]); const char *init = inits[i]; char *comment = NULL; if (init) { if (strcmp(init, "NULL") == 0) { init = "undef"; } comment = CFCUtil_sprintf("default: %s", init); } else { comment = CFCUtil_strdup("required"); } char *line = CFCUtil_sprintf(" %-*s => $%-*s # %s\n", (int)max_name_len, name, (int)max_name_len, name, comment); params = CFCUtil_cat(params, line, NULL); FREEMEM(line); FREEMEM(comment); } const char pattern[] = " %s(\n" "%s" " );\n"; char *sample = CFCUtil_sprintf(pattern, prologue, params); FREEMEM(params); return sample; }
static void S_test_initial_value(CFCTest *test, CFCParser *parser, const char *const *values, const char *type, const char *test_name) { for (int i = 0; values[i]; ++i) { const char *value = values[i]; char *src = CFCUtil_sprintf("(%s foo = %s)", type, value); CFCParamList *param_list = CFCTest_parse_param_list(test, parser, src); const char **initial_values = CFCParamList_get_initial_values(param_list); STR_EQ(test, initial_values[0], value, "%s %s", test_name, value); FREEMEM(src); CFCBase_decref((CFCBase*)param_list); } }
char* CFCPerlSub_build_allot_params(CFCPerlSub *self) { CFCParamList *param_list = self->param_list; CFCVariable **arg_vars = CFCParamList_get_variables(param_list); const char **arg_inits = CFCParamList_get_initial_values(param_list); size_t num_vars = CFCParamList_num_vars(param_list); char *allot_params = CFCUtil_strdup(""); // Declare variables and assign default values. for (size_t i = 1; i < num_vars; i++) { CFCVariable *arg_var = arg_vars[i]; const char *val = arg_inits[i]; const char *var_name = CFCVariable_micro_sym(arg_var); if (val == NULL) { CFCType *arg_type = CFCVariable_get_type(arg_var); val = CFCType_is_object(arg_type) ? "NULL" : "0"; } allot_params = CFCUtil_cat(allot_params, "arg_", var_name, " = ", val, ";\n ", NULL); } // Iterate over args in param list. allot_params = CFCUtil_cat(allot_params, "args_ok = XSBind_allot_params(\n" " &(ST(0)), 1, items,\n", NULL); for (size_t i = 1; i < num_vars; i++) { CFCVariable *var = arg_vars[i]; const char *val = arg_inits[i]; int required = val ? 0 : 1; const char *name = CFCVariable_micro_sym(var); CFCType *type = CFCVariable_get_type(var); char *arg = S_allot_params_arg(type, name, required); allot_params = CFCUtil_cat(allot_params, " ", arg, ",\n", NULL); FREEMEM(arg); } allot_params = CFCUtil_cat(allot_params, " NULL);\n", " if (!args_ok) {\n" " CFISH_RETHROW(CFISH_INCREF(cfish_Err_get_error()));\n" " }", NULL); return allot_params; }
char* CFCPerlSub_params_hash_def(CFCPerlSub *self) { if (!self->use_labeled_params) { return NULL; } char *def = CFCUtil_strdup(""); def = CFCUtil_cat(def, "%", self->perl_name, "_PARAMS = (", NULL); CFCVariable **arg_vars = CFCParamList_get_variables(self->param_list); const char **vals = CFCParamList_get_initial_values(self->param_list); // No labeled params means an empty params hash def. if (!arg_vars[1]) { def = CFCUtil_cat(def, ");\n", NULL); return def; } for (int i = 1; arg_vars[i] != NULL; i++) { CFCVariable *var = arg_vars[i]; const char *micro_sym = CFCVariable_micro_sym(var); const char *val = vals[i]; val = val == NULL ? "undef" : strcmp(val, "NULL") == 0 ? "undef" : strcmp(val, "true") == 0 ? "1" : strcmp(val, "false") == 0 ? "0" : val; def = CFCUtil_cat(def, "\n ", micro_sym, " => ", val, ",", NULL); } def = CFCUtil_cat(def, "\n);\n", NULL); return def; }
/* Generate the code which parses arguments passed from Python and converts * them to Clownfish-flavored C values. */ static char* S_gen_arg_parsing(CFCParamList *param_list, int first_tick, char **error) { char *content = NULL; CFCVariable **vars = CFCParamList_get_variables(param_list); const char **vals = CFCParamList_get_initial_values(param_list); int num_vars = CFCParamList_num_vars(param_list); char *declarations = CFCUtil_strdup(""); char *keywords = CFCUtil_strdup(""); char *format_str = CFCUtil_strdup(""); char *targets = CFCUtil_strdup(""); int optional_started = 0; for (int i = first_tick; i < num_vars; i++) { CFCVariable *var = vars[i]; const char *val = vals[i]; const char *var_name = CFCVariable_get_name(var); keywords = CFCUtil_cat(keywords, "\"", var_name, "\", ", NULL); // Build up ParseTuple format string. if (val == NULL) { if (optional_started) { // problem! *error = "Required after optional param"; goto CLEAN_UP_AND_RETURN; } } else { if (!optional_started) { optional_started = 1; format_str = CFCUtil_cat(format_str, "|", NULL); } } format_str = CFCUtil_cat(format_str, "O&", NULL); char *declaration = S_gen_declaration(var, val); declarations = CFCUtil_cat(declarations, declaration, NULL); FREEMEM(declaration); char *target = S_gen_target(var, val); targets = CFCUtil_cat(targets, target, NULL); FREEMEM(target); } char parse_pattern[] = "%s" " char *keywords[] = {%sNULL};\n" " char *fmt = \"%s\";\n" " int ok = PyArg_ParseTupleAndKeywords(args, kwargs, fmt,\n" " keywords%s);\n" " if (!ok) { return NULL; }\n" ; content = CFCUtil_sprintf(parse_pattern, declarations, keywords, format_str, targets); CLEAN_UP_AND_RETURN: FREEMEM(declarations); FREEMEM(keywords); FREEMEM(format_str); FREEMEM(targets); return content; }
static char* S_xsub_def_positional_args(CFCPerlMethod *self) { CFCMethod *method = self->method; CFCParamList *param_list = CFCMethod_get_param_list(method); CFCVariable **arg_vars = CFCParamList_get_variables(param_list); const char **arg_inits = CFCParamList_get_initial_values(param_list); unsigned num_vars = (unsigned)CFCParamList_num_vars(param_list); char *body = S_xsub_body(self); // Determine how many args are truly required and build an error check. unsigned min_required = 0; for (unsigned i = 0; i < num_vars; i++) { if (arg_inits[i] == NULL) { min_required = i + 1; } } char *xs_name_list = num_vars > 0 ? CFCUtil_strdup(CFCVariable_micro_sym(arg_vars[0])) : CFCUtil_strdup(""); for (unsigned i = 1; i < num_vars; i++) { const char *var_name = CFCVariable_micro_sym(arg_vars[i]); if (i < min_required) { xs_name_list = CFCUtil_cat(xs_name_list, ", ", var_name, NULL); } else { xs_name_list = CFCUtil_cat(xs_name_list, ", [", var_name, "]", NULL); } } const char num_args_pattern[] = "if (items %s %u) { CFISH_THROW(CFISH_ERR, \"Usage: %%s(%s)\", GvNAME(CvGV(cv))); }"; char *num_args_check; if (min_required < num_vars) { num_args_check = CFCUtil_sprintf(num_args_pattern, "<", min_required, xs_name_list); } else { num_args_check = CFCUtil_sprintf(num_args_pattern, "!=", num_vars, xs_name_list); } // Var assignments. char *var_assignments = CFCUtil_strdup(""); for (unsigned i = 0; i < num_vars; i++) { CFCVariable *var = arg_vars[i]; const char *val = arg_inits[i]; const char *var_name = CFCVariable_micro_sym(var); CFCType *var_type = CFCVariable_get_type(var); const char *type_c = CFCType_to_c(var_type); if (i == 0) { // self const char *meth_micro_sym = CFCMethod_micro_sym(self->method); char *statement = S_self_assign_statement(self, var_type, meth_micro_sym); var_assignments = CFCUtil_cat(var_assignments, statement, NULL); FREEMEM(statement); } else { char perl_stack_var[30]; sprintf(perl_stack_var, "ST(%u)", i); char *conversion = CFCPerlTypeMap_from_perl(var_type, perl_stack_var); if (!conversion) { CFCUtil_die("Can't map type '%s'", type_c); } if (val) { char pattern[] = "\n %s %s = ( items >= %u && XSBind_sv_defined(ST(%u)) )" " ? %s : %s;"; char *statement = CFCUtil_sprintf(pattern, type_c, var_name, i, i, conversion, val); var_assignments = CFCUtil_cat(var_assignments, statement, NULL); FREEMEM(statement); } else { var_assignments = CFCUtil_cat(var_assignments, "\n ", type_c, " ", var_name, " = ", conversion, ";", NULL); } FREEMEM(conversion); } } char pattern[] = "XS(%s);\n" "XS(%s) {\n" " dXSARGS;\n" " CHY_UNUSED_VAR(cv);\n" " SP -= items;\n" " %s;\n" "\n" " /* Extract vars from Perl stack. */\n" " %s\n" "\n" " /* Execute */\n" " %s\n" "}\n"; char *xsub = CFCUtil_sprintf(pattern, self->sub.c_name, self->sub.c_name, num_args_check, var_assignments, body); FREEMEM(num_args_check); FREEMEM(var_assignments); FREEMEM(body); return xsub; }
static char* S_gen_labeled_sample(const char *invocant, const char *alias, CFCParamList *param_list, size_t start) { size_t num_vars = CFCParamList_num_vars(param_list); CFCVariable **vars = CFCParamList_get_variables(param_list); const char **inits = CFCParamList_get_initial_values(param_list); size_t max_label_len = 0; size_t max_var_len = 0; // Find maximum length of label and Perl variable. for (size_t i = start; i < num_vars; i++) { CFCVariable *var = vars[i]; const char *label = CFCVariable_get_name(var); size_t label_len = strlen(label); if (label_len > max_label_len) { max_label_len = label_len; } char *perl_var = S_perl_var_name(var); size_t perl_var_len = strlen(perl_var); if (perl_var_len > max_var_len) { max_var_len = perl_var_len; } FREEMEM(perl_var); } char *params = CFCUtil_strdup(""); for (size_t i = start; i < num_vars; i++) { CFCVariable *var = vars[i]; const char *label = CFCVariable_get_name(var); char *perl_var = S_perl_var_name(var); perl_var = CFCUtil_cat(perl_var, ",", NULL); char *comment = NULL; const char *init = inits[i]; if (init) { if (strcmp(init, "NULL") == 0) { init = "undef"; } comment = CFCUtil_sprintf("default: %s", init); } else { comment = CFCUtil_strdup("required"); } char *line = CFCUtil_sprintf(" %-*s => $%-*s # %s\n", max_label_len, label, max_var_len + 1, perl_var, comment); params = CFCUtil_cat(params, line, NULL); FREEMEM(line); FREEMEM(comment); FREEMEM(perl_var); } const char pattern[] = " %s->%s(\n" "%s" " );\n"; char *sample = CFCUtil_sprintf(pattern, invocant, alias, params); FREEMEM(params); return sample; }
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(); }