You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucy.apache.org by ma...@apache.org on 2016/02/25 00:07:28 UTC

[01/20] lucy-clownfish git commit: Use Python refcounting.

Repository: lucy-clownfish
Updated Branches:
  refs/heads/master 32e2701aa -> 7c80fb818


Use Python refcounting.

In our Python bindings, Clownfish objects are Python objects, so use
Python's refcounting.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/7910ca91
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/7910ca91
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/7910ca91

Branch: refs/heads/master
Commit: 7910ca91a9005798ae8d6537f5fb55e01d07a17a
Parents: 32e2701
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Wed Jan 20 16:25:02 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:01 2016 -0800

----------------------------------------------------------------------
 runtime/python/cfext/CFBind.c | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/7910ca91/runtime/python/cfext/CFBind.c
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.c b/runtime/python/cfext/CFBind.c
index a386e4c..6b13120 100644
--- a/runtime/python/cfext/CFBind.c
+++ b/runtime/python/cfext/CFBind.c
@@ -40,23 +40,20 @@
 
 uint32_t
 cfish_get_refcount(void *vself) {
-    CFISH_UNUSED_VAR(vself);
-    CFISH_THROW(CFISH_ERR, "TODO");
-    CFISH_UNREACHABLE_RETURN(uint32_t);
+    return Py_REFCNT(vself);
 }
 
 cfish_Obj*
 cfish_inc_refcount(void *vself) {
-    CFISH_UNUSED_VAR(vself);
-    CFISH_THROW(CFISH_ERR, "TODO");
-    CFISH_UNREACHABLE_RETURN(cfish_Obj*);
+    Py_INCREF(vself);
+    return (cfish_Obj*)vself;
 }
 
 uint32_t
 cfish_dec_refcount(void *vself) {
-    CFISH_UNUSED_VAR(vself);
-    CFISH_THROW(CFISH_ERR, "TODO");
-    CFISH_UNREACHABLE_RETURN(uint32_t);
+    uint32_t modified_refcount = Py_REFCNT(vself);
+    Py_DECREF(vself);
+    return modified_refcount;
 }
 
 /**** Obj ******************************************************************/


[14/20] lucy-clownfish git commit: Add dummy routine for fetching PyTypeObject.

Posted by ma...@apache.org.
Add dummy routine for fetching PyTypeObject.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/9bbb13f4
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/9bbb13f4
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/9bbb13f4

Branch: refs/heads/master
Commit: 9bbb13f48240d2365b02371bf9f9ce23c7e30c34
Parents: 2b65f36
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Wed Jan 27 17:06:12 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:04 2016 -0800

----------------------------------------------------------------------
 runtime/python/cfext/CFBind.c | 10 ++++++++++
 1 file changed, 10 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/9bbb13f4/runtime/python/cfext/CFBind.c
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.c b/runtime/python/cfext/CFBind.c
index e7d15ed..9ecd11e 100644
--- a/runtime/python/cfext/CFBind.c
+++ b/runtime/python/cfext/CFBind.c
@@ -42,6 +42,9 @@
 
 static bool Err_initialized;
 
+static PyTypeObject*
+S_get_cached_py_type(cfish_Class *klass);
+
 /**** Utility **************************************************************/
 
 static bool
