Repository: lucy-clownfish Updated Branches: refs/heads/master fe23648e7 -> 2a7728094
Autogenerate Perl subroutine code samples Resolves CLOWNFISH-64. Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/5e7c5cea Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/5e7c5cea Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/5e7c5cea Branch: refs/heads/master Commit: 5e7c5ceaa56e900417c67550fab2573598cb8eb4 Parents: fe23648 Author: Nick Wellnhofer <wellnho...@aevum.de> Authored: Sat Dec 12 15:34:03 2015 +0100 Committer: Nick Wellnhofer <wellnho...@aevum.de> Committed: Sat Dec 12 15:34:03 2015 +0100 ---------------------------------------------------------------------- compiler/src/CFCPerlPod.c | 220 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 217 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/5e7c5cea/compiler/src/CFCPerlPod.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCPerlPod.c b/compiler/src/CFCPerlPod.c index add7bfa..5d1ecc4 100644 --- a/compiler/src/CFCPerlPod.c +++ b/compiler/src/CFCPerlPod.c @@ -34,6 +34,8 @@ #include "CFCDocuComment.h" #include "CFCUri.h" #include "CFCDocument.h" +#include "CFCType.h" +#include "CFCVariable.h" #ifndef true #define true 1 @@ -64,6 +66,24 @@ static const CFCMeta CFCPERLPOD_META = { }; static char* +S_gen_code_sample(CFCFunction *func, const char *alias, CFCClass *klass, + int is_constructor); + +static char* +S_gen_positional_sample(const char *invocant, const char *alias, + CFCParamList *param_list, size_t start); + +static char* +S_gen_labeled_sample(const char *invocant, const char *alias, + CFCParamList *param_list, size_t start); + +static char* +S_perl_var_name(CFCVariable *var); + +static char* +S_camel_to_lower(const char *camel); + +static char* S_nodes_to_pod(cmark_node *node, CFCClass *klass, int header_level); static char* @@ -124,7 +144,7 @@ CFCPerlPod_add_method(CFCPerlPod *self, const char *alias, const char *method, NamePod *slot = &self->methods[self->num_methods - 1]; slot->alias = CFCUtil_strdup(alias); slot->func = method ? CFCUtil_strdup(method) : NULL; - slot->sample = CFCUtil_strdup(sample ? sample : ""); + slot->sample = sample ? CFCUtil_strdup(sample) : NULL; slot->pod = pod ? CFCUtil_strdup(pod) : NULL; } @@ -138,7 +158,7 @@ CFCPerlPod_add_constructor(CFCPerlPod *self, const char *alias, NamePod *slot = &self->constructors[self->num_constructors - 1]; slot->alias = CFCUtil_strdup(alias ? alias : "new"); slot->func = CFCUtil_strdup(initializer ? initializer : "init"); - slot->sample = CFCUtil_strdup(sample ? sample : ""); + slot->sample = sample ? CFCUtil_strdup(sample) : NULL; slot->pod = pod ? CFCUtil_strdup(pod) : NULL; } @@ -323,7 +343,13 @@ CFCPerlPod_gen_subroutine_pod(CFCFunction *func, } // Add code sample. - if (code_sample && strlen(code_sample)) { + if (!code_sample) { + char *auto_sample + = S_gen_code_sample(func, alias, klass, is_constructor); + pod = CFCUtil_cat(pod, auto_sample, "\n", NULL); + FREEMEM(auto_sample); + } + else { pod = CFCUtil_cat(pod, code_sample, "\n", NULL); } @@ -377,6 +403,194 @@ CFCPerlPod_gen_subroutine_pod(CFCFunction *func, return pod; } +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_positional_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); + + 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($%s);\n", invocant, alias, name); + + const char *init = inits[start]; + if (init) { + if (strcmp(init, "NULL") == 0) { init = "undef"; } + char *def_sample = CFCUtil_sprintf(" %s->%s(); # default: %s\n", + invocant, alias, init); + sample = CFCUtil_cat(sample, def_sample, NULL); + FREEMEM(def_sample); + } + + return sample; +} + +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 char* +S_perl_var_name(CFCVariable *var) { + CFCType *type = CFCVariable_get_type(var); + const char *specifier = CFCType_get_specifier(type); + char *perl_name = NULL; + + if (CFCType_is_object(type)) { + // Skip parcel prefix. + if (islower(*specifier)) { + for (specifier++; *specifier; specifier++) { + if (*specifier == '_') { + specifier++; + break; + } + } + } + + perl_name = S_camel_to_lower(specifier); + } + else if (CFCType_is_integer(type)) { + if (strcmp(specifier, "bool") == 0) { + perl_name = CFCUtil_strdup("bool"); + } + else { + perl_name = CFCUtil_strdup("int"); + } + } + else if (CFCType_is_floating(type)) { + perl_name = CFCUtil_strdup("float"); + } + else { + CFCUtil_die("Don't know how to create code sample for type '%s'", + specifier); + } + + return perl_name; +} + +static char* +S_camel_to_lower(const char *camel) { + if (camel[0] == '\0') { return CFCUtil_strdup(""); } + + size_t alloc = 1; + for (size_t i = 1; camel[i]; i++) { + if (isupper(camel[i]) && islower(camel[i+1])) { + alloc += 1; + } + alloc += 1; + } + char *lower = MALLOCATE(alloc + 1); + + lower[0] = tolower(camel[0]); + size_t j = 1; + for (size_t i = 1; camel[i]; i++) { + // Only insert underscore if next char is lowercase. + if (isupper(camel[i]) && islower(camel[i+1])) { + lower[j++] = '_'; + } + lower[j++] = tolower(camel[i]); + } + lower[j] = '\0'; + + return lower; +} + char* CFCPerlPod_md_to_pod(const char *md, CFCClass *klass, int header_level) { int options = CMARK_OPT_NORMALIZE