static char* S_meth_top(CFCMethod *method) { CFCParamList *param_list = CFCMethod_get_param_list(method); if (CFCParamList_num_vars(param_list) == 1) { char pattern[] = "(PyObject *self, PyObject *unused) {\n" " CFISH_UNUSED_VAR(unused);\n" ; return CFCUtil_sprintf(pattern); } else { char *error = NULL; char *arg_parsing = S_gen_arg_parsing(param_list, 1, &error); if (error) { CFCUtil_die("%s in %s", error, CFCMethod_get_name(method)); } if (!arg_parsing) { return NULL; } char *decs = S_gen_decs(param_list, 1); char pattern[] = "(PyObject *self, PyObject *args, PyObject *kwargs) {\n" "%s" // decs "%s" ; char *result = CFCUtil_sprintf(pattern, decs, arg_parsing); FREEMEM(arg_parsing); return result; } }
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; }
static char* S_gen_code_sample(CFCFunction *func, const char *alias, CFCClass *klass, int is_constructor) { char *invocant = NULL; if (is_constructor) { invocant = CFCUtil_strdup(CFCClass_get_name(klass)); } else { char *lower = S_camel_to_lower(CFCClass_get_struct_sym(klass)); invocant = CFCUtil_sprintf("$%s", lower); FREEMEM(lower); } CFCParamList *param_list = CFCFunction_get_param_list(func); size_t num_vars = CFCParamList_num_vars(param_list); size_t start = is_constructor ? 0 : 1; char *sample = NULL; if (start == num_vars) { sample = CFCUtil_sprintf(" %s->%s();\n", invocant, alias); } else if (is_constructor || num_vars - start >= 2) { sample = S_gen_labeled_sample(invocant, alias, param_list, start); } else { sample = S_gen_positional_sample(invocant, alias, param_list, start); } return sample; }
static char* S_gen_code_sample(CFCCallable *func, const char *alias, CFCClass *klass, int is_constructor) { char *prologue = CFCUtil_sprintf(""); char *class_var_name = S_camel_to_lower(CFCClass_get_struct_sym(klass)); CFCType *ret_type = CFCCallable_get_return_type(func); if (!CFCType_is_void(ret_type)) { char *ret_name = S_perl_var_name(ret_type, is_constructor); if (!is_constructor && strcmp(ret_name, class_var_name) == 0) { // Return type equals `klass`. Use a generic variable name // to avoid confusing code samples like // `my $string = $string->trim`. prologue = CFCUtil_cat(prologue, "my $result = ", NULL); } else { prologue = CFCUtil_cat(prologue, "my $", ret_name, " = ", NULL); } FREEMEM(ret_name); } if (is_constructor) { const char *invocant = CFCClass_get_name(klass); prologue = CFCUtil_cat(prologue, invocant, NULL); } else { prologue = CFCUtil_cat(prologue, "$", class_var_name, NULL); } prologue = CFCUtil_cat(prologue, "->", alias, NULL); CFCParamList *param_list = CFCCallable_get_param_list(func); int num_vars = CFCParamList_num_vars(param_list); int start = is_constructor ? 0 : 1; char *sample = NULL; if (start == num_vars) { sample = CFCUtil_sprintf(" %s();\n", prologue); } else if (is_constructor || num_vars - start >= 2) { sample = S_gen_labeled_sample(prologue, param_list, start); } else { sample = S_gen_positional_sample(prologue, param_list, start); } FREEMEM(class_var_name); FREEMEM(prologue); return sample; }
static char* S_gen_decs(CFCParamList *param_list, int first_tick) { char *decs = CFCUtil_strdup(""); int num_vars = CFCParamList_num_vars(param_list); CFCVariable **vars = CFCParamList_get_variables(param_list); for (int i = first_tick; i < num_vars; i++) { CFCType *type = CFCVariable_get_type(vars[i]); const char *name = CFCVariable_get_name(vars[i]); decs = CFCUtil_cat(decs, " ", CFCType_to_c(type), " ", name, "_ARG = 0;\n", NULL); } return decs; }
void CFCGoTypeMap_go_meth_receiever(const char *struct_name, CFCParamList *param_list, char *buf, size_t buf_len) { size_t max_required = 2; if (param_list != NULL && CFCParamList_num_vars(param_list) > 0) { CFCVariable **vars = CFCParamList_get_variables(param_list); const char *orig = CFCVariable_get_name(vars[0]); max_required = strlen(orig) + 1; } if (buf_len < max_required) { CFCUtil_die("Buffer length too short: %d", buf_len); } // Find the first letter of the type and lowercase it. for (size_t i = 0, max = strlen(struct_name); i < max; i++) { if (isupper(struct_name[i])) { buf[0] = tolower(struct_name[i]); buf[1] = '\0'; break; } } // Check for another argument with the same name. if (param_list != NULL) { CFCVariable **vars = CFCParamList_get_variables(param_list); size_t num_vars = CFCParamList_num_vars(param_list); for (size_t i = 1; i < num_vars; i++) { const char *name = CFCVariable_get_name(vars[i]); if (strcmp(name, buf) == 0) { // Bah, a clash. Use the original name, even though it's // probably "self" which isn't good Go style. CFCGoTypeMap_go_arg_name(param_list, 0, buf, buf_len); break; } } } }
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; }
char* CFCPerlSub_arg_name_list(CFCPerlSub *self) { CFCParamList *param_list = self->param_list; CFCVariable **arg_vars = CFCParamList_get_variables(param_list); size_t num_vars = CFCParamList_num_vars(param_list); char *name_list = CFCUtil_strdup("arg_self"); for (int i = 1; i < num_vars; i++) { CFCVariable *arg_var = arg_vars[i]; const char *var_name = CFCVariable_micro_sym(arg_vars[i]); name_list = CFCUtil_cat(name_list, ", arg_", var_name, NULL); } return name_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; }
void CFCGoTypeMap_go_arg_name(CFCParamList *param_list, size_t tick, char *buf, size_t buf_len) { size_t num_vars = CFCParamList_num_vars(param_list); if (tick >= num_vars) { CFCUtil_die("Index out of range: %d >= %d", (int)tick, (int)num_vars); } CFCVariable **vars = CFCParamList_get_variables(param_list); const char *orig = CFCVariable_get_name(vars[tick]); size_t max_required = strlen(orig) + 2; if (buf_len < max_required || buf_len < 5) { CFCUtil_die("Buffer length too short: %d", buf_len); } // If the argument name is a Go keyword, append an underscore. This is // ugly but bulletproof. for (int i = 0; i < num_go_keywords; i++) { if (strcmp(orig, go_keywords[i]) == 0) { sprintf(buf, "%s_", orig); return; } } // Transform into lowerCamelCase. size_t dest_tick = 0; int last_was_underscore = 0; for (size_t i = 0; i <= strlen(orig); i++) { if (i > buf_len) { CFCUtil_die("Name too long for buffer of size %d: '%s'", buf_len, orig); } if (orig[i] == '_') { last_was_underscore = 1; continue; } else if (last_was_underscore) { buf[dest_tick] = toupper(orig[i]); } else { buf[dest_tick] = orig[i]; } last_was_underscore = 0; dest_tick++; } }
static char* S_build_py_args(CFCParamList *param_list) { int num_vars = CFCParamList_num_vars(param_list); CFCVariable **vars = CFCParamList_get_variables(param_list); char pattern[] = " PyObject *cfcb_ARGS = S_pack_tuple(%d"; char *py_args = CFCUtil_sprintf(pattern, num_vars - 1); for (int i = 1; vars[i] != NULL; i++) { const char *var_name = CFCVariable_get_name(vars[i]); CFCType *type = CFCVariable_get_type(vars[i]); char *conversion = CFCPyTypeMap_c_to_py(type, var_name); py_args = CFCUtil_cat(py_args, ",\n ", conversion, NULL); FREEMEM(conversion); } py_args = CFCUtil_cat(py_args, ");", NULL); return py_args; }
static char* S_gen_arg_list(CFCParamList *param_list, const char *first_arg) { CFCVariable **vars = CFCParamList_get_variables(param_list); int num_vars = CFCParamList_num_vars(param_list); char *arg_list = CFCUtil_strdup(""); for (int i = 0; i < num_vars; i++) { if (i > 0) { arg_list = CFCUtil_cat(arg_list, ", ", NULL); } if (i == 0 && first_arg != NULL) { arg_list = CFCUtil_cat(arg_list, first_arg, NULL); } else { arg_list = CFCUtil_cat(arg_list, CFCVariable_get_name(vars[i]), "_ARG", NULL); } } return arg_list; }
static char* S_gen_arg_increfs(CFCParamList *param_list, int first_tick) { CFCVariable **vars = CFCParamList_get_variables(param_list); int num_vars = CFCParamList_num_vars(param_list); char *content = CFCUtil_strdup(""); for (int i = first_tick;i < num_vars; i++) { CFCType *type = CFCVariable_get_type(vars[i]); if (CFCType_decremented(type)) { const char *name = CFCVariable_get_name(vars[i]); const char *specifier = CFCType_get_specifier(type); char pattern[] = " %s_ARG = (%s*)CFISH_INCREF(%s_ARG);\n"; char *incref = CFCUtil_sprintf(pattern, name, specifier, name); content = CFCUtil_cat(content, incref, NULL); FREEMEM(incref); } } return content; }
char* CFCPerlSub_arg_declarations(CFCPerlSub *self) { CFCParamList *param_list = self->param_list; CFCVariable **arg_vars = CFCParamList_get_variables(param_list); size_t num_vars = CFCParamList_num_vars(param_list); char *decls = CFCUtil_strdup(""); // Declare variables. for (size_t i = 1; i < num_vars; i++) { CFCVariable *arg_var = arg_vars[i]; CFCType *type = CFCVariable_get_type(arg_var); const char *type_str = CFCType_to_c(type); const char *var_name = CFCVariable_micro_sym(arg_var); decls = CFCUtil_cat(decls, " ", type_str, " arg_", var_name, ";\n", NULL); } return decls; }
char* CFCPyMethod_pymethoddef(CFCMethod *method, CFCClass *invoker) { CFCParamList *param_list = CFCMethod_get_param_list(method); const char *flags = CFCParamList_num_vars(param_list) == 1 ? "METH_NOARGS" : "METH_KEYWORDS|METH_VARARGS"; char *meth_sym = CFCMethod_full_method_sym(method, invoker); char *micro_sym = CFCUtil_strdup(CFCSymbol_get_name((CFCSymbol*)method)); for (int i = 0; micro_sym[i] != 0; i++) { micro_sym[i] = tolower(micro_sym[i]); } char pattern[] = "{\"%s\", (PyCFunction)S_%s, %s, NULL},"; char *py_meth_def = CFCUtil_sprintf(pattern, micro_sym, meth_sym, flags); FREEMEM(meth_sym); FREEMEM(micro_sym); return py_meth_def; }
CFCPerlMethod* CFCPerlMethod_init(CFCPerlMethod *self, CFCMethod *method, const char *alias) { CFCParamList *param_list = CFCMethod_get_param_list(method); const char *class_name = CFCMethod_get_class_name(method); int use_labeled_params = CFCParamList_num_vars(param_list) > 2 ? 1 : 0; // The Clownfish destructor needs to be spelled DESTROY for Perl. if (!alias) { alias = CFCMethod_micro_sym(method); } static const char destroy_uppercase[] = "DESTROY"; if (strcmp(alias, "destroy") == 0) { alias = destroy_uppercase; } CFCPerlSub_init((CFCPerlSub*)self, param_list, class_name, alias, use_labeled_params); self->method = (CFCMethod*)CFCBase_incref((CFCBase*)method); return self; }
// Prep refcount decrement calls to follow the Clownfish subroutine // invocation. static char* S_gen_decrefs(CFCParamList *param_list, int first_tick) { CFCVariable **vars = CFCParamList_get_variables(param_list); int num_vars = CFCParamList_num_vars(param_list); char *decrefs = CFCUtil_strdup(""); for (int i = first_tick; i < num_vars; i++) { CFCVariable *var = vars[i]; CFCType *type = CFCVariable_get_type(var); const char *micro_sym = CFCVariable_get_name(var); const char *specifier = CFCType_get_specifier(type); if (strcmp(specifier, "cfish_Obj") == 0 || strcmp(specifier, "cfish_String") == 0 || strcmp(specifier, "cfish_Vector") == 0 || strcmp(specifier, "cfish_Hash") == 0 ) { decrefs = CFCUtil_cat(decrefs, " CFISH_DECREF(", micro_sym, "_ARG);\n", NULL); } } return decrefs; }
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(); }
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; }
/* 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; }
char* CFCPerlPod_gen_subroutine_pod(CFCPerlPod *self, CFCFunction *func, const char *alias, CFCClass *klass, const char *code_sample, const char *class_name, int is_constructor) { // Only allow "public" subs to be exposed as part of the public API. if (!CFCFunction_public(func)) { CFCUtil_die("%s#%s is not public", class_name, alias); } CFCParamList *param_list = CFCFunction_get_param_list(func); int num_vars = (int)CFCParamList_num_vars(param_list); char *pod = CFCUtil_sprintf("=head2 %s", alias); // Get documentation, which may be inherited. CFCDocuComment *docucomment = CFCFunction_get_docucomment(func); if (!docucomment) { const char *micro_sym = CFCFunction_micro_sym(func); CFCClass *parent = klass; while (NULL != (parent = CFCClass_get_parent(parent))) { CFCFunction *parent_func = (CFCFunction*)CFCClass_method(parent, micro_sym); if (!parent_func) { break; } docucomment = CFCFunction_get_docucomment(parent_func); if (docucomment) { break; } } } if (!docucomment) { CFCUtil_die("No DocuComment for '%s' in '%s'", alias, class_name); } // Build string summarizing arguments to use in header. if (num_vars > 2 || (is_constructor && num_vars > 1)) { pod = CFCUtil_cat(pod, "( I<[labeled params]> )\n\n", NULL); } else if (num_vars == 2) { // Kill self param. const char *name_list = CFCParamList_name_list(param_list); const char *after_comma = strchr(name_list, ',') + 1; while (isspace(*after_comma)) { after_comma++; } pod = CFCUtil_cat(pod, "(", after_comma, ")\n\n", NULL); } else { // num_args == 1, leave off 'self'. pod = CFCUtil_cat(pod, "()\n\n", NULL); } // Add code sample. if (code_sample && strlen(code_sample)) { pod = CFCUtil_cat(pod, code_sample, "\n", NULL); } // Incorporate "description" text from DocuComment. const char *long_doc = CFCDocuComment_get_description(docucomment); if (long_doc && strlen(long_doc)) { char *perlified = CFCPerlPod_md_to_pod(self, klass, long_doc); pod = CFCUtil_cat(pod, perlified, NULL); FREEMEM(perlified); } // Add params in a list. const char**param_names = CFCDocuComment_get_param_names(docucomment); const char**param_docs = CFCDocuComment_get_param_docs(docucomment); if (param_names[0]) { pod = CFCUtil_cat(pod, "=over\n\n", NULL); for (size_t i = 0; param_names[i] != NULL; i++) { char *perlified = CFCPerlPod_md_to_pod(self, klass, param_docs[i]); pod = CFCUtil_cat(pod, "=item *\n\nB<", param_names[i], "> - ", perlified, NULL); FREEMEM(perlified); } pod = CFCUtil_cat(pod, "=back\n\n", NULL); } // Add return value description, if any. const char *retval_doc = CFCDocuComment_get_retval(docucomment); if (retval_doc && strlen(retval_doc)) { char *perlified = CFCPerlPod_md_to_pod(self, klass, retval_doc); pod = CFCUtil_cat(pod, "Returns: ", perlified, NULL); FREEMEM(perlified); } return pod; }
static char* S_callback_params(CFCMethod *method) { const char *micro_sym = CFCSymbol_micro_sym((CFCSymbol*)method); CFCParamList *param_list = CFCMethod_get_param_list(method); unsigned num_params = CFCParamList_num_vars(param_list) - 1; size_t needed = strlen(micro_sym) + 30; char *params = (char*)MALLOCATE(needed); // TODO: use something other than micro_sym here. sprintf(params, "self, \"%s\", %u", micro_sym, num_params); // Iterate over arguments, mapping them to various arg wrappers which // conform to Host's callback interface. CFCVariable **arg_vars = CFCParamList_get_variables(param_list); for (int i = 1; arg_vars[i] != NULL; i++) { CFCVariable *var = arg_vars[i]; const char *name = CFCVariable_micro_sym(var); size_t name_len = strlen(name); CFCType *type = CFCVariable_get_type(var); const char *c_type = CFCType_to_c(type); size_t size = strlen(params) + strlen(c_type) + name_len * 2 + 30; char *new_buf = (char*)MALLOCATE(size); if (CFCType_is_string_type(type)) { sprintf(new_buf, "%s, CFISH_ARG_STR(\"%s\", %s)", params, name, name); } else if (CFCType_is_object(type)) { sprintf(new_buf, "%s, CFISH_ARG_OBJ(\"%s\", %s)", params, name, name); } else if (CFCType_is_integer(type)) { int width = CFCType_get_width(type); if (width) { if (width <= 4) { sprintf(new_buf, "%s, CFISH_ARG_I32(\"%s\", %s)", params, name, name); } else { sprintf(new_buf, "%s, CFISH_ARG_I64(\"%s\", %s)", params, name, name); } } else { sprintf(new_buf, "%s, CFISH_ARG_I(%s, \"%s\", %s)", params, c_type, name, name); } } else if (CFCType_is_floating(type)) { sprintf(new_buf, "%s, CFISH_ARG_F64(\"%s\", %s)", params, name, name); } else { // Can't map variable type. Signal to caller. FREEMEM(params); FREEMEM(new_buf); return NULL; } FREEMEM(params); params = new_buf; } return params; }
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_callback_start(CFCMethod *method) { CFCParamList *param_list = CFCMethod_get_param_list(method); static const char pattern[] = " dSP;\n" " EXTEND(SP, %d);\n" " ENTER;\n" " SAVETMPS;\n" " PUSHMARK(SP);\n" " mPUSHs((SV*)Cfish_Obj_To_Host((cfish_Obj*)self));\n"; int num_args = (int)CFCParamList_num_vars(param_list) - 1; int num_to_extend = num_args == 0 ? 1 : num_args == 1 ? 2 : 1 + (num_args * 2); char *params = CFCUtil_sprintf(pattern, num_to_extend); // Iterate over arguments, mapping them to Perl scalars. CFCVariable **arg_vars = CFCParamList_get_variables(param_list); for (int i = 1; arg_vars[i] != NULL; i++) { CFCVariable *var = arg_vars[i]; const char *name = CFCVariable_micro_sym(var); CFCType *type = CFCVariable_get_type(var); const char *c_type = CFCType_to_c(type); // Add labels when there are two or more parameters. if (num_args > 1) { char num_buf[20]; sprintf(num_buf, "%d", (int)strlen(name)); params = CFCUtil_cat(params, " mPUSHp(\"", name, "\", ", num_buf, ");\n", NULL); } if (CFCType_is_string_type(type)) { // Convert Clownfish string type to UTF-8 Perl string scalars. params = CFCUtil_cat(params, " mPUSHs(XSBind_cb_to_sv(", "(cfish_CharBuf*)", name, "));\n", NULL); } else if (CFCType_is_object(type)) { // Wrap other Clownfish object types in Perl objects. params = CFCUtil_cat(params, " mPUSHs(XSBind_cfish_to_perl(", "(cfish_Obj*)", name, "));\n", NULL); } else if (CFCType_is_integer(type)) { // Convert primitive integer types to IV Perl scalars. int width = (int)CFCType_get_width(type); if (width != 0 && width <= 4) { params = CFCUtil_cat(params, " mPUSHi(", name, ");\n", NULL); } else { // If the Perl IV integer type is not wide enough, use // doubles. This may be lossy if the value is above 2**52, // but practically speaking, it's important to handle numbers // between 2**32 and 2**52 cleanly. params = CFCUtil_cat(params, " if (sizeof(IV) >= sizeof(", c_type, ")) { mPUSHi(", name, "); }\n", " else { mPUSHn((double)", name, "); } // lossy \n", NULL); } } else if (CFCType_is_floating(type)) { // Convert primitive floating point types to NV Perl scalars. params = CFCUtil_cat(params, " mPUSHn(", name, ");\n", NULL); } else { // Can't map variable type. Signal to caller. FREEMEM(params); return NULL; } } // Restore the Perl stack pointer. params = CFCUtil_cat(params, " PUTBACK;\n", NULL); return params; }