@@ -693,6 +696,13 @@ CFISH_Obj_To_Host_IMP(cfish_Obj *self) {
 
 /**** Class ****************************************************************/
 
+static PyTypeObject*
+S_get_cached_py_type(cfish_Class *self) {
+    // FIXME: dummy implementation
+    CFISH_UNUSED_VAR(self);
+    return NULL;
+}
+
 cfish_Obj*
 CFISH_Class_Make_Obj_IMP(cfish_Class *self) {
     CFISH_UNUSED_VAR(self);


[18/20] lucy-clownfish git commit: Gen OVERRIDE callbacks for Python.

Posted by ma...@apache.org.
Gen OVERRIDE callbacks for Python.

Generate the C functions which call back into Python from
Clownfish-flavored C when a method is overridden.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/335b61fc
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/335b61fc
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/335b61fc

Branch: refs/heads/master
Commit: 335b61fc0f35868d1cda0bb1bbd879c616df1ae3
Parents: 02617ac
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Wed Jan 27 19:24:32 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:05 2016 -0800

----------------------------------------------------------------------
 compiler/src/CFCPyMethod.c | 109 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 107 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/335b61fc/compiler/src/CFCPyMethod.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCPyMethod.c b/compiler/src/CFCPyMethod.c
index 3cc7b96..983df28 100644
--- a/compiler/src/CFCPyMethod.c
+++ b/compiler/src/CFCPyMethod.c
@@ -15,6 +15,7 @@
  */
 
 #include <string.h>
+#include <ctype.h>
 
 #include "CFCPyMethod.h"
 #include "CFCPyTypeMap.h"
@@ -47,6 +48,93 @@ S_build_unused_vars(CFCVariable **vars);
 static char*
 S_maybe_unreachable(CFCType *return_type);
 
+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_build_pymeth_invocation(CFCMethod *method) {
+    CFCType *return_type = CFCMethod_get_return_type(method);
+    const char *micro_sym = CFCSymbol_get_name((CFCSymbol*)method);
+    char *invocation = NULL;
+    const char *ret_type_str = CFCType_to_c(return_type);
+
+    if (CFCType_is_void(return_type)) {
+        const char pattern[] =
+            "    CALL_PYMETH_VOID((PyObject*)self, \"%s\", cfcb_ARGS);";
+        invocation = CFCUtil_sprintf(pattern, micro_sym);
+    }
+    else if (CFCType_is_object(return_type)) {
+        const char *nullable
+            = CFCType_nullable(return_type) ? "true" : "false";
+        const char *ret_class = CFCType_get_class_var(return_type);
+        const char pattern[] =
+            "    %s cfcb_RESULT = (%s)CALL_PYMETH_OBJ((PyObject*)self, \"%s\", cfcb_ARGS, %s, %s);";
+        invocation = CFCUtil_sprintf(pattern, ret_type_str, ret_type_str, micro_sym,
+                                     ret_class, nullable);
+    }
+    else if (CFCType_is_primitive(return_type)) {
+        char type_upcase[64];
+        if (strlen(ret_type_str) > 63) {
+            CFCUtil_die("Unexpectedly long type name: %s", ret_type_str);
+        }
+        for (int i = 0, max = strlen(ret_type_str) + 1; i < max; i++) {
+            type_upcase[i] = toupper(ret_type_str[i]);
+        }
+        const char pattern[] =
+            "    %s cfcb_RESULT = CALL_PYMETH_%s((PyObject*)self, \"%s\", cfcb_ARGS);";
+        invocation = CFCUtil_sprintf(pattern, ret_type_str, type_upcase,
+                                     micro_sym);
+    }
+    else {
+        CFCUtil_die("Unexpected return type: %s", CFCType_to_c(return_type));
+    }
+
+    return invocation;
+}
+
+static char*
+S_callback_refcount_mods(CFCParamList *param_list) {
+    char *refcount_mods = CFCUtil_strdup("");
+    CFCVariable **arg_vars = CFCParamList_get_variables(param_list);
+
+    // Adjust refcounts of arguments per method signature, so that Perl code
+    // does not have to.
+    for (int i = 0; arg_vars[i] != NULL; i++) {
+        CFCVariable *var  = arg_vars[i];
+        CFCType     *type = CFCVariable_get_type(var);
+        const char  *name = CFCVariable_get_name(var);
+        if (!CFCType_is_object(type)) {
+            continue;
+        }
+        else if (CFCType_incremented(type)) {
+            refcount_mods = CFCUtil_cat(refcount_mods, "    CFISH_INCREF(",
+                                        name, ");\n", NULL);
+        }
+        else if (CFCType_decremented(type)) {
+            refcount_mods = CFCUtil_cat(refcount_mods, "    CFISH_DECREF(",
+                                        name, ");\n", NULL);
+        }
+    }
+
+    return refcount_mods;
+}
+
 char*
 CFCPyMethod_callback_def(CFCMethod *method, CFCClass *invoker) {
     CFCParamList *param_list   = CFCMethod_get_param_list(method);
@@ -57,8 +145,25 @@ CFCPyMethod_callback_def(CFCMethod *method, CFCClass *invoker) {
     char         *override_sym = CFCMethod_full_override_sym(method, invoker);
     char *content;
 
-    //if (CFCMethod_can_be_bound(method)) {
-    if (false) {
+    if (CFCMethod_can_be_bound(method)) {
+        char *py_args = S_build_py_args(param_list);
+        char *invocation = S_build_pymeth_invocation(method);
+        char *refcount_mods = S_callback_refcount_mods(param_list);
+        const char *maybe_return = CFCType_is_void(return_type)
+                                   ? ""
+                                   : "    return cfcb_RESULT;\n";
+
+        const char pattern[] =
+            "%s\n"
+            "%s(%s) {\n"
+            "%s\n"
+            "%s\n"
+            "%s"
+            "%s"
+            "}\n";
+        content = CFCUtil_sprintf(pattern, ret_type_str, override_sym, params,
+                                  py_args, invocation, refcount_mods,
+                                  maybe_return);
     }
     else {
         char *unused = S_build_unused_vars(vars);


[05/20] lucy-clownfish git commit: Re-raise Python exceptions from Clownfish.

Posted by ma...@apache.org.
Re-raise Python exceptions from Clownfish.

Add a function which takes the Python `sys.exc_info`, wraps it in a
Clownfish Err, and THROWs it.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/9162199c
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/9162199c
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/9162199c

Branch: refs/heads/master
Commit: 9162199c26ab869bff1133d517388899468b4170
Parents: eccf1b6
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Fri Jan 22 16:51:01 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:02 2016 -0800

----------------------------------------------------------------------
 runtime/python/cfext/CFBind.c | 36 ++++++++++++++++++++++++++++++++++++
 runtime/python/cfext/CFBind.h | 13 +++++++++++++
 2 files changed, 49 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/9162199c/runtime/python/cfext/CFBind.c
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.c b/runtime/python/cfext/CFBind.c
index 3c4bb4b..95ae70d 100644
--- a/runtime/python/cfext/CFBind.c
+++ b/runtime/python/cfext/CFBind.c
@@ -40,6 +40,42 @@
 
 static bool Err_initialized;
 
+/**** Utility **************************************************************/
+
+void
+CFBind_reraise_pyerr(cfish_Class *err_klass, cfish_String *mess) {
+    PyObject *type, *value, *traceback;
+    PyObject *type_pystr = NULL;
+    PyObject *value_pystr = NULL;
+    PyObject *traceback_pystr = NULL;
+    char *type_str = "";
+    char *value_str = "";
+    char *traceback_str = "";
+    PyErr_GetExcInfo(&type, &value, &traceback);
+    if (type != NULL) {
+        PyObject *type_pystr = PyObject_Str(type);
+        type_str = PyUnicode_AsUTF8(type_pystr);
+    }
+    if (value != NULL) {
+        PyObject *value_pystr = PyObject_Str(value);
+        value_str = PyUnicode_AsUTF8(value_pystr);
+    }
+    if (traceback != NULL) {
+        PyObject *traceback_pystr = PyObject_Str(traceback);
+        traceback_str = PyUnicode_AsUTF8(traceback_pystr);
+    }
+    cfish_String *new_mess = cfish_Str_newf("%o... %s: %s %s", mess, type_str,
+                                            value_str, traceback_str);
+    Py_XDECREF(type);
+    Py_XDECREF(value);
+    Py_XDECREF(traceback);
+    Py_XDECREF(type_pystr);
+    Py_XDECREF(value_pystr);
+    Py_XDECREF(traceback_pystr);
+    CFISH_DECREF(mess);
+    cfish_Err_throw_mess(err_klass, new_mess);
+}
+
 /**** refcounting **********************************************************/
 
 uint32_t

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/9162199c/runtime/python/cfext/CFBind.h
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.h b/runtime/python/cfext/CFBind.h
index 810f201..d6a362e 100644
--- a/runtime/python/cfext/CFBind.h
+++ b/runtime/python/cfext/CFBind.h
@@ -24,6 +24,19 @@ extern "C" {
 #include "cfish_platform.h"
 #include "Python.h"
 
+struct cfish_Class;
+struct cfish_String;
+
+/** Wrap the current state of Python's sys.exc_info in a Clownfish Err and
+  * throw it.
+  *
+  * One refcount of `mess` will be consumed by this function.
+  *
+  * TODO: Leave the original exception intact.
+  */
+void
+CFBind_reraise_pyerr(struct cfish_Class *err_klass, struct cfish_String *mess);
+
 #ifdef __cplusplus
 }
 #endif


[10/20] lucy-clownfish git commit: Implement scalar types To_Host for Python.

Posted by ma...@apache.org.
Implement scalar types To_Host for Python.

- String  -> string
- Blob    -> bytes
- Boolean -> bool
- Integer -> int
- Float   -> float


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/5d3809ba
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/5d3809ba
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/5d3809ba

Branch: refs/heads/master
Commit: 5d3809ba960455a6695fcfece1063e7d91f23fac
Parents: bebc758
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Mon Jan 25 17:26:40 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:03 2016 -0800

----------------------------------------------------------------------
 runtime/python/cfext/CFBind.c | 38 ++++++++++++++++++++------------------
 1 file changed, 20 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/5d3809ba/runtime/python/cfext/CFBind.c
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.c b/runtime/python/cfext/CFBind.c
index f524b78..e991bf6 100644
--- a/runtime/python/cfext/CFBind.c
+++ b/runtime/python/cfext/CFBind.c
@@ -552,23 +552,23 @@ cfish_TestUtils_destroy_host_runtime(void *runtime) {
 
 void*
 CFISH_Str_To_Host_IMP(cfish_String *self) {
-    CFISH_UNUSED_VAR(self);
-    CFISH_THROW(CFISH_ERR, "TODO");
-    CFISH_UNREACHABLE_RETURN(void*);
+    const char *ptr = CFISH_Str_Get_Ptr8(self);
+    size_t size = CFISH_Str_Get_Size(self);
+    return PyUnicode_FromStringAndSize(ptr, size);
 }
 
 void*
 CFISH_Blob_To_Host_IMP(cfish_Blob *self) {
-    CFISH_UNUSED_VAR(self);
-    CFISH_THROW(CFISH_ERR, "TODO");
-    CFISH_UNREACHABLE_RETURN(void*);
+    const char *buf = CFISH_Blob_Get_Buf(self);
+    size_t size = CFISH_Blob_Get_Size(self);
+    return PyBytes_FromStringAndSize(buf, size);
 }
 
 void*
 CFISH_BB_To_Host_IMP(cfish_ByteBuf *self) {
-    CFISH_UNUSED_VAR(self);
-    CFISH_THROW(CFISH_ERR, "TODO");
-    CFISH_UNREACHABLE_RETURN(void*);
+    CFISH_BB_To_Host_t super_to_host
+        = CFISH_SUPER_METHOD_PTR(CFISH_BYTEBUF, CFISH_BB_To_Host);
+    return super_to_host(self);
 }
 
 void*
@@ -613,22 +613,24 @@ CFISH_Hash_To_Host_IMP(cfish_Hash *self) {
 
 void*
 CFISH_Float_To_Host_IMP(cfish_Float *self) {
-    CFISH_UNUSED_VAR(self);
-    CFISH_THROW(CFISH_ERR, "TODO");
-    CFISH_UNREACHABLE_RETURN(void*);
+    return PyFloat_FromDouble(CFISH_Float_Get_Value(self));
 }
 
 void*
 CFISH_Int_To_Host_IMP(cfish_Integer *self) {
-    CFISH_UNUSED_VAR(self);
-    CFISH_THROW(CFISH_ERR, "TODO");
-    CFISH_UNREACHABLE_RETURN(void*);
+    int64_t num = CFISH_Int_Get_Value(self);
+    return PyLong_FromLongLong(num);
 }
 
 void*
 CFISH_Bool_To_Host_IMP(cfish_Boolean *self) {
-    CFISH_UNUSED_VAR(self);
-    CFISH_THROW(CFISH_ERR, "TODO");
-    CFISH_UNREACHABLE_RETURN(void*);
+    if (self == CFISH_TRUE) {
+        Py_INCREF(Py_True);
+        return Py_True;
+    }
+    else {
+        Py_INCREF(Py_False);
+        return Py_False;
+    }
 }
 


[12/20] lucy-clownfish git commit: ParseTuple routines for Obj, String.

Posted by ma...@apache.org.
ParseTuple routines for Obj, String.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/514827b7
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/514827b7
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/514827b7

Branch: refs/heads/master
Commit: 514827b7293471645b0d6a5d450ef7ad1177e79d
Parents: 9bbb13f
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Wed Jan 27 17:13:51 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:04 2016 -0800

----------------------------------------------------------------------
 runtime/python/cfext/CFBind.c | 100 +++++++++++++++++++++++++++++++++++++
 runtime/python/cfext/CFBind.h |  13 +++++
 2 files changed, 113 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/514827b7/runtime/python/cfext/CFBind.c
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.c b/runtime/python/cfext/CFBind.c
index 9ecd11e..7e7f617 100644
--- a/runtime/python/cfext/CFBind.c
+++ b/runtime/python/cfext/CFBind.c
@@ -285,6 +285,106 @@ CFBind_py_to_cfish_noinc(PyObject *py_obj, cfish_Class *klass,
 }
 
 static int
+S_convert_obj(PyObject *py_obj, CFBindArg *arg, bool nullable) {
+    if (py_obj == Py_None) {
+        if (nullable) {
+            return 1;
+        }
+        else {
+            PyErr_SetString(PyExc_TypeError, "Required argument cannot be None");
+            return 0;
+        }
+    }
+    PyTypeObject *py_type = S_get_cached_py_type(arg->klass);
+    if (!PyObject_TypeCheck(py_obj, py_type)) {
+        PyErr_SetString(PyExc_TypeError, "Invalid argument type");
+        return 0;
+    }
+    *((PyObject**)arg->ptr) = py_obj;
+    return 1;
+}
+
+int
+CFBind_convert_obj(PyObject *py_obj, CFBindArg *arg) {
+    return S_convert_obj(py_obj, arg, false);
+}
+
+int
+CFBind_maybe_convert_obj(PyObject *py_obj, CFBindArg *arg) {
+    return S_convert_obj(py_obj, arg, true);
+}
+
+static int
+S_convert_string(PyObject *py_obj, cfish_String **ptr, bool nullable) {
+    if (py_obj == NULL) { // Py_CLEANUP_SUPPORTED cleanup
+        if (*ptr != NULL) {
+            if (!CFISH_Str_Is_Copy_On_IncRef(*ptr)) {
+                CFISH_DECREF(*ptr);
+            }
+            *ptr = NULL;
+        }
+        return 1;
+    }
+
+    if (py_obj == Py_None) {
+        if (*ptr != NULL) {
+            // Default value passed as stack string.
+            *ptr = CFISH_Str_Clone(*ptr);
+            return Py_CLEANUP_SUPPORTED;
+        }
+        else if (nullable) {
+            return 1;
+        }
+        else {
+            PyErr_SetString(PyExc_TypeError, "Required argument cannot be None");
+            return 0;
+        }
+    }
+    else if (PyUnicode_CheckExact(py_obj)) {
+        Py_ssize_t size;
+        char *utf8 = PyUnicode_AsUTF8AndSize(py_obj, &size);
+        if (!utf8) {
+            return 0;
+        }
+        *ptr = cfish_Str_new_from_trusted_utf8(utf8, size);
+        return Py_CLEANUP_SUPPORTED;
+    }
+    else if (S_py_obj_is_a(py_obj, CFISH_STRING)) {
+        *ptr = (cfish_String*)CFISH_INCREF(py_obj);
+        return Py_CLEANUP_SUPPORTED;
+    }
+    else if (S_py_obj_is_a(py_obj, CFISH_OBJ)) {
+        *ptr = CFISH_Obj_To_String((cfish_Obj*)py_obj);
+        return Py_CLEANUP_SUPPORTED;
+    }
+    else {
+        PyObject *stringified = PyObject_Str(py_obj);
+        if (stringified == NULL) {
+            return 0;
+        }
+        Py_ssize_t size;
+        char *utf8 = PyUnicode_AsUTF8AndSize(stringified, &size);
+        if (!utf8) {
+            Py_DECREF(stringified);
+            return 0;
+        }
+        *ptr = cfish_Str_new_from_trusted_utf8(utf8, size);
+        Py_DECREF(stringified);
+        return Py_CLEANUP_SUPPORTED;
+    }
+}
+
+int
+CFBind_convert_string(PyObject *py_obj, cfish_String **ptr) {
+    return S_convert_string(py_obj, ptr, false);
+}
+
+int
+CFBind_maybe_convert_string(PyObject *py_obj, cfish_String **ptr) {
+    return S_convert_string(py_obj, ptr, true);
+}
+
+static int
 S_convert_hash(PyObject *py_obj, cfish_Hash **hash_ptr, bool nullable) {
     if (py_obj == NULL) { // Py_CLEANUP_SUPPORTED cleanup
         CFISH_DECREF(*hash_ptr);

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/514827b7/runtime/python/cfext/CFBind.h
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.h b/runtime/python/cfext/CFBind.h
index d7eb0ba..b6e57fd 100644
--- a/runtime/python/cfext/CFBind.h
+++ b/runtime/python/cfext/CFBind.h
@@ -100,16 +100,29 @@ cfish_Obj*
 CFBind_py_to_cfish_noinc(PyObject *py_obj, cfish_Class *klass,
                          void *allocation);
 
+typedef struct CFBindArg {
+    cfish_Class *klass;
+    void        *ptr;
+} CFBindArg;
+
 /* ParseTuple conversion routines for reference types.
  *
  * If `input` is `None`, the "maybe_convert" variants will leave `ptr`
  * untouched, while the "convert" routines will raise a TypeError.
  */
 int
+CFBind_convert_obj(PyObject *input, CFBindArg *arg);
+int
+CFBind_convert_string(PyObject *input, cfish_String **ptr);
+int
 CFBind_convert_hash(PyObject *input, cfish_Hash **ptr);
 int
 CFBind_convert_vec(PyObject *input, cfish_Vector **ptr);
 int
+CFBind_maybe_convert_obj(PyObject *input, CFBindArg *arg);
+int
+CFBind_maybe_convert_string(PyObject *input, cfish_String **ptr);
+int
 CFBind_maybe_convert_hash(PyObject *input, cfish_Hash **ptr);
 int
 CFBind_maybe_convert_vec(PyObject *input, cfish_Vector **ptr);


[02/20] lucy-clownfish git commit: `To_Host` for Python returns incref'd self.

Posted by ma...@apache.org.
`To_Host` for Python returns incref'd self.

The default implementation for `To_Host` for Python just returns the
result of calling INCREF on the invocant.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/3e4952ca
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/3e4952ca
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/3e4952ca

Branch: refs/heads/master
Commit: 3e4952ca641308a8704fa41c51786dace4389ce7
Parents: 2aab6ef
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Mon Jan 25 16:41:37 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:02 2016 -0800

----------------------------------------------------------------------
 runtime/python/cfext/CFBind.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/3e4952ca/runtime/python/cfext/CFBind.c
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.c b/runtime/python/cfext/CFBind.c
index 7653325..ef0d094 100644
--- a/runtime/python/cfext/CFBind.c
+++ b/runtime/python/cfext/CFBind.c
@@ -405,9 +405,7 @@ cfish_dec_refcount(void *vself) {
 
 void*
 CFISH_Obj_To_Host_IMP(cfish_Obj *self) {
-    CFISH_UNUSED_VAR(self);
-    CFISH_THROW(CFISH_ERR, "TODO");
-    CFISH_UNREACHABLE_RETURN(void*);
+    return CFISH_INCREF(self);
 }
 
 /**** Class ****************************************************************/


[09/20] lucy-clownfish git commit: To_Host for composite types to Python.

Posted by ma...@apache.org.
To_Host for composite types to Python.

Implement To_Host conversion for Hash -> Python dict and Vector ->
Python list.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/bebc7589
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/bebc7589
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/bebc7589

Branch: refs/heads/master
Commit: bebc75899ec1dbbbd040fbf6ff21b1b6c4b9be97
Parents: 3e4952c
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Mon Jan 25 17:02:25 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:03 2016 -0800

----------------------------------------------------------------------
 runtime/python/cfext/CFBind.c | 39 ++++++++++++++++++++++++++++++++------
 1 file changed, 33 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/bebc7589/runtime/python/cfext/CFBind.c
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.c b/runtime/python/cfext/CFBind.c
index ef0d094..f524b78 100644
--- a/runtime/python/cfext/CFBind.c
+++ b/runtime/python/cfext/CFBind.c
@@ -31,6 +31,7 @@
 #include "Clownfish/ByteBuf.h"
 #include "Clownfish/Err.h"
 #include "Clownfish/Hash.h"
+#include "Clownfish/HashIterator.h"
 #include "Clownfish/Method.h"
 #include "Clownfish/Num.h"
 #include "Clownfish/String.h"
@@ -572,16 +573,42 @@ CFISH_BB_To_Host_IMP(cfish_ByteBuf *self) {
 
 void*
 CFISH_Vec_To_Host_IMP(cfish_Vector *self) {
-    CFISH_UNUSED_VAR(self);
-    CFISH_THROW(CFISH_ERR, "TODO");
-    CFISH_UNREACHABLE_RETURN(void*);
+    uint32_t num_elems = CFISH_Vec_Get_Size(self);
+    PyObject *list = PyList_New(num_elems);
+
+    // Iterate over vector items.
+    for (uint32_t i = 0; i < num_elems; i++) {
+        cfish_Obj *val = CFISH_Vec_Fetch(self, i);
+        PyObject *item = CFBind_cfish_to_py(val);
+        PyList_SET_ITEM(list, i, item);
+    }
+
+    return list;
 }
 
 void*
 CFISH_Hash_To_Host_IMP(cfish_Hash *self) {
-    CFISH_UNUSED_VAR(self);
-    CFISH_THROW(CFISH_ERR, "TODO");
-    CFISH_UNREACHABLE_RETURN(void*);
+    PyObject *dict = PyDict_New();
+
+    // Iterate over key-value pairs.
+    cfish_HashIterator *iter = cfish_HashIter_new(self);
+    while (CFISH_HashIter_Next(iter)) {
+        cfish_String *key = (cfish_String*)CFISH_HashIter_Get_Key(iter);
+        if (!cfish_Obj_is_a((cfish_Obj*)key, CFISH_STRING)) {
+            CFISH_THROW(CFISH_ERR, "Non-string key: %o",
+                        cfish_Obj_get_class_name((cfish_Obj*)key));
+        }
+        size_t size = CFISH_Str_Get_Size(key);
+        const char *ptr = CFISH_Str_Get_Ptr8(key);
+        PyObject *py_key = PyUnicode_FromStringAndSize(ptr, size);
+        PyObject *py_val = CFBind_cfish_to_py(CFISH_HashIter_Get_Value(iter));
+        PyDict_SetItem(dict, py_key, py_val);
+        Py_DECREF(py_key);
+        Py_DECREF(py_val);
+    }
+    CFISH_DECREF(iter);
+
+    return dict;
 }
 
 void*


[13/20] lucy-clownfish git commit: Stub out callback-generating code for Python.

Posted by ma...@apache.org.
Stub out callback-generating code for Python.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/8996f0f6
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/8996f0f6
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/8996f0f6

Branch: refs/heads/master
Commit: 8996f0f62d0e07ed3b0d14934041aeb9d71fa698
Parents: 514827b
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Wed Jan 27 16:05:37 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:04 2016 -0800

----------------------------------------------------------------------
 compiler/src/CFCPyMethod.c | 38 ++++++++++++++++++++++++++++++++
 compiler/src/CFCPyMethod.h | 39 ++++++++++++++++++++++++++++++++
 compiler/src/CFCPython.c   | 49 ++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 125 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8996f0f6/compiler/src/CFCPyMethod.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCPyMethod.c b/compiler/src/CFCPyMethod.c
new file mode 100644
index 0000000..2892b86
--- /dev/null
+++ b/compiler/src/CFCPyMethod.c
@@ -0,0 +1,38 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "CFCPyMethod.h"
+#include "CFCPyTypeMap.h"
+#include "CFCUtil.h"
+#include "CFCClass.h"
+#include "CFCFunction.h"
+#include "CFCMethod.h"
+#include "CFCSymbol.h"
+#include "CFCType.h"
+#include "CFCParcel.h"
+#include "CFCParamList.h"
+#include "CFCVariable.h"
+
+#ifndef true
+#define true  1
+#define false 0
+#endif
+
+char*
+CFCPyMethod_callback_def(CFCMethod *method, CFCClass *invoker) {
+    return CFCUtil_strdup("");
+}

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8996f0f6/compiler/src/CFCPyMethod.h
----------------------------------------------------------------------
diff --git a/compiler/src/CFCPyMethod.h b/compiler/src/CFCPyMethod.h
new file mode 100644
index 0000000..15fa1e6
--- /dev/null
+++ b/compiler/src/CFCPyMethod.h
@@ -0,0 +1,39 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef H_CFCPYMETHOD
+#define H_CFCPYMETHOD
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct CFCMethod;
+struct CFCClass;
+
+/** Return C code which knows how to call back into Python for this method.  This
+ * code is run when a Python subclass has overridden a method in a Clownfish base
+ * class.
+ */
+char*
+CFCPyMethod_callback_def(struct CFCMethod *method, struct CFCClass *invoker);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_CFCPYMETHOD */
+

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8996f0f6/compiler/src/CFCPython.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCPython.c b/compiler/src/CFCPython.c
index 3efaa3a..10c9872 100644
--- a/compiler/src/CFCPython.c
+++ b/compiler/src/CFCPython.c
@@ -24,6 +24,7 @@
 #define CFC_NEED_BASE_STRUCT_DEF
 #include "CFCBase.h"
 #include "CFCPython.h"
+#include "CFCPyMethod.h"
 #include "CFCParcel.h"
 #include "CFCClass.h"
 #include "CFCMethod.h"
@@ -108,6 +109,48 @@ S_write_hostdefs(CFCPython *self) {
     FREEMEM(content);
 }
 
+static char*
+S_gen_callbacks(CFCPython *self, CFCParcel *parcel, CFCClass **ordered) {
+    char *callbacks  = CFCUtil_strdup("");
+
+    // Generate implementation files containing callback definitions.
+    for (size_t i = 0; ordered[i] != NULL; i++) {
+        CFCClass *klass = ordered[i];
+        if (CFCClass_included(klass)
+            || CFCClass_inert(klass)
+            //|| CFCClass_get_parcel(klass) != parcel
+           ) {
+            continue;
+        }
+
+        CFCMethod **fresh_methods = CFCClass_fresh_methods(klass);
+        for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) {
+            CFCMethod *method = fresh_methods[meth_num];
+
+            // Define callback.
+            if (CFCMethod_novel(method) && !CFCMethod_final(method)) {
+                char *cb_def = CFCPyMethod_callback_def(method, klass);
+                callbacks = CFCUtil_cat(callbacks, cb_def, "\n", NULL);
+                FREEMEM(cb_def);
+            }
+        }
+    }
+
+    static const char helpers[] =
+        ""
+        ;
+
+    static const char pattern[] =
+        "%s\n"
+        "\n"
+        "%s"
+        ;
+    char *content = CFCUtil_sprintf(pattern, helpers, callbacks);
+
+    FREEMEM(callbacks);
+    return content;
+}
+
 static void
 S_write_module_file(CFCPython *self, CFCParcel *parcel, const char *dest) {
     const char *parcel_name = CFCParcel_get_name(parcel);
@@ -127,6 +170,7 @@ S_write_module_file(CFCPython *self, CFCParcel *parcel, const char *dest) {
 
     CFCClass  **ordered = CFCHierarchy_ordered_classes(self->hierarchy);
     CFCParcel **parcels = CFCParcel_all_parcels();
+    char *callbacks          = S_gen_callbacks(self, parcel, ordered);
     char *pound_includes     = CFCUtil_strdup("");
 
     for (size_t i = 0; ordered[i] != NULL; i++) {
@@ -146,6 +190,8 @@ S_write_module_file(CFCPython *self, CFCParcel *parcel, const char *dest) {
         "#include \"CFBind.h\"\n"
         "%s\n"
         "\n"
+        "%s\n" // callbacks
+        "\n"
         "static PyModuleDef module_def = {\n"
         "    PyModuleDef_HEAD_INIT,\n"
         "    \"%s\",\n" // module name
@@ -164,7 +210,7 @@ S_write_module_file(CFCPython *self, CFCParcel *parcel, const char *dest) {
         "\n";
 
     char *content
-        = CFCUtil_sprintf(pattern, self->header, pound_includes,
+        = CFCUtil_sprintf(pattern, self->header, pound_includes, callbacks,
                           helper_mod_name, last_component, self->footer);
 
     char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "_%s.c", dest,
@@ -176,6 +222,7 @@ S_write_module_file(CFCPython *self, CFCParcel *parcel, const char *dest) {
     FREEMEM(helper_mod_name);
     FREEMEM(pymod_name);
     FREEMEM(pound_includes);
+    FREEMEM(callbacks);
     FREEMEM(ordered);
 }
 


[19/20] lucy-clownfish git commit: Prepare to trap errors in Python glue code.

Posted by ma...@apache.org.
Prepare to trap errors in Python glue code.

Add private macro `CFBIND_TRY` and supporting routines, in an effort to
wrap Clownfish C routines and trap errors.  This will be used inside
glue code for Python, setting `sys.exc_info` when something goes wrong.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/91622dbf
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/91622dbf
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/91622dbf

Branch: refs/heads/master
Commit: 91622dbf97f1afb2a195609fd1bea2b703119211
Parents: bb7e880
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Thu Feb 18 19:31:30 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:05 2016 -0800

----------------------------------------------------------------------
 compiler/src/CFCPython.c      |  9 +++++++++
 runtime/python/cfext/CFBind.c | 22 ++++++++++++++++++++++
 runtime/python/cfext/CFBind.h | 12 ++++++++++++
 3 files changed, 43 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/91622dbf/compiler/src/CFCPython.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCPython.c b/compiler/src/CFCPython.c
index b39240d..a2b13a4 100644
--- a/compiler/src/CFCPython.c
+++ b/compiler/src/CFCPython.c
@@ -149,6 +149,15 @@ S_gen_callbacks(CFCPython *self, CFCParcel *parcel, CFCClass **ordered) {
         "    va_end(args);\n"
         "    return tuple;\n"
         "}\n"
+        "#define CFBIND_TRY(routine) \\\n"
+        "    do { \\\n"
+        "        jmp_buf env; \\\n"
+        "        jmp_buf *prev_env = CFBind_swap_env(&env); \\\n"
+        "        if (!setjmp(env)) { \\\n"
+        "            routine; \\\n"
+        "        } \\\n"
+        "        CFBind_swap_env(prev_env); \\\n"
+        "    } while (0)\n"
         "\n"
         "static PyObject*\n"
         "S_call_pymeth(PyObject *self, const char *meth_name, PyObject *args,\n"

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/91622dbf/runtime/python/cfext/CFBind.c
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.c b/runtime/python/cfext/CFBind.c
index 7e7f617..48743a7 100644
--- a/runtime/python/cfext/CFBind.c
+++ b/runtime/python/cfext/CFBind.c
@@ -855,6 +855,28 @@ static cfish_Err *current_error;
 static cfish_Err *thrown_error;
 static jmp_buf  *current_env;
 
+jmp_buf*
+CFBind_swap_env(jmp_buf *env) {
+    jmp_buf *prev_env = current_env;
+    current_env = env;
+    return prev_env;
+}
+
+int
+CFBind_migrate_cferr() {
+    if (thrown_error != NULL) {
+        cfish_Err *err = thrown_error;
+        thrown_error = NULL;
+        cfish_String *mess = CFISH_Err_Get_Mess(err);
+        char *utf8 = CFISH_Str_To_Utf8(mess);
+        PyErr_SetString(PyExc_RuntimeError, utf8);
+        CFISH_FREEMEM(utf8);
+        CFISH_DECREF(err);
+        return true;
+    }
+    return false;
+}
+
 void
 cfish_Err_init_class(void) {
     Err_initialized = true;

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/91622dbf/runtime/python/cfext/CFBind.h
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.h b/runtime/python/cfext/CFBind.h
index b6e57fd..9279510 100644
--- a/runtime/python/cfext/CFBind.h
+++ b/runtime/python/cfext/CFBind.h
@@ -21,6 +21,7 @@
 extern "C" {
 #endif
 
+#include <setjmp.h>
 #include "cfish_platform.h"
 #include "Python.h"
 #include "Clownfish/Obj.h"
@@ -38,6 +39,17 @@ struct cfish_String;
 void
 CFBind_reraise_pyerr(struct cfish_Class *err_klass, struct cfish_String *mess);
 
+/** Set the current_env, return the old value (internal use only).
+  */
+jmp_buf*
+CFBind_swap_env(jmp_buf *env);
+
+/** If a Clownfish error was thrown, propagate it to Python and return true
+ * (internal use only).
+  */
+int
+CFBind_migrate_cferr(void);
+
 /** Null-safe invocation of Obj_To_Host.
   */
 static CFISH_INLINE PyObject*


[08/20] lucy-clownfish git commit: Add CF pound-includes in autogen Py module file.

Posted by ma...@apache.org.
Add CF pound-includes in autogen Py module file.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/82d79df8
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/82d79df8
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/82d79df8

Branch: refs/heads/master
Commit: 82d79df8bc4c57ff42922a60436425c078ae6731
Parents: 5d3809b
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Tue Jan 26 17:24:22 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:03 2016 -0800

----------------------------------------------------------------------
 compiler/src/CFCPython.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/82d79df8/compiler/src/CFCPython.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCPython.c b/compiler/src/CFCPython.c
index 2c57eb0..3efaa3a 100644
--- a/compiler/src/CFCPython.c
+++ b/compiler/src/CFCPython.c
@@ -125,12 +125,26 @@ S_write_module_file(CFCPython *self, CFCParcel *parcel, const char *dest) {
         helper_mod_name[i] = tolower(helper_mod_name[i]);
     }
 
+    CFCClass  **ordered = CFCHierarchy_ordered_classes(self->hierarchy);
+    CFCParcel **parcels = CFCParcel_all_parcels();
+    char *pound_includes     = CFCUtil_strdup("");
+
+    for (size_t i = 0; ordered[i] != NULL; i++) {
+        CFCClass *klass = ordered[i];
+        if (CFCClass_included(klass)) { continue; }
+
+        const char *include_h  = CFCClass_include_h(klass);
+        pound_includes = CFCUtil_cat(pound_includes, "#include \"",
+                                     include_h, "\"\n", NULL);
+    }
+
     const char pattern[] =
         "%s\n"
         "\n"
         "#include \"Python.h\"\n"
         "#include \"cfish_parcel.h\"\n"
         "#include \"CFBind.h\"\n"
+        "%s\n"
         "\n"
         "static PyModuleDef module_def = {\n"
         "    PyModuleDef_HEAD_INIT,\n"
@@ -150,7 +164,7 @@ S_write_module_file(CFCPython *self, CFCParcel *parcel, const char *dest) {
         "\n";
 
     char *content
-        = CFCUtil_sprintf(pattern, self->header,
+        = CFCUtil_sprintf(pattern, self->header, pound_includes,
                           helper_mod_name, last_component, self->footer);
 
     char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "_%s.c", dest,
@@ -161,6 +175,8 @@ S_write_module_file(CFCPython *self, CFCParcel *parcel, const char *dest) {
     FREEMEM(content);
     FREEMEM(helper_mod_name);
     FREEMEM(pymod_name);
+    FREEMEM(pound_includes);
+    FREEMEM(ordered);
 }
 
 void


[11/20] lucy-clownfish git commit: Add dummy py_obj_is_a implementation.

Posted by ma...@apache.org.
Add dummy py_obj_is_a implementation.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/999b39f4
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/999b39f4
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/999b39f4

Branch: refs/heads/master
Commit: 999b39f44d69744f08f86e5688a84d7ff0153cd8
Parents: 82d79df
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Tue Jan 26 17:42:39 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:03 2016 -0800

----------------------------------------------------------------------
 runtime/python/cfext/CFBind.c | 8 ++++++++
 1 file changed, 8 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/999b39f4/runtime/python/cfext/CFBind.c
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.c b/runtime/python/cfext/CFBind.c
index e991bf6..d3c39c2 100644
--- a/runtime/python/cfext/CFBind.c
+++ b/runtime/python/cfext/CFBind.c
@@ -43,6 +43,14 @@ static bool Err_initialized;
 
 /**** Utility **************************************************************/
 
+static bool
+S_py_obj_is_a(PyObject *py_obj, cfish_Class *klass) {
+    CFISH_UNUSED_VAR(py_obj);
+    CFISH_UNUSED_VAR(klass);
+    CFISH_THROW(CFISH_ERR, "TODO");
+    CFISH_UNREACHABLE_RETURN(bool);
+}
+
 void
 CFBind_reraise_pyerr(cfish_Class *err_klass, cfish_String *mess) {
     PyObject *type, *value, *traceback;


[15/20] lucy-clownfish git commit: Add helper routines for Python callbacks.

Posted by ma...@apache.org.
Add helper routines for Python callbacks.

The generated C code for calling back into Python from Clownfish when a
method is overridden will require these helper functions for running the
Python callable, converting return values, etc.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/bb7e8807
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/bb7e8807
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/bb7e8807

Branch: refs/heads/master
Commit: bb7e880799d6bb0e4743d24fcacb630e04b2e32f
Parents: 8996f0f
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Wed Jan 27 18:23:36 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:04 2016 -0800

----------------------------------------------------------------------
 compiler/src/CFCPython.c | 203 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 202 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/bb7e8807/compiler/src/CFCPython.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCPython.c b/compiler/src/CFCPython.c
index 10c9872..b39240d 100644
--- a/compiler/src/CFCPython.c
+++ b/compiler/src/CFCPython.c
@@ -137,7 +137,208 @@ S_gen_callbacks(CFCPython *self, CFCParcel *parcel, CFCClass **ordered) {
     }
 
     static const char helpers[] =
-        ""
+        "static PyObject*\n"
+        "S_pack_tuple(int num_args, ...) {\n"
+        "    PyObject *tuple = PyTuple_New(num_args);\n"
+        "    va_list args;\n"
+        "    va_start(args, num_args);\n"
+        "    for (int i = 0; i < num_args; i++) {\n"
+        "        PyObject *arg = va_arg(args, PyObject*);\n"
+        "        PyTuple_SET_ITEM(tuple, i, arg);\n"
+        "    }\n"
+        "    va_end(args);\n"
+        "    return tuple;\n"
+        "}\n"
+        "\n"
+        "static PyObject*\n"
+        "S_call_pymeth(PyObject *self, const char *meth_name, PyObject *args,\n"
+        "              const char *file, int line, const char *func) {\n"
+        "    PyObject *callable = PyObject_GetAttrString(self, meth_name);\n"
+        "    if (!PyCallable_Check(callable)) {\n"
+        "        cfish_String *mess\n"
+        "            = cfish_Err_make_mess(file, line, func, \"Attr '%s' not callable\",\n"
+        "                                  meth_name);\n"
+        "        cfish_Err_throw_mess(CFISH_ERR, mess);\n"
+        "    }\n"
+        "    PyObject *result = PyObject_CallObject(callable, args);\n"
+        "    Py_DECREF(args);\n"
+        "    if (result == NULL) {\n"
+        "        cfish_String *mess\n"
+        "            = cfish_Err_make_mess(file, line, func,\n"
+        "                                  \"Callback to '%s' failed\", meth_name);\n"
+        "        CFBind_reraise_pyerr(CFISH_ERR, mess);\n"
+        "    }\n"
+        "    return result;\n"
+        "}\n"
+        "\n"
+        "#define CALL_PYMETH_VOID(self, meth_name, args) \\\n"
+        "    S_call_pymeth_void(self, meth_name, args, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n"
+        "\n"
+        "static void\n"
+        "S_call_pymeth_void(PyObject *self, const char *meth_name, PyObject *args,\n"
+        "                   const char *file, int line, const char *func) {\n"
+        "    PyObject *py_result\n"
+        "        = S_call_pymeth(self, meth_name, args, file, line, func);\n"
+        "    if (py_result == NULL) {\n"
+        "        cfish_String *mess\n"
+        "            = cfish_Err_make_mess(file, line, func, \"Call to %s failed\",\n"
+        "                                  meth_name);\n"
+        "        CFBind_reraise_pyerr(CFISH_ERR, mess);\n"
+        "    }\n"
+        "    Py_DECREF(py_result);\n"
+        "}\n"
+        "\n"
+        "#define CALL_PYMETH_BOOL(self, meth_name, args) \\\n"
+        "    S_call_pymeth_bool(self, meth_name, args, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n"
+        "\n"
+        "static bool\n"
+        "S_call_pymeth_bool(PyObject *self, const char *meth_name, PyObject *args,\n"
+        "                   const char *file, int line, const char *func) {\n"
+        "    PyObject *py_result\n"
+        "        = S_call_pymeth(self, meth_name, args, file, line, func);\n"
+        "    int truthiness = py_result != NULL\n"
+        "                     ? PyObject_IsTrue(py_result)\n"
+        "                     : -1;\n"
+        "    if (truthiness == -1) {\n"
+        "        cfish_String *mess\n"
+        "            = cfish_Err_make_mess(file, line, func, \"Call to %s failed\",\n"
+        "                                  meth_name);\n"
+        "        CFBind_reraise_pyerr(CFISH_ERR, mess);\n"
+        "    }\n"
+        "    Py_DECREF(py_result);\n"
+        "    return !!truthiness;\n"
+        "}\n"
+        "\n"
+        "#define CALL_PYMETH_OBJ(self, meth_name, args, ret_class, nullable) \\\n"
+        "    S_call_pymeth_obj(self, meth_name, args, ret_class, nullable, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n"
+        "\n"
+        "static cfish_Obj*\n"
+        "S_call_pymeth_obj(PyObject *self, const char *meth_name,\n"
+        "                  PyObject *args, cfish_Class *ret_class, bool nullable,\n"
+        "                  const char *file, int line, const char *func) {\n"
+        "    PyObject *py_result\n"
+        "        = S_call_pymeth(self, meth_name, args, file, line, func);\n"
+        "    cfish_Obj *result = CFBind_py_to_cfish(py_result, ret_class);\n"
+        "    Py_DECREF(py_result);\n"
+        "    if (!nullable && result == NULL) {\n"
+        "        CFISH_THROW(CFISH_ERR, \"%s cannot return NULL\", meth_name);\n"
+        "    }\n"
+        "    else if (!cfish_Obj_is_a(result, ret_class)) {\n"
+        "        cfish_Class *result_class = cfish_Obj_get_class(result);\n"
+        "        CFISH_DECREF(result);\n"
+        "        CFISH_THROW(CFISH_ERR, \"%s returned %o instead of %o\", meth_name,\n"
+        "                    CFISH_Class_Get_Name(result_class),\n"
+        "                    CFISH_Class_Get_Name(ret_class));\n"
+        "    }\n"
+        "    return result;\n"
+        "}\n"
+        "\n"
+        "#define CALL_PYMETH_DOUBLE(self, meth_name, args) \\\n"
+        "    S_call_pymeth_f64(self, meth_name, args, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n"
+        "#define CALL_PYMETH_FLOAT(self, meth_name, args) \\\n"
+        "    ((float)S_call_pymeth_f64(self, meth_name, args, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
+        "\n"
+        "static double\n"
+        "S_call_pymeth_f64(PyObject *self, const char *meth_name, PyObject *args,\n"
+        "                  const char *file, int line, const char *func) {\n"
+        "    PyObject *py_result\n"
+        "        = S_call_pymeth(self, meth_name, args, file, line, func);\n"
+        "    PyErr_Clear();\n"
+        "    double result = PyFloat_AsDouble(py_result);\n"
+        "    if (PyErr_Occurred()) {\n"
+        "        cfish_String *mess\n"
+        "            = cfish_Err_make_mess(file, line, func,\n"
+        "                                  \"Converting result of '%s' to double failed\",\n"
+        "                                  meth_name);\n"
+        "        CFBind_reraise_pyerr(CFISH_ERR, mess);\n"
+        "    }\n"
+        "    Py_DECREF(py_result);\n"
+        "    return result;\n"
+        "}\n"
+        "\n"
+        "#define CALL_PYMETH_INT64_T(self, meth_name, args) \\\n"
+        "    S_call_pymeth_i64(self, meth_name, args, INT64_MAX, INT64_MIN, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n"
+        "#define CALL_PYMETH_INT32_T(self, meth_name, args) \\\n"
+        "    ((int32_t)S_call_pymeth_i64(self, meth_name, args, INT32_MAX, INT32_MIN, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
+        "#define CALL_PYMETH_INT16_T(self, meth_name, args) \\\n"
+        "    ((int16_t)S_call_pymeth_i64(self, meth_name, args, INT16_MAX, INT16_MIN, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
+        "#define CALL_PYMETH_INT8_T(self, meth_name, args) \\\n"
+        "    ((int8_t)S_call_pymeth_i64(self, meth_name, args, INT8_MAX, INT8_MIN, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
+        "#define CALL_PYMETH_CHAR(self, meth_name, args) \\\n"
+        "    ((char)S_call_pymeth_i64(self, meth_name, args, CHAR_MAX, CHAR_MIN, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
+        "#define CALL_PYMETH_SHORT(self, meth_name, args) \\\n"
+        "    ((short)S_call_pymeth_i64(self, meth_name, args, SHRT_MAX, SHRT_MIN, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
+        "#define CALL_PYMETH_INT(self, meth_name, args) \\\n"
+        "    ((int16_t)S_call_pymeth_i64(self, meth_name, args, INT_MAX, INT_MIN, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
+        "#define CALL_PYMETH_LONG(self, meth_name, args) \\\n"
+        "    ((int16_t)S_call_pymeth_i64(self, meth_name, args, LONG_MAX, LONG_MIN, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
+        "\n"
+        "static int64_t\n"
+        "S_call_pymeth_i64(PyObject *self, const char *meth_name, PyObject *args,\n"
+        "                  int64_t max, int64_t min,\n"
+        "                  const char *file, int line, const char *func) {\n"
+        "    PyObject *py_result\n"
+        "        = S_call_pymeth(self, meth_name, args, file, line, func);\n"
+        "    PyErr_Clear();\n"
+        "    int64_t result = PyLong_AsLongLong(py_result);\n"
+        "    if (PyErr_Occurred() || result > max || result < min) {\n"
+        "        cfish_String *mess\n"
+        "            = cfish_Err_make_mess(file, line, func,\n"
+        "                                  \"Converting result of '%s' to int64_t failed\",\n"
+        "                                  meth_name);\n"
+        "        CFBind_reraise_pyerr(CFISH_ERR, mess);\n"
+        "    }\n"
+        "    Py_DECREF(py_result);\n"
+        "    return result;\n"
+        "}\n"
+        "\n"
+        "#define CALL_PYMETH_UINT64_T(self, meth_name, args) \\\n"
+        "    S_call_pymeth_u64(self, meth_name, args, UINT64_MAX, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n"
+        "#define CALL_PYMETH_UINT32_T(self, meth_name, args) \\\n"
+        "    ((uint32_t)S_call_pymeth_u64(self, meth_name, args, UINT32_MAX, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
+        "#define CALL_PYMETH_UINT16_T(self, meth_name, args) \\\n"
+        "    ((uint32_t)S_call_pymeth_u64(self, meth_name, args, UINT16_MAX, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
+        "#define CALL_PYMETH_UINT8_T(self, meth_name, args) \\\n"
+        "    ((uint32_t)S_call_pymeth_u64(self, meth_name, args, UINT8_MAX, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n"
+        "#define CALL_PYMETH_SIZE_T(self, meth_name, args) \\\n"
+        "    S_call_pymeth_u64(self, meth_name, args, SIZE_MAX, \\\n"
+        "        __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n"
+        "\n"
+        "static uint64_t\n"
+        "S_call_pymeth_u64(PyObject *self, const char *meth_name, PyObject *args,\n"
+        "                  uint64_t max,\n"
+        "                  const char *file, int line, const char *func) {\n"
+        "    PyObject *py_result\n"
+        "        = S_call_pymeth(self, meth_name, args, file, line, func);\n"
+        "    PyErr_Clear();\n"
+        "    uint64_t result = PyLong_AsUnsignedLongLong(py_result);\n"
+        "    if (PyErr_Occurred()) {\n"
+        "        cfish_String *mess\n"
+        "            = cfish_Err_make_mess(file, line, func,\n"
+        "                                  \"Converting result of '%s' to uint64_t failed\",\n"
+        "                                  meth_name);\n"
+        "        CFBind_reraise_pyerr(CFISH_ERR, mess);\n"
+        "    }\n"
+        "    Py_DECREF(py_result);\n"
+        "    return result;\n"
+        "}\n"
         ;
 
     static const char pattern[] =


[06/20] lucy-clownfish git commit: Implement Clownfish exceptions using `longjmp`.

Posted by ma...@apache.org.
Implement Clownfish exceptions using `longjmp`.

Though Python uses exceptions, executing exceptions from the Python C
API requires setting thread-local error information and indicating an
error condition to the Python interpreter by returning NULL from the
glue function.  So Clownfish has to have its own independent exception
handling.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/eccf1b64
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/eccf1b64
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/eccf1b64

Branch: refs/heads/master
Commit: eccf1b645bb9042825d84bbe4db4041ad63e2fc1
Parents: 7910ca9
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Fri Jan 22 15:40:08 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:02 2016 -0800

----------------------------------------------------------------------
 runtime/python/cfext/CFBind.c | 66 +++++++++++++++++++++++++++-----------
 1 file changed, 48 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/eccf1b64/runtime/python/cfext/CFBind.c
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.c b/runtime/python/cfext/CFBind.c
index 6b13120..3c4bb4b 100644
--- a/runtime/python/cfext/CFBind.c
+++ b/runtime/python/cfext/CFBind.c
@@ -19,6 +19,8 @@
 #define C_CFISH_METHOD
 #define C_CFISH_ERR
 
+#include <setjmp.h>
+
 #include "charmony.h"
 #include "CFBind.h"
 
@@ -36,6 +38,8 @@
 #include "Clownfish/Util/Memory.h"
 #include "Clownfish/Vector.h"
 
+static bool Err_initialized;
+
 /**** refcounting **********************************************************/
 
 uint32_t
@@ -114,48 +118,74 @@ CFISH_Method_Host_Name_IMP(cfish_Method *self) {
 
 /**** Err ******************************************************************/
 
+/* TODO: Thread safety? */
+static cfish_Err *current_error;
+static cfish_Err *thrown_error;
+static jmp_buf  *current_env;
+
 void
-cfish_Err_init_class() {
-    CFISH_THROW(CFISH_ERR, "TODO");
+cfish_Err_init_class(void) {
+    Err_initialized = true;
 }
 
 cfish_Err*
 cfish_Err_get_error() {
-    CFISH_THROW(CFISH_ERR, "TODO");
-    CFISH_UNREACHABLE_RETURN(cfish_Err*);
+    return current_error;
 }
 
 void
 cfish_Err_set_error(cfish_Err *error) {
-    CFISH_UNUSED_VAR(error);
-    CFISH_THROW(CFISH_ERR, "TODO");
+    if (current_error) {
+        CFISH_DECREF(current_error);
+    }
+    current_error = error;
 }
 
 void
 cfish_Err_do_throw(cfish_Err *error) {
-    CFISH_UNUSED_VAR(error);
-    CFISH_THROW(CFISH_ERR, "TODO");
+    if (current_env) {
+        thrown_error = error;
+        longjmp(*current_env, 1);
+    }
+    else {
+        cfish_String *message = CFISH_Err_Get_Mess(error);
+        char *utf8 = CFISH_Str_To_Utf8(message);
+        fprintf(stderr, "%s", utf8);
+        CFISH_FREEMEM(utf8);
+        exit(EXIT_FAILURE);
+    }
 }
 
 void
 cfish_Err_throw_mess(cfish_Class *klass, cfish_String *message) {
-    CFISH_UNUSED_VAR(klass);
-    CFISH_UNUSED_VAR(message);
-    CFISH_THROW(CFISH_ERR, "TODO");
+    CFISH_UNUSED_VAR(klass); // TODO use klass
+    cfish_Err *err = cfish_Err_new(message);
+    cfish_Err_do_throw(err);
 }
 
 void
 cfish_Err_warn_mess(cfish_String *message) {
-    CFISH_UNUSED_VAR(message);
-    CFISH_THROW(CFISH_ERR, "TODO");
+    char *utf8 = CFISH_Str_To_Utf8(message);
+    fprintf(stderr, "%s", utf8);
+    CFISH_FREEMEM(utf8);
+    CFISH_DECREF(message);
 }
 
 cfish_Err*
-cfish_Err_trap(CFISH_Err_Attempt_t routine, void *routine_context) {
-    CFISH_UNUSED_VAR(routine);
-    CFISH_UNUSED_VAR(routine_context);
-    CFISH_THROW(CFISH_ERR, "TODO");
-    CFISH_UNREACHABLE_RETURN(cfish_Err*);
+cfish_Err_trap(CFISH_Err_Attempt_t routine, void *context) {
+    jmp_buf  env;
+    jmp_buf *prev_env = current_env;
+    current_env = &env;
+
+    if (!setjmp(env)) {
+        routine(context);
+    }
+
+    current_env = prev_env;
+
+    cfish_Err *error = thrown_error;
+    thrown_error = NULL;
+    return error;
 }
 
 /**** TestUtils ************************************************************/


[16/20] lucy-clownfish git commit: Add ParseTuple converters for composite types.

Posted by ma...@apache.org.
Add ParseTuple converters for composite types.

Provide ParseTuple-compatible C functions which convert Python list to
Clownfish Vector and Python dict to Clownfish Hash.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/2b65f360
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/2b65f360
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/2b65f360

Branch: refs/heads/master
Commit: 2b65f36064ae0f733123a82202dc97f18a6dd325
Parents: 99d6bc0
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Wed Jan 27 16:43:55 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:04 2016 -0800

----------------------------------------------------------------------
 runtime/python/cfext/CFBind.c | 78 ++++++++++++++++++++++++++++++++++++++
 runtime/python/cfext/CFBind.h | 14 +++++++
 2 files changed, 92 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/2b65f360/runtime/python/cfext/CFBind.c
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.c b/runtime/python/cfext/CFBind.c
index 501c342..e7d15ed 100644
--- a/runtime/python/cfext/CFBind.c
+++ b/runtime/python/cfext/CFBind.c
@@ -282,6 +282,84 @@ CFBind_py_to_cfish_noinc(PyObject *py_obj, cfish_Class *klass,
 }
 
 static int
+S_convert_hash(PyObject *py_obj, cfish_Hash **hash_ptr, bool nullable) {
+    if (py_obj == NULL) { // Py_CLEANUP_SUPPORTED cleanup
+        CFISH_DECREF(*hash_ptr);
+        return 1;
+    }
+
+    if (py_obj == Py_None) {
+        if (nullable) {
+            return Py_CLEANUP_SUPPORTED;
+        }
+        else {
+            PyErr_SetString(PyExc_TypeError, "Required argument cannot be None");
+            return 0;
+        }
+    }
+    else if (PyDict_CheckExact(py_obj)) {
+        *hash_ptr = S_py_dict_to_hash(py_obj);
+        return Py_CLEANUP_SUPPORTED;
+    }
+    else if (S_py_obj_is_a(py_obj, CFISH_HASH)) {
+        *hash_ptr = (cfish_Hash*)CFISH_INCREF(py_obj);
+        return Py_CLEANUP_SUPPORTED;
+    }
+    else {
+        return 0;
+    }
+}
+
+int
+CFBind_convert_hash(PyObject *py_obj, cfish_Hash **hash_ptr) {
+    return S_convert_hash(py_obj, hash_ptr, false);
+}
+
+int
+CFBind_maybe_convert_hash(PyObject *py_obj, cfish_Hash **hash_ptr) {
+    return S_convert_hash(py_obj, hash_ptr, true);
+}
+
+static int
+S_convert_vec(PyObject *py_obj, cfish_Vector **vec_ptr, bool nullable) {
+    if (py_obj == NULL) { // Py_CLEANUP_SUPPORTED cleanup
+        CFISH_DECREF(*vec_ptr);
+        return 1;
+    }
+
+    if (py_obj == Py_None) {
+        if (nullable) {
+            return Py_CLEANUP_SUPPORTED;
+        }
+        else {
+            PyErr_SetString(PyExc_TypeError, "Required argument cannot be None");
+            return 0;
+        }
+    }
+    else if (PyList_CheckExact(py_obj)) {
+        *vec_ptr = S_py_list_to_vector(py_obj);
+        return Py_CLEANUP_SUPPORTED;
+    }
+    else if (S_py_obj_is_a(py_obj, CFISH_VECTOR)) {
+        *vec_ptr = (cfish_Vector*)CFISH_INCREF(py_obj);
+        return Py_CLEANUP_SUPPORTED;
+    }
+    else {
+        return 0;
+    }
+}
+
+int
+CFBind_convert_vec(PyObject *py_obj, cfish_Vector **vec_ptr) {
+    return S_convert_vec(py_obj, vec_ptr, false);
+}
+
+int
+CFBind_maybe_convert_vec(PyObject *py_obj, cfish_Vector **vec_ptr) {
+    return S_convert_vec(py_obj, vec_ptr, true);
+}
+
+static int
 S_convert_sint(PyObject *py_obj, void *ptr, bool nullable, unsigned width) {
     if (py_obj == Py_None) {
         if (nullable) {

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/2b65f360/runtime/python/cfext/CFBind.h
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.h b/runtime/python/cfext/CFBind.h
index e4e0f23..d7eb0ba 100644
--- a/runtime/python/cfext/CFBind.h
+++ b/runtime/python/cfext/CFBind.h
@@ -100,6 +100,20 @@ cfish_Obj*
 CFBind_py_to_cfish_noinc(PyObject *py_obj, cfish_Class *klass,
                          void *allocation);
 
+/* ParseTuple conversion routines for reference types.
+ *
+ * If `input` is `None`, the "maybe_convert" variants will leave `ptr`
+ * untouched, while the "convert" routines will raise a TypeError.
+ */
+int
+CFBind_convert_hash(PyObject *input, cfish_Hash **ptr);
+int
+CFBind_convert_vec(PyObject *input, cfish_Vector **ptr);
+int
+CFBind_maybe_convert_hash(PyObject *input, cfish_Hash **ptr);
+int
+CFBind_maybe_convert_vec(PyObject *input, cfish_Vector **ptr);
+
 /* ParseTuple conversion routines for primitive numeric types.
  *
  * If the value of `input` is out of range for the an integer C type, an


[03/20] lucy-clownfish git commit: Add wrappers for `To_Host` in Python bindings.

Posted by ma...@apache.org.
Add wrappers for `To_Host` in Python bindings.

Add NULL-safe wrappers, one of which is refcount neutral.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/2aab6efe
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/2aab6efe
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/2aab6efe

Branch: refs/heads/master
Commit: 2aab6efe2533e6e5b526cd6b408e24443c6d0f61
Parents: 991e4a5
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Mon Jan 25 16:39:02 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:02 2016 -0800

----------------------------------------------------------------------
 runtime/python/cfext/CFBind.h | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/2aab6efe/runtime/python/cfext/CFBind.h
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.h b/runtime/python/cfext/CFBind.h
index 85be7af..6637fca 100644
--- a/runtime/python/cfext/CFBind.h
+++ b/runtime/python/cfext/CFBind.h
@@ -23,6 +23,7 @@ extern "C" {
 
 #include "cfish_platform.h"
 #include "Python.h"
+#include "Clownfish/Obj.h"
 
 struct cfish_Class;
 struct cfish_String;
@@ -37,6 +38,34 @@ struct cfish_String;
 void
 CFBind_reraise_pyerr(struct cfish_Class *err_klass, struct cfish_String *mess);
 
+/** Null-safe invocation of Obj_To_Host.
+  */
+static CFISH_INLINE PyObject*
+CFBind_cfish_to_py(struct cfish_Obj *obj) {
+    if (obj != NULL) {
+        return (PyObject*)CFISH_Obj_To_Host(obj);
+    }
+    else {
+        Py_RETURN_NONE;
+    }
+}
+
+/** Perform the same conversion as `CFBind_cfish_to_py`, but ensure that the
+  * result is refcount-neutral, decrementing a refcount from `obj` and passing
+  * it along.
+  */
+static CFISH_INLINE PyObject*
+CFBind_cfish_to_py_zeroref(struct cfish_Obj *obj) {
+    if (obj != NULL) {
+        PyObject *result = (PyObject*)CFISH_Obj_To_Host(obj);
+        CFISH_DECREF(obj);
+        return result;
+    }
+    else {
+        return Py_None;
+    }
+}
+
 /* ParseTuple conversion routines for primitive numeric types.
  *
  * If the value of `input` is out of range for the an integer C type, an


[20/20] lucy-clownfish git commit: Merge branch 'CLOWNFISH-66-py-glue1-v2'

Posted by ma...@apache.org.
Merge branch 'CLOWNFISH-66-py-glue1-v2'

Glue code for the Python host.

* Refcounting
* Error handling
* Host conversion for basic types
* Argument conversion
* Callbacks for overridden methods

This closes #58.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/7c80fb81
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/7c80fb81
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/7c80fb81

Branch: refs/heads/master
Commit: 7c80fb8189328bc92c17b938701b60ede8e42be2
Parents: 32e2701 335b61f
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Wed Feb 24 15:02:50 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Wed Feb 24 15:02:50 2016 -0800

----------------------------------------------------------------------
 compiler/src/CFCPyMethod.c    | 217 +++++++++
 compiler/src/CFCPyMethod.h    |  39 ++
 compiler/src/CFCPython.c      | 275 ++++++++++-
 runtime/python/cfext/CFBind.c | 917 ++++++++++++++++++++++++++++++++++---
 runtime/python/cfext/CFBind.h | 188 ++++++++
 5 files changed, 1581 insertions(+), 55 deletions(-)
----------------------------------------------------------------------



[17/20] lucy-clownfish git commit: Generate OVERRIDEs for non-overrideable meths.

Posted by ma...@apache.org.
Generate OVERRIDEs for non-overrideable meths.

Generate error-throwing OVERRIDE routines for methods which cannot be
overridden.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/02617ac0
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/02617ac0
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/02617ac0

Branch: refs/heads/master
Commit: 02617ac057b70a2358233d9f65fb205123b308e1
Parents: 91622db
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Wed Jan 27 19:14:25 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:05 2016 -0800

----------------------------------------------------------------------
 compiler/src/CFCPyMethod.c | 76 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 75 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/02617ac0/compiler/src/CFCPyMethod.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCPyMethod.c b/compiler/src/CFCPyMethod.c
index 2892b86..3cc7b96 100644
--- a/compiler/src/CFCPyMethod.c
+++ b/compiler/src/CFCPyMethod.c
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <string.h>
 
 #include "CFCPyMethod.h"
 #include "CFCPyTypeMap.h"
@@ -32,7 +33,80 @@
 #define false 0
 #endif
 
+/* Take a NULL-terminated list of CFCVariables and build up a string of
+ * directives like:
+ *
+ *     UNUSED_VAR(var1);
+ *     UNUSED_VAR(var2);
+ */
+static char*
+S_build_unused_vars(CFCVariable **vars);
+
+/* Create an unreachable return statement if necessary, in order to thwart
+ * compiler warnings. */
+static char*
+S_maybe_unreachable(CFCType *return_type);
+
 char*
 CFCPyMethod_callback_def(CFCMethod *method, CFCClass *invoker) {
-    return CFCUtil_strdup("");
+    CFCParamList *param_list   = CFCMethod_get_param_list(method);
+    CFCVariable **vars         = CFCParamList_get_variables(param_list);
+    CFCType      *return_type  = CFCMethod_get_return_type(method);
+    const char   *ret_type_str = CFCType_to_c(return_type);
+    const char   *params       = CFCParamList_to_c(param_list);
+    char         *override_sym = CFCMethod_full_override_sym(method, invoker);
+    char *content;
+
+    //if (CFCMethod_can_be_bound(method)) {
+    if (false) {
+    }
+    else {
+        char *unused = S_build_unused_vars(vars);
+        char *unreachable = S_maybe_unreachable(return_type);
+        char *meth_sym = CFCMethod_full_method_sym(method, invoker);
+        const char pattern[] =
+            "%s\n"
+            "%s(%s) {%s\n"
+            "    CFISH_THROW(CFISH_ERR, \"Can't override %s via binding\");%s\n"
+            "}\n";
+        content = CFCUtil_sprintf(pattern, ret_type_str, override_sym,
+                                  params, unused, meth_sym, unreachable);
+        FREEMEM(meth_sym);
+        FREEMEM(unused);
+        FREEMEM(unreachable);
+    }
+
+    FREEMEM(override_sym);
+    return content;
 }
+
+static char*
+S_build_unused_vars(CFCVariable **vars) {
+    char *unused = CFCUtil_strdup("");
+
+    for (int i = 0; vars[i] != NULL; i++) {
+        const char *var_name = CFCVariable_get_name(vars[i]);
+        size_t size = strlen(unused) + strlen(var_name) + 80;
+        unused = (char*)REALLOCATE(unused, size);
+        strcat(unused, "\n    CFISH_UNUSED_VAR(");
+        strcat(unused, var_name);
+        strcat(unused, ");");
+    }
+
+    return unused;
+}
+
+static char*
+S_maybe_unreachable(CFCType *return_type) {
+    char *return_statement;
+    if (CFCType_is_void(return_type)) {
+        return_statement = CFCUtil_strdup("");
+    }
+    else {
+        const char *ret_type_str = CFCType_to_c(return_type);
+        char pattern[] = "\n    CFISH_UNREACHABLE_RETURN(%s);";
+        return_statement = CFCUtil_sprintf(pattern, ret_type_str);
+    }
+    return return_statement;
+}
+


[07/20] lucy-clownfish git commit: Conversion routines from Python to Clownfish.

Posted by ma...@apache.org.
Conversion routines from Python to Clownfish.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/99d6bc0f
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/99d6bc0f
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/99d6bc0f

Branch: refs/heads/master
Commit: 99d6bc0fe89382c8492d97825fc03ca82eac051f
Parents: 999b39f
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Wed Jan 27 15:35:08 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:03 2016 -0800

----------------------------------------------------------------------
 runtime/python/cfext/CFBind.c | 196 +++++++++++++++++++++++++++++++++++++
 runtime/python/cfext/CFBind.h |  34 +++++++
 2 files changed, 230 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/99d6bc0f/runtime/python/cfext/CFBind.c
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.c b/runtime/python/cfext/CFBind.c
index d3c39c2..501c342 100644
--- a/runtime/python/cfext/CFBind.c
+++ b/runtime/python/cfext/CFBind.c
@@ -37,6 +37,7 @@
 #include "Clownfish/String.h"
 #include "Clownfish/TestHarness/TestUtils.h"
 #include "Clownfish/Util/Memory.h"
+#include "Clownfish/Util/StringHelper.h"
 #include "Clownfish/Vector.h"
 
 static bool Err_initialized;
@@ -85,6 +86,201 @@ CFBind_reraise_pyerr(cfish_Class *err_klass, cfish_String *mess) {
     cfish_Err_throw_mess(err_klass, new_mess);
 }
 
+static cfish_Vector*
+S_py_list_to_vector(PyObject *list) {
+    Py_ssize_t size = PyList_GET_SIZE(list);
+    cfish_Vector *vec = cfish_Vec_new(size);
+    for (Py_ssize_t i = 0; i < size; i++) {
+        CFISH_Vec_Store(vec, i, CFBind_py_to_cfish(PyList_GET_ITEM(list, i), NULL));
+    }
+    return vec;
+}
+
+static cfish_Hash*
+S_py_dict_to_hash(PyObject *dict) {
+    Py_ssize_t pos = 0;
+    PyObject *key, *value;
+    cfish_Hash *hash = cfish_Hash_new(PyDict_Size(dict));
+    while (PyDict_Next(dict, &pos, &key, &value)) {
+        char *ptr;
+        Py_ssize_t size;
+        PyObject *stringified = key;
+        if (!PyUnicode_CheckExact(key)) {
+            stringified = PyObject_Str(key);
+        }
+        ptr = PyUnicode_AsUTF8AndSize(stringified, &size);
+        if (!ptr) {
+            cfish_String *mess
+                = CFISH_MAKE_MESS("Failed to stringify as UTF-8");
+            CFBind_reraise_pyerr(CFISH_ERR, mess);
+        }
+        CFISH_Hash_Store_Utf8(hash, ptr, size, CFBind_py_to_cfish(value, NULL));
+        if (stringified != key) {
+            Py_DECREF(stringified);
+        }
+    }
+    return hash;
+}
+
+static cfish_Obj*
+S_maybe_increment(void *vobj, bool increment) {
+    if (increment) {
+        return CFISH_INCREF((cfish_Obj*)vobj);
+    }
+    return (cfish_Obj*)vobj;
+}
+
+static bool
+S_maybe_py_to_cfish(PyObject *py_obj, cfish_Class *klass, bool increment,
+                    bool nullable, void *allocation, cfish_Obj **obj_ptr) {
+    CFISH_UNUSED_VAR(allocation); // FIXME implement stack strings
+
+    if (!py_obj || py_obj == Py_None) {
+        *obj_ptr = NULL;
+        return nullable;
+    }
+
+	// Default to accepting any type.
+	if (klass == NULL) {
+		klass = CFISH_OBJ;
+	}
+
+    if (S_py_obj_is_a(py_obj, klass)) {
+        *obj_ptr = S_maybe_increment(py_obj, increment);
+        return true;
+    }
+    else if (py_obj == Py_True) {
+        if (klass != CFISH_BOOLEAN && klass != CFISH_OBJ) {
+            return false;
+        }
+        *obj_ptr = S_maybe_increment(CFISH_TRUE, increment);
+        return true;
+    }
+    else if (py_obj == Py_False) {
+        if (klass != CFISH_BOOLEAN && klass != CFISH_OBJ) {
+            return false;
+        }
+        *obj_ptr = S_maybe_increment(CFISH_FALSE, increment);
+        return true;
+    }
+    else if (klass == CFISH_BOOLEAN) {
+        int truthiness = PyObject_IsTrue(py_obj);
+        if (truthiness == 1) {
+            *obj_ptr = S_maybe_increment(CFISH_TRUE, increment);
+        }
+        else if (truthiness == 0) {
+            *obj_ptr = S_maybe_increment(CFISH_FALSE, increment);
+        }
+        else {
+            return false;
+        }
+        return true;
+    }
+
+    // From here on out, we're going to return a new Clownfish object.  The
+    // caller has to take ownership of a refcount; if they don't want to, then
+    // fail rather than attempt to return an object with a refcount of 0.
+    if (!increment) {
+        return false;
+    }
+
+    if (PyUnicode_CheckExact(py_obj)) {
+        if (klass != CFISH_STRING && klass != CFISH_OBJ) {
+            return false;
+        }
+        // TODO: Allow Clownfish String to wrap buffer of Python str?
+        Py_ssize_t size;
+        char *ptr = PyUnicode_AsUTF8AndSize(py_obj, &size);
+        // TODO: Can we guarantee that Python will always supply valid UTF-8?
+        if (!ptr || !cfish_StrHelp_utf8_valid(ptr, size)) {
+            return false;
+        }
+        *obj_ptr = (cfish_Obj*)cfish_Str_new_from_trusted_utf8(ptr, size);
+        return true;
+    }
+    else if (PyBytes_CheckExact(py_obj)) {
+        if (klass != CFISH_BLOB && klass != CFISH_OBJ) {
+            return false;
+        }
+        char *ptr = PyBytes_AS_STRING(py_obj);
+        Py_ssize_t size = PyBytes_GET_SIZE(py_obj);
+        *obj_ptr = (cfish_Obj*)cfish_BB_new_bytes(ptr, size);
+        return true;
+    }
+    else if (PyList_CheckExact(py_obj)) {
+        if (klass != CFISH_VECTOR && klass != CFISH_OBJ) {
+            return false;
+        }
+        *obj_ptr = (cfish_Obj*)S_py_list_to_vector(py_obj);
+        return true;
+    }
+    else if (PyDict_CheckExact(py_obj)) {
+        if (klass != CFISH_HASH && klass != CFISH_OBJ) {
+            return false;
+        }
+        *obj_ptr = (cfish_Obj*)S_py_dict_to_hash(py_obj);
+        return true;
+    }
+    else if (PyLong_CheckExact(py_obj)) {
+        if (klass != CFISH_INTEGER && klass != CFISH_OBJ) {
+            return false;
+        }
+        // Raises ValueError on overflow.
+        int64_t value = PyLong_AsLongLong(py_obj);
+        if (PyErr_Occurred()) {
+            return false;
+        }
+        *obj_ptr = (cfish_Obj*)cfish_Int_new(value);
+        return true;
+    }
+    else if (PyFloat_CheckExact(py_obj)) {
+        if (klass != CFISH_FLOAT && klass != CFISH_OBJ) {
+            return false;
+        }
+        double value = PyFloat_AsDouble(py_obj);
+        if (PyErr_Occurred()) {
+            return false;
+        }
+        *obj_ptr = (cfish_Obj*)cfish_Float_new(value);
+        return true;
+    }
+
+    // The value did not meet the required spec, so return false to indicate
+    // failure.
+    return false;
+}
+
+cfish_Obj*
+CFBind_py_to_cfish_nullable(PyObject *py_obj, cfish_Class *klass) {
+    cfish_Obj *retval;
+    bool success = S_maybe_py_to_cfish(py_obj, klass, true, true, NULL, &retval);
+    if (!success) {
+        CFISH_THROW(CFISH_ERR, "Can't convert to %o", klass);
+    }
+    return retval;
+}
+
+cfish_Obj*
+CFBind_py_to_cfish(PyObject *py_obj, cfish_Class *klass) {
+    cfish_Obj *retval;
+    bool success = S_maybe_py_to_cfish(py_obj, klass, true, false, NULL, &retval);
+    if (!success) {
+        CFISH_THROW(CFISH_ERR, "Can't convert to %o", klass);
+    }
+    return retval;
+}
+
+cfish_Obj*
+CFBind_py_to_cfish_noinc(PyObject *py_obj, cfish_Class *klass,
+                         void *allocation) {
+    cfish_Obj *retval;
+    bool success = S_maybe_py_to_cfish(py_obj, klass, false, false, allocation, &retval);
+    if (!success) {
+        CFISH_THROW(CFISH_ERR, "Can't convert to %o", klass);
+    }
+    return retval;
+}
+
 static int
 S_convert_sint(PyObject *py_obj, void *ptr, bool nullable, unsigned width) {
     if (py_obj == Py_None) {

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/99d6bc0f/runtime/python/cfext/CFBind.h
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.h b/runtime/python/cfext/CFBind.h
index 6637fca..e4e0f23 100644
--- a/runtime/python/cfext/CFBind.h
+++ b/runtime/python/cfext/CFBind.h
@@ -66,6 +66,40 @@ CFBind_cfish_to_py_zeroref(struct cfish_Obj *obj) {
     }
 }
 
+/** Perform recursive conversion of Python objects to Clownfish, return an
+  * incremented Clownfish Obj.
+  *
+  *     string -> String
+  *     bytes  -> Blob
+  *     None   -> NULL
+  *     bool   -> Boolean
+  *     int    -> Integer
+  *     float  -> Float
+  *     list   -> Vector
+  *     dict   -> Hash
+  *
+  * Python dict keys will be stringified.  Other Clownfish objects will be
+  * left intact.  Anything else will be stringified.
+  */
+cfish_Obj*
+CFBind_py_to_cfish(PyObject *py_obj, cfish_Class *klass);
+
+/** As CFBind_py_to_cfish above, but returns NULL if the PyObject is None
+  * or NULL.
+  */
+cfish_Obj*
+CFBind_py_to_cfish_nullable(PyObject *py_obj, cfish_Class *klass);
+
+/** As CFBind_py_to_cfish above, but returns an object that can be used for a
+  * while with no need to decref.
+  *
+  * If `klass` is STRING or OBJ, `allocation` must point to stack-allocated
+  * memory that can hold a String. Otherwise, `allocation` should be NULL.
+  */
+cfish_Obj*
+CFBind_py_to_cfish_noinc(PyObject *py_obj, cfish_Class *klass,
+                         void *allocation);
+
 /* ParseTuple conversion routines for primitive numeric types.
  *
  * If the value of `input` is out of range for the an integer C type, an


[04/20] lucy-clownfish git commit: Add ParseTuple-compatible converters for numerics.

Posted by ma...@apache.org.
Add ParseTuple-compatible converters for numerics.

Add some conversion functions which conform to the API specified by
Python's ParseTuple C functionality.


Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/991e4a57
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/991e4a57
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/991e4a57

Branch: refs/heads/master
Commit: 991e4a576ab7edfd96e285c8493c3bdbd1794d6d
Parents: 9162199
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Fri Jan 22 17:14:30 2016 -0800
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Tue Feb 23 18:22:02 2016 -0800

----------------------------------------------------------------------
 runtime/python/cfext/CFBind.c | 305 +++++++++++++++++++++++++++++++++++++
 runtime/python/cfext/CFBind.h |  73 +++++++++
 2 files changed, 378 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/991e4a57/runtime/python/cfext/CFBind.c
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.c b/runtime/python/cfext/CFBind.c
index 95ae70d..7653325 100644
--- a/runtime/python/cfext/CFBind.c
+++ b/runtime/python/cfext/CFBind.c
@@ -76,6 +76,311 @@ CFBind_reraise_pyerr(cfish_Class *err_klass, cfish_String *mess) {
     cfish_Err_throw_mess(err_klass, new_mess);
 }
 
+static int
+S_convert_sint(PyObject *py_obj, void *ptr, bool nullable, unsigned width) {
+    if (py_obj == Py_None) {
+        if (nullable) {
+            return 1;
+        }
+        else {
+            PyErr_SetString(PyExc_TypeError, "Required argument cannot be None");
+            return 0;
+        }
+    }
+    int overflow = 0;
+    int64_t value = PyLong_AsLongLongAndOverflow(py_obj, &overflow);
+    if (value == -1 && PyErr_Occurred()) {
+        return 0;
+    }
+    switch (width & 0xF) {
+        case 1:
+            if (value < INT8_MIN  || value > INT8_MAX)  { overflow = 1; }
+            break;
+        case 2:
+            if (value < INT16_MIN || value > INT16_MAX) { overflow = 1; }
+            break;
+        case 4:
+            if (value < INT32_MIN || value > INT32_MAX) { overflow = 1; }
+            break;
+        case 8:
+            break;
+    }
+    if (overflow) {
+        PyErr_SetString(PyExc_OverflowError, "Python int out of range");
+        return 0;
+    }
+    switch (width & 0xF) {
+        case 1:
+            *((int8_t*)ptr) = (int8_t)value;
+            break;
+        case 2:
+            *((int16_t*)ptr) = (int16_t)value;
+            break;
+        case 4:
+            *((int32_t*)ptr) = (int32_t)value;
+            break;
+        case 8:
+            *((int64_t*)ptr) = value;
+            break;
+    }
+    return 1;
+}
+
+static int
+S_convert_uint(PyObject *py_obj, void *ptr, bool nullable, unsigned width) {
+    if (py_obj == Py_None) {
+        if (nullable) {
+            return 1;
+        }
+        else {
+            PyErr_SetString(PyExc_TypeError, "Required argument cannot be None");
+            return 0;
+        }
+    }
+    uint64_t value = PyLong_AsUnsignedLongLong(py_obj);
+    if (PyErr_Occurred()) {
+        return 0;
+    }
+    int overflow = 0;
+    switch (width & 0xF) {
+        case 1:
+            if (value > UINT8_MAX)  { overflow = 1; }
+            break;
+        case 2:
+            if (value > UINT16_MAX) { overflow = 1; }
+            break;
+        case 4:
+            if (value > UINT32_MAX) { overflow = 1; }
+            break;
+        case 8:
+            break;
+    }
+    if (overflow) {
+        PyErr_SetString(PyExc_OverflowError, "Python int out of range");
+        return 0;
+    }
+    switch (width & 0xF) {
+        case 1:
+            *((uint8_t*)ptr) = (uint8_t)value;
+            break;
+        case 2:
+            *((uint16_t*)ptr) = (uint16_t)value;
+            break;
+        case 4:
+            *((uint32_t*)ptr) = (uint32_t)value;
+            break;
+        case 8:
+            *((uint64_t*)ptr) = value;
+            break;
+    }
+    return 1;
+}
+
+int
+CFBind_convert_char(PyObject *py_obj, char *ptr) {
+    return S_convert_sint(py_obj, ptr, false, sizeof(char));
+}
+
+int
+CFBind_convert_short(PyObject *py_obj, short *ptr) {
+    return S_convert_sint(py_obj, ptr, false, sizeof(short));
+}
+
+int
+CFBind_convert_int(PyObject *py_obj, int *ptr) {
+    return S_convert_sint(py_obj, ptr, false, sizeof(int));
+}
+
+int
+CFBind_convert_long(PyObject *py_obj, long *ptr) {
+    return S_convert_sint(py_obj, ptr, false, sizeof(long));
+}
+
+int
+CFBind_convert_int8_t(PyObject *py_obj, int8_t *ptr) {
+    return S_convert_sint(py_obj, ptr, false, sizeof(int8_t));
+}
+
+int
+CFBind_convert_int16_t(PyObject *py_obj, int16_t *ptr) {
+    return S_convert_sint(py_obj, ptr, false, sizeof(int16_t));
+}
+
+int
+CFBind_convert_int32_t(PyObject *py_obj, int32_t *ptr) {
+    return S_convert_sint(py_obj, ptr, false, sizeof(int32_t));
+}
+
+int
+CFBind_convert_int64_t(PyObject *py_obj, int64_t *ptr) {
+    return S_convert_sint(py_obj, ptr, false, sizeof(int64_t));
+}
+
+int
+CFBind_convert_uint8_t(PyObject *py_obj, uint8_t *ptr) {
+    return S_convert_uint(py_obj, ptr, false, sizeof(uint8_t));
+}
+
+int
+CFBind_convert_uint16_t(PyObject *py_obj, uint16_t *ptr) {
+    return S_convert_uint(py_obj, ptr, false, sizeof(uint16_t));
+}
+
+int
+CFBind_convert_uint32_t(PyObject *py_obj, uint32_t *ptr) {
+    return S_convert_uint(py_obj, ptr, false, sizeof(uint32_t));
+}
+
+int
+CFBind_convert_uint64_t(PyObject *py_obj, uint64_t *ptr) {
+    return S_convert_uint(py_obj, ptr, false, sizeof(uint64_t));
+}
+
+int
+CFBind_convert_size_t(PyObject *py_obj, size_t *ptr) {
+    return S_convert_uint(py_obj, ptr, false, sizeof(size_t));
+}
+
+int
+CFBind_maybe_convert_char(PyObject *py_obj, char *ptr) {
+    return S_convert_sint(py_obj, ptr, true, sizeof(char));
+}
+
+int
+CFBind_maybe_convert_short(PyObject *py_obj, short *ptr) {
+    return S_convert_sint(py_obj, ptr, true, sizeof(short));
+}
+
+int
+CFBind_maybe_convert_int(PyObject *py_obj, int *ptr) {
+    return S_convert_sint(py_obj, ptr, true, sizeof(int));
+}
+
+int
+CFBind_maybe_convert_long(PyObject *py_obj, long *ptr) {
+    return S_convert_sint(py_obj, ptr, true, sizeof(long));
+}
+
+int
+CFBind_maybe_convert_int8_t(PyObject *py_obj, int8_t *ptr) {
+    return S_convert_sint(py_obj, ptr, true, sizeof(int8_t));
+}
+
+int
+CFBind_maybe_convert_int16_t(PyObject *py_obj, int16_t *ptr) {
+    return S_convert_sint(py_obj, ptr, true, sizeof(int16_t));
+}
+
+int
+CFBind_maybe_convert_int32_t(PyObject *py_obj, int32_t *ptr) {
+    return S_convert_sint(py_obj, ptr, true, sizeof(int32_t));
+}
+
+int
+CFBind_maybe_convert_int64_t(PyObject *py_obj, int64_t *ptr) {
+    return S_convert_sint(py_obj, ptr, true, sizeof(int64_t));
+}
+
+int
+CFBind_maybe_convert_uint8_t(PyObject *py_obj, uint8_t *ptr) {
+    return S_convert_uint(py_obj, ptr, true, sizeof(uint8_t));
+}
+
+int
+CFBind_maybe_convert_uint16_t(PyObject *py_obj, uint16_t *ptr) {
+    return S_convert_uint(py_obj, ptr, true, sizeof(uint16_t));
+}
+
+int
+CFBind_maybe_convert_uint32_t(PyObject *py_obj, uint32_t *ptr) {
+    return S_convert_uint(py_obj, ptr, true, sizeof(uint32_t));
+}
+
+int
+CFBind_maybe_convert_uint64_t(PyObject *py_obj, uint64_t *ptr) {
+    return S_convert_uint(py_obj, ptr, true, sizeof(uint64_t));
+}
+
+int
+CFBind_maybe_convert_size_t(PyObject *py_obj, size_t *ptr) {
+    return S_convert_uint(py_obj, ptr, true, sizeof(size_t));
+}
+
+static int
+S_convert_floating(PyObject *py_obj, void *ptr, bool nullable, int width) {
+    if (py_obj == Py_None) {
+        if (nullable) {
+            return 1;
+        }
+        else {
+            PyErr_SetString(PyExc_TypeError, "Required argument cannot be None");
+            return 0;
+        }
+    }
+    double value = PyFloat_AsDouble(py_obj);
+    if (PyErr_Occurred()) {
+        return 0;
+    }
+    switch (width & 0xF) {
+        case sizeof(float):
+            *((float*)ptr) = (float)value;
+            break;
+        case sizeof(double):
+            *((double*)ptr) = value;
+            break;
+    }
+    return 1;
+}
+
+int
+CFBind_convert_float(PyObject *py_obj, float *ptr) {
+    return S_convert_floating(py_obj, ptr, false, sizeof(float));
+}
+
+int
+CFBind_convert_double(PyObject *py_obj, double *ptr) {
+    return S_convert_floating(py_obj, ptr, false, sizeof(double));
+}
+
+int
+CFBind_maybe_convert_float(PyObject *py_obj, float *ptr) {
+    return S_convert_floating(py_obj, ptr, true, sizeof(float));
+}
+
+int
+CFBind_maybe_convert_double(PyObject *py_obj, double *ptr) {
+    return S_convert_floating(py_obj, ptr, true, sizeof(double));
+}
+
+static int
+S_convert_bool(PyObject *py_obj, bool *ptr, bool nullable) {
+    if (py_obj == Py_None) {
+        if (nullable) {
+            return 1;
+        }
+        else {
+            PyErr_SetString(PyExc_TypeError, "Required argument cannot be None");
+            return 0;
+        }
+    }
+    int truth = PyObject_IsTrue(py_obj);
+    if (truth == -1) {
+        return 0;
+    }
+    *ptr = !!truth;
+    return 1;
+}
+
+int
+CFBind_convert_bool(PyObject *py_obj, bool *ptr) {
+    return S_convert_bool(py_obj, ptr, false);
+}
+
+int
+CFBind_maybe_convert_bool(PyObject *py_obj, bool *ptr) {
+    return S_convert_bool(py_obj, ptr, true);
+}
+
 /**** refcounting **********************************************************/
 
 uint32_t

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/991e4a57/runtime/python/cfext/CFBind.h
----------------------------------------------------------------------
diff --git a/runtime/python/cfext/CFBind.h b/runtime/python/cfext/CFBind.h
index d6a362e..85be7af 100644
--- a/runtime/python/cfext/CFBind.h
+++ b/runtime/python/cfext/CFBind.h
@@ -37,6 +37,79 @@ struct cfish_String;
 void
 CFBind_reraise_pyerr(struct cfish_Class *err_klass, struct cfish_String *mess);
 
+/* ParseTuple conversion routines for primitive numeric types.
+ *
+ * If the value of `input` is out of range for the an integer C type, an
+ * OverflowError will be raised.
+ *
+ * If `input` is `None`, the "maybe_convert" variants will leave `ptr`
+ * untouched, while the "convert" routines will raise a TypeError.
+ */
+int
+CFBind_convert_char(PyObject *input, char *ptr);
+int
+CFBind_convert_short(PyObject *input, short *ptr);
+int
+CFBind_convert_int(PyObject *input, int *ptr);
+int
+CFBind_convert_long(PyObject *input, long *ptr);
+int
+CFBind_convert_int8_t(PyObject *input, int8_t *ptr);
+int
+CFBind_convert_int16_t(PyObject *input, int16_t *ptr);
+int
+CFBind_convert_int32_t(PyObject *input, int32_t *ptr);
+int
+CFBind_convert_int64_t(PyObject *input, int64_t *ptr);
+int
+CFBind_convert_uint8_t(PyObject *input, uint8_t *ptr);
+int
+CFBind_convert_uint16_t(PyObject *input, uint16_t *ptr);
+int
+CFBind_convert_uint32_t(PyObject *input, uint32_t *ptr);
+int
+CFBind_convert_uint64_t(PyObject *input, uint64_t *ptr);
+int
+CFBind_convert_bool(PyObject *input, bool *ptr);
+int
+CFBind_convert_size_t(PyObject *input, size_t *ptr);
+int
+CFBind_convert_float(PyObject *input, float *ptr);
+int
+CFBind_convert_double(PyObject *input, double *ptr);
+int
+CFBind_maybe_convert_char(PyObject *input, char *ptr);
+int
+CFBind_maybe_convert_short(PyObject *input, short *ptr);
+int
+CFBind_maybe_convert_int(PyObject *input, int *ptr);
+int
+CFBind_maybe_convert_long(PyObject *input, long *ptr);
+int
+CFBind_maybe_convert_int8_t(PyObject *input, int8_t *ptr);
+int
+CFBind_maybe_convert_int16_t(PyObject *input, int16_t *ptr);
+int
+CFBind_maybe_convert_int32_t(PyObject *input, int32_t *ptr);
+int
+CFBind_maybe_convert_int64_t(PyObject *input, int64_t *ptr);
+int
+CFBind_maybe_convert_uint8_t(PyObject *input, uint8_t *ptr);
+int
+CFBind_maybe_convert_uint16_t(PyObject *input, uint16_t *ptr);
+int
+CFBind_maybe_convert_uint32_t(PyObject *input, uint32_t *ptr);
+int
+CFBind_maybe_convert_uint64_t(PyObject *input, uint64_t *ptr);
+int
+CFBind_maybe_convert_bool(PyObject *input, bool *ptr);
+int
+CFBind_maybe_convert_size_t(PyObject *input, size_t *ptr);
+int
+CFBind_maybe_convert_float(PyObject *input, float *ptr);
+int
+CFBind_maybe_convert_double(PyObject *input, double *ptr);
+
 #ifdef __cplusplus
 }
 #endif