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

Reply via email to