Ejemplo n.º 1
0
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;
    }
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
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;
}
Ejemplo n.º 5
0
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;
}
Ejemplo n.º 6
0
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;
            }
        }
    }
}
Ejemplo n.º 7
0
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;
}
Ejemplo n.º 8
0
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;
}
Ejemplo n.º 9
0
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;
}
Ejemplo n.º 10
0
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++;
    }
}
Ejemplo n.º 11
0
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;
}
Ejemplo n.º 12
0
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;
}
Ejemplo n.º 13
0
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;
}
Ejemplo n.º 14
0
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;
}
Ejemplo n.º 15
0
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;
}
Ejemplo n.º 16
0
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;
}
Ejemplo n.º 17
0
// 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;
}
Ejemplo n.º 18
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();
}
Ejemplo n.º 19
0
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;
}
Ejemplo n.º 20
0
/* 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;
}
Ejemplo n.º 21
0
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;
}
Ejemplo n.º 22
0
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;
}
Ejemplo n.º 23
0
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;
}
Ejemplo n.º 24
0
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;
}