diff options
Diffstat (limited to 'guiletopy.c')
-rw-r--r-- | guiletopy.c | 1141 |
1 files changed, 1141 insertions, 0 deletions
diff --git a/guiletopy.c b/guiletopy.c new file mode 100644 index 0000000..967dffc --- /dev/null +++ b/guiletopy.c @@ -0,0 +1,1141 @@ +// guiletopy functions +// Functions for conversion from Guile SCMs into PyObjects. +// +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2008 Omer Zak. +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library, in a file named COPYING; if not, write to the +// Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// +// For licensing issues, contact <w1@zak.co.il>. +// +//////////////////////////////////////////////////////////////////////// +//#include <Python.h> // included in guiletopy.h +//#include <libguile.h> // included in guiletopy.h +#include "guiletopy.h" +#include "pysmob.h" +#include <guile/gh.h> +#include "g2p2g_smob.h" // used in guiletopy.inc +#include "verbose.h" +#include "pyscm.h" + +//////////////////////////////////////////////////////////////////////// +// +// VERBOSITY HANDLING CONVENTIONS: +// +// PYGUILE_VERBOSE_G2P2G_SUCCESSFUL is tested whenever conversion in +// a primitive data type conversion function (g2p*) is successful. +// This condition is intended to capture all successful conversions +// of primitive values. +// +// PYGUILE_VERBOSE_G2P2G_ALWAYS is tested in all conversion failures +// and also when successfully converting aggregate data structures +// (such as Tuples, Lists and Dicts). It is intended to provide +// detailed trace of operation of the conversion functions. +// +//////////////////////////////////////////////////////////////////////// +// Convert data from Guile (SCM) representation into Python +// representation +//////////////////////////////////////////////////////////////////////// + +// The following functions convert each a single data type. +// They have to be efficient. +// The interface conventions are: +// 1. The function gets a single SCM argument, and upon +// success - returns a single PyObject, which has already +// been Py_INCREF()-ed. +// 2. The function is responsible for checking the data type of +// and whether the value of its argument is in range. +// If any of them fails, the function returns NULL. +// NOTE: no error is raised with the NULL return (unlike the +// usual convention in Python code). +// 3. If there is any error not associated with wrong data type +// of its argument, the function throws a scm exception. +// 4. Naming convention: +// g2p_{Guile datatype name}2{Python datatype name} +// The reason for (2),(3) above is that those functions are +// intended to be called one after one, until one of them +// succeeds in converting a data item. + +//////////////////////// general template handling ///////////////////// + +// Apply a template to sobj. +// The template consists of pair of g2p* token and data structure +// which serves as the stemplate argument when the token's function +// is invoked. +// Alternatively, the template may consist of a single g2p* token. +// In that case, the corresponding function gets SCM_UNSPECIFIED +// as its stemplate argument. +// The invoked function is responsible for ensuring that the stemplate +// which it received is appropriate to sobj. Inappropriateness +// means that NULL is returned, which allows g2p_leaf (see below) +// to backtrace and try another template. +PyObject * +g2p_apply(SCM sobj,SCM stemplate) +{ + PyObject *pobj; + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# Entered g2p_apply: sobj=~S stemplate=~S\n"),scm_list_2(sobj,stemplate)); + } + if (IS_G2P_SMOBP(stemplate)) { + pobj = (get_g2p_function(stemplate))(sobj,SCM_UNSPECIFIED); + } + else if (!SCM_EQ_P(SCM_BOOL_F,scm_pair_p(stemplate))) { + if (IS_G2P_SMOBP(SCM_CAR(stemplate))) { + pobj = (get_g2p_function(SCM_CAR(stemplate)))(sobj,SCM_CDR(stemplate)); + } + else { + scm_misc_error("g2p_apply","bad template CAR item ~S", + scm_list_1(SCM_CAR(stemplate))); + } + } + else { + scm_misc_error("g2p_apply","bad template item ~S", + scm_list_1(stemplate)); + } + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(), + scm_makfrom0str("# Leaving g2p_apply: with ~A result\n"), + scm_list_1(NULL == pobj ? scm_makfrom0str("null") + : scm_makfrom0str("non-null"))); + } + return(pobj); // is NULL if conversion failed, otherwise a new + // reference to a PyObject (Py_INCREF has been invoked on it). +} + +////////////////////////// leaf //////////////////////////////////////// + +// Perform 'leaf' data conversion. +// Normally, stemplate is a list of templates, to be tried one by one +// until one of them succeeds. +PyObject * +g2p_leaf(SCM sobj,SCM stemplate) +{ + PyObject *pobj; + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# Entered g2p_leaf: sobj=~S stemplate=~S\n"),scm_list_2(sobj,stemplate)); + } + if (IS_G2P_SMOBP(stemplate)) { + pobj = (get_g2p_function(stemplate))(sobj,SCM_UNSPECIFIED); + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(), + scm_makfrom0str("# Leaving g2p_leaf, after G2P_SMOBP conversion, with ~A result\n"), + scm_list_1(NULL == pobj ? scm_makfrom0str("null") + : scm_makfrom0str("non-null"))); + } + return(pobj); // Will be NULL if the conversion failed. + } + if (!SCM_EQ_P(SCM_BOOL_F,scm_list_p(stemplate))) { + // Each template list item is examined. + // If the template list item is itself a list, then its CAR is invoked + // and gets its CDR as template, and the whole sobj (which is + // expected to be a list, too) as the argument. + // If the template list item is a G2P_SMOB, then it is invoked with + // sobj. + // + // At any case, template list items are invoked one by one until + // one of them succeeds. + SCM slist; + for (slist = stemplate; (!SCM_EQ_P(slist,SCM_EOL)); + slist = SCM_CDR(slist)) { + SCM scandidate = SCM_CAR(slist); + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_leaf: trying another stemplate ~S on sobj\n"),scm_list_1(scandidate)); + } + pobj = g2p_apply(sobj,scandidate); + if (NULL != pobj) { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_leaf: successful conversion\n"),SCM_EOL); + } + return(pobj); + } + } + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_leaf: unsuccessful conversion, no stemplate fit the sobj\n"),SCM_EOL); + } + return(NULL); // None of the templates in the list fit the sobj. + // NULL return supports backtracking if the template is so designed. + } + scm_wrong_type_arg("g2p_leaf",SCM_ARG2,stemplate); // Bad template +} + +////////////////////////// null //////////////////////////////////////// + +PyObject * +g2p_null2PyNone(SCM sobj,SCM stemplate) +{ + if (SCM_NULLP(sobj)) { //(SCM_BOOL_F != scm_null_p(sobj)) + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2PyNone: successful conversion of ~S into Python None\n"),scm_list_1(sobj)); + } + Py_INCREF(Py_None); + return(Py_None); + } + else { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2PyNone: unsuccessful conversion: ~S is not null\n"),scm_list_1(sobj)); + } + return(NULL); + } +} + +PyObject * +g2p_null2Tuple0(SCM sobj,SCM stemplate) +{ + if (SCM_NULLP(sobj)) { //(SCM_BOOL_F != scm_null_p(sobj)) + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2Tuple0: successful conversion of ~S into Python ()\n"),scm_list_1(sobj)); + } + PyObject *pres = PyTuple_New(0); + if (NULL == pres) { + scm_memory_error("g2p-null2Tuple0"); // NOT COVERED BY TESTS + } + return(pres); + } + else { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2Tuple0: unsuccessful conversion: ~S is not null\n"),scm_list_1(sobj)); + } + return(NULL); + } +} + +PyObject * +g2p_null2List0(SCM sobj,SCM stemplate) +{ + if (SCM_NULLP(sobj)) { //(SCM_BOOL_F != scm_null_p(sobj)) + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2List0: successful conversion of ~S into Python []\n"),scm_list_1(sobj)); + } + PyObject *pres = PyList_New(0); + if (NULL == pres) { + scm_memory_error("g2p-null2List0"); // NOT COVERED BY TESTS + } + return(pres); + } + else { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2List0: unsuccessful conversion: ~S is not null\n"),scm_list_1(sobj)); + } + return(NULL); + } +} + +PyObject * +g2p_null2DictEmpty(SCM sobj,SCM stemplate) +{ + if (SCM_NULLP(sobj)) { //(SCM_BOOL_F != scm_null_p(sobj)) + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2DictEmpty: successful conversion of ~S into Python {}\n"),scm_list_1(sobj)); + } + PyObject *pres = PyDict_New(); + if (NULL == pres) { + scm_memory_error("g2p-null2DictEmpty"); // NOT COVERED BY TESTS + } + return(pres); + } + else { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2DictEmpty: unsuccessful conversion: ~S is not null\n"),scm_list_1(sobj)); + } + return(NULL); + } +} + +// Python's None is different from Python's () or Python's [] +// !!! Also, check for token such as '*none* or '*None* for +// !!! conversion into Python None instead of Python string. + +///////////////////////// Numeric ////////////////////////////////////// + +PyObject * +g2p_bool2Bool(SCM sobj,SCM stemplate) +{ + if (SCM_BOOLP(sobj)) { //(SCM_BOOL_F != scm_boolean_p(sobj)) + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_bool2Bool: successful conversion of ~S into a Python Bool value\n"),scm_list_1(sobj)); + } + if (SCM_EQ_P(SCM_BOOL_T,sobj)) { + Py_INCREF(Py_True); + return(Py_True); + } + else { + Py_INCREF(Py_False); + return(Py_False); + } + } + else { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_bool2Bool: unsuccessful conversion: ~S is not a bool value\n"),scm_list_1(sobj)); + } + return(NULL); + } +} + +PyObject * +g2p_num2Int(SCM sobj,SCM stemplate) +{ + if (SCM_INUMP(sobj)) { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_num2Int: successful conversion of ~S into a Python Int value\n"),scm_list_1(sobj)); + } + return(PyInt_FromLong(scm_num2long(sobj,0,"g2p_long2Int"))); + } + else { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_num2Int: unsuccessful conversion: ~S is not a num value\n"),scm_list_1(sobj)); + } + return(NULL); + } +} + +PyObject * +g2p_real2Float(SCM sobj,SCM stemplate) +{ + if (SCM_INUMP(sobj) || SCM_REALP(sobj)) { //(SCM_BOOL_F != scm_real_p(sobj)) + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_real2Float: successful conversion of ~S into a Python Float value\n"),scm_list_1(sobj)); + } + return(PyFloat_FromDouble(scm_num2double(sobj,0,"g2p_real2Float"))); + } + else { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_real2Float: unsuccessful conversion: ~S is not a real value\n"),scm_list_1(sobj)); + } + return(NULL); + } +} + +PyObject * +g2p_complex2Complex(SCM sobj,SCM stemplate) +{ + if (SCM_COMPLEXP(sobj)) { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_complex2Complex: successful conversion of ~S into a Python Complex value\n"),scm_list_1(sobj)); + } + double re = SCM_COMPLEX_REAL(sobj); + double im = SCM_COMPLEX_IMAG(sobj); + return(PyComplex_FromDoubles(re,im)); + } + else { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_complex2Complex: unsuccessful conversion: ~S is not a complex value\n"),scm_list_1(sobj)); + } + return(NULL); + } +} + +PyObject * +g2p_bignum2Long(SCM sobj,SCM stemplate) +{ + // Like schemepy, we accomplish this conversion by first + // converting into string and then evaluating the string. + if (SCM_BIGP(sobj)) { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_bignum2Long: successful conversion of ~S into a Python Long value\n"),scm_list_1(sobj)); + } + SCM swrite_proc = scm_variable_ref(scm_c_lookup("write")); + SCM sbignumstr = scm_object_to_string(sobj,swrite_proc); + char *pstr = gh_scm2newstr(sbignumstr,NULL); + if (NULL == pstr) { + scm_memory_error("g2p_bignum2Long"); // NOT COVERED BY TESTS + } + + PyObject *pres = PyInt_FromString(pstr, NULL, 10); // Will return PyLong if the value does not fit into PyInt. + free(pstr); + PyObject *pexception = PyErr_Occurred(); + if (pexception) { + Py_XDECREF(pres); // NOT COVERED BY TESTS + PyErr_Clear(); // NOT COVERED BY TESTS + scm_misc_error("g2p_bignum2Long","internal conversion error of bignum ~S", // NOT COVERED BY TESTS + sobj); + } + return(pres); + } + else { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_bignum2Long: unsuccessful conversion: ~S is not a bignum value\n"),scm_list_1(sobj)); + } + return(NULL); + } +} + +///////////////////// Pairs and Lists ////////////////////////////////// + +PyObject * +g2p_pair2Tuple(SCM sobj,SCM stemplate) +{ + // We expect the template to be a pair. + // SCM_CAR(stemplate) is used to convert SCM_CAR(sobj), and similarly + // for SCM_CDR. + if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(sobj))) { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2Tuple: unsuccessful conversion: ~S is not a pair\n"),scm_list_1(sobj)); + } + return(NULL); + } + if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(stemplate))) { + scm_misc_error("g2p_pair2Tuple","bad template ~S", + scm_list_1(stemplate)); + } + + // Transform it into Python tuple. + PyObject *ppair = PyTuple_New(2); + if (NULL == ppair) { + scm_memory_error("g2p_pair2Tuple"); // NOT COVERED BY TESTS + } + + // CAR + PyObject *pitem = g2p_apply(SCM_CAR(sobj),SCM_CAR(stemplate)); + if (NULL == pitem) { + Py_DECREF(ppair); + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2Tuple: unsuccessful conversion: CAR conversion failure\n"),SCM_EOL); + } + return(NULL); // Conversion failure. + } + if (-1 == PyTuple_SetItem(ppair, 0, pitem)) { + Py_DECREF(ppair); // NOT COVERED BY TESTS + // Py_DECREF(pitem); // already performed by PyTuple_SetItem + scm_misc_error("g2p_pair2Tuple","PyTuple_SetItem car failure (~S)", // NOT COVERED BY TESTS + scm_list_1(SCM_CAR(sobj))); + } + + // CDR + pitem = g2p_apply(SCM_CDR(sobj),SCM_CDR(stemplate)); + if (NULL == pitem) { + Py_DECREF(ppair); + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2Tuple: unsuccessful conversion: CDR conversion failure\n"),SCM_EOL); + } + return(NULL); // Conversion failure. + } + if (-1 == PyTuple_SetItem(ppair, 1, pitem)) { + Py_DECREF(ppair); // NOT COVERED BY TESTS + // Py_DECREF(pitem); // already performed by PyTuple_SetItem + scm_misc_error("g2p_pair2Tuple","PyTuple_SetItem cdr failure (~S)", // NOT COVERED BY TESTS + scm_list_1(SCM_CDR(sobj))); + } + + // Done + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2Tuple: successful conversion of ~S into a Python 2-Tuple\n"),scm_list_1(sobj)); + } + return(ppair); +} + +// Very similar to g2p_pair2Tuple() above. +PyObject * +g2p_pair2List(SCM sobj,SCM stemplate) +{ + // We expect the template to be a pair. + // SCM_CAR(stemplate) is used to convert SCM_CAR(sobj), and similarly + // for SCM_CDR. + if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(sobj))) { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2List: unsuccessful conversion: ~S is not a pair\n"),scm_list_1(sobj)); + } + return(NULL); + } + if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(stemplate))) { + scm_misc_error("g2p_pair2List","bad template ~S", + scm_list_1(stemplate)); + } + + // Transform it into Python tuple. + PyObject *ppair = PyList_New(2); + if (NULL == ppair) { + scm_memory_error("g2p_pair2List"); // NOT COVERED BY TESTS + } + + // CAR + PyObject *pitem = g2p_apply(SCM_CAR(sobj),SCM_CAR(stemplate)); + if (NULL == pitem) { + Py_DECREF(ppair); + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2List: unsuccessful conversion: CAR conversion failure\n"),SCM_EOL); + } + return(NULL); + } + if (-1 == PyList_SetItem(ppair, 0, pitem)) { + Py_DECREF(ppair); // NOT COVERED BY TESTS + // Py_DECREF(pitem); // already performed by PyList_SetItem + scm_misc_error("g2p_pair2List","PyList_SetItem car failure (~S)", // NOT COVERED BY TESTS + scm_list_1(SCM_CAR(sobj))); + } + + // CDR + pitem = g2p_apply(SCM_CDR(sobj),SCM_CDR(stemplate)); + if (NULL == pitem) { + Py_DECREF(ppair); + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2List: unsuccessful conversion: CDR conversion failure\n"),SCM_EOL); + } + return(NULL); + } + if (-1 == PyList_SetItem(ppair, 1, pitem)) { + Py_DECREF(ppair); // NOT COVERED BY TESTS + // Py_DECREF(pitem); // already performed by PyList_SetItem + scm_misc_error("g2p_pair2List","PyList_SetItem cdr failure (~S)", // NOT COVERED BY TESTS + scm_list_1(SCM_CDR(sobj))); + } + + // Done + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2List: successful conversion of ~S into a Python 2-List\n"),scm_list_1(sobj)); + } + return(ppair); +} + +PyObject * +g2p_list2Tuple(SCM sobj,SCM stemplate) +{ + // sobj is expected to be a list. + + if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(sobj))) { + // sobj is not a list, so this is the wrong conversion function for it. + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2Tuple: unsuccessful conversion: ~S is not a list\n"),scm_list_1(sobj)); + } + return(NULL); + } + + // The template may be either a G2P_SMOB (to be used for converting + // all list items) or a list of templates. + long listlen = scm_num2long(scm_length(sobj),0,"g2p_list2Tuple"); + PyObject *plist = PyTuple_New(listlen); + if (NULL == plist) { + scm_memory_error("g2p_list2Tuple"); // NOT COVERED BY TESTS + } + + // Conversion loop for the case in which the template is a single G2P_SMOB + if (IS_G2P_SMOBP(stemplate)) { + long ind1; + for (ind1 = 0; ind1 < listlen; sobj = SCM_CDR(sobj),++ind1) { + SCM sitem = SCM_CAR(sobj); + PyObject *pobj1 = g2p_apply(sitem,stemplate); + if (NULL == pobj1) { + Py_DECREF(plist); + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2Tuple: unsuccessful conversion of element ~A: ~S does not match template\n"),scm_list_2(scm_long2num(ind1),sitem)); + } + return(NULL); // Conversion failure. + } + if (-1 == PyTuple_SetItem(plist, ind1, pobj1)) { + Py_DECREF(plist); // NOT COVERED BY TESTS + //Py_DECREF(pobj1); + scm_misc_error("g2p_list2Tuple","PyTuple_SetItem ~S failure (~S)", // NOT COVERED BY TESTS + scm_list_2(scm_long2num(ind1),sitem)); + } + } + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2Tuple: successful conversion of list ~S\n"),scm_list_1(sobj)); + } + return(plist); + } + + if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(stemplate))) { + // Bad template. + scm_wrong_type_arg("g2p_list2Tuple",SCM_ARG2,stemplate); + } + + // Conversion loop for the case in which the template is a list. + long ind2; + SCM stemp = SCM_EOL; // We loop over stemplate again and again as needed. + for (ind2 = 0; ind2 < listlen; + sobj = SCM_CDR(sobj), stemp=SCM_CDR(stemp), ++ind2) { + if (SCM_EQ_P(stemp,SCM_EOL)) { + stemp = stemplate; // Loop back to stemplate's beginning. + } + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2Tuple - processing item CAR(sobj)=~S CAR(stemp)=~S\n"),scm_list_2(SCM_CAR(sobj),SCM_CAR(stemp))); + } + PyObject *pobj2 = g2p_apply(SCM_CAR(sobj),SCM_CAR(stemp)); + if (NULL == pobj2) { + Py_DECREF(plist); + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2Tuple: unsuccessful conversion of element ~A: ~S does not match personalized template\n"),scm_list_2(scm_long2num(ind2),SCM_CAR(sobj))); + } + return(NULL); // Conversion failure. + } + if (-1 == PyTuple_SetItem(plist, ind2, pobj2)) { + Py_DECREF(plist); // NOT COVERED BY TESTS + //Py_DECREF(pobj2); + scm_misc_error("g2p_list2Tuple","PyTuple_SetItem ~S failure (~S)", // NOT COVERED BY TESTS + scm_list_2(scm_long2num(ind2),SCM_CAR(stemp))); + } + } + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2Tuple: successful conversion of list ~S by template\n"),scm_list_1(sobj)); + } + return(plist); +} + +// Very similar to g2p_list2Tuple() above. +PyObject * +g2p_list2List(SCM sobj,SCM stemplate) +{ + // sobj is expected to be a list. + + if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(sobj))) { + // sobj is not a list, so this is the wrong conversion function for it. + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2List: unsuccessful conversion: ~S is not a list\n"),scm_list_1(sobj)); + } + return(NULL); + } + + // The template may be either a G2P_SMOB (to be used for converting + // all list items) or a list of templates. + long listlen = scm_num2long(scm_length(sobj),0,"g2p_list2List"); + PyObject *plist = PyList_New(listlen); + if (NULL == plist) { + scm_memory_error("g2p_list2List"); // NOT COVERED BY TESTS + } + + // Conversion loop for the case in which the template is a single G2P_SMOB + if (IS_G2P_SMOBP(stemplate)) { + long ind1; + for (ind1 = 0; ind1 < listlen; sobj = SCM_CDR(sobj),++ind1) { + SCM sitem = SCM_CAR(sobj); + PyObject *pobj1 = g2p_apply(sitem,stemplate); + if (NULL == pobj1) { + Py_DECREF(plist); + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2List: unsuccessful conversion of element ~A: ~S does not match template\n"),scm_list_2(scm_long2num(ind1),sitem)); + } + return(NULL); // Conversion failure. + } + if (-1 == PyList_SetItem(plist, ind1, pobj1)) { + Py_DECREF(plist); // NOT COVERED BY TESTS + //Py_DECREF(pobj1); + scm_misc_error("g2p_list2List","PyList_SetItem ~S failure (~S)", // NOT COVERED BY TESTS + scm_list_2(scm_long2num(ind1),sitem)); + } + } + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2List: successful conversion of list ~S\n"),scm_list_1(sobj)); + } + return(plist); + } + + if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(stemplate))) { + // Bad template. + scm_wrong_type_arg("g2p_list2List",SCM_ARG2,stemplate); + } + + // Conversion loop for the case in which the template is a list. + long ind2; + SCM stemp = SCM_EOL; // We loop over stemplate again and again as needed. + for (ind2 = 0; ind2 < listlen; + sobj = SCM_CDR(sobj), stemp=SCM_CDR(stemp), ++ind2) { + if (SCM_EQ_P(stemp,SCM_EOL)) { + stemp = stemplate; // Loop back to stemplate's beginning. + } + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# DEBUG: g2p_list2List - processing item CAR(sobj)=~S CAR(stemp)=~S\n"),scm_list_2(SCM_CAR(sobj),SCM_CAR(stemp))); + } + PyObject *pobj2 = g2p_apply(SCM_CAR(sobj),SCM_CAR(stemp)); + if (NULL == pobj2) { + Py_DECREF(plist); + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2List: unsuccessful conversion of element ~A: ~S does not match personalized template\n"),scm_list_2(scm_long2num(ind2),SCM_CAR(sobj))); + } + return(NULL); // Conversion failure + } + if (-1 == PyList_SetItem(plist, ind2, pobj2)) { + Py_DECREF(plist); // NOT COVERED BY TESTS + //Py_DECREF(pobj2); + scm_misc_error("g2p_list2List","PyList_SetItem ~S failure (~S)", // NOT COVERED BY TESTS + scm_list_2(scm_long2num(ind2),SCM_CAR(stemp))); + } + } + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2List: successful conversion of list ~S by template\n"),scm_list_1(sobj)); + } + return(plist); +} + +///////////////////////// strings and symbols ////////////////////////// + +PyObject * +g2p_char2String(SCM sobj,SCM stemplate) +{ + if (SCM_CHARP(sobj)) { + return(PyString_FromFormat("%c",(int) SCM_CHAR(sobj))); + } + else { + return(NULL); + } +} + +PyObject * +g2p_string2String(SCM sobj,SCM stemplate) +{ + if (SCM_STRINGP(sobj)) { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_string2String: successful conversion of ~S into a Python String value\n"),scm_list_1(sobj)); + } + PyObject *pstr = PyString_FromStringAndSize(SCM_STRING_CHARS(sobj),SCM_STRING_LENGTH(sobj)); + if (NULL == pstr) { + scm_memory_error("g2p_string2String"); // NOT COVERED BY TESTS + } + return(pstr); + } + else { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_string2String: unsuccessful conversion: ~S is not a string value\n"),scm_list_1(sobj)); + } + return(NULL); + } +} + +// Guile is case-sensitive by default, so we don't have to +// ensure that symbols always lowercase their characters. +PyObject * +g2p_symbol2String(SCM sobj,SCM stemplate) +{ + if (SCM_SYMBOLP(sobj)) { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_symbol2String: successful conversion of ~S into a Python String value\n"),scm_list_1(sobj)); + } + PyObject *pstr = PyString_FromStringAndSize(SCM_SYMBOL_CHARS(sobj),SCM_SYMBOL_LENGTH(sobj)); + if (NULL == pstr) { + scm_memory_error("g2p_symbol2String"); // NOT COVERED BY TESTS + } + return(pstr); + } + else { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_symbol2String: unsuccessful conversion: ~S is not a symbol value\n"),scm_list_1(sobj)); + } + return(NULL); + } +} + +PyObject * +g2p_keyword2String(SCM sobj,SCM stemplate) +{ + if (SCM_KEYWORDP(sobj)) { //(SCM_BOOL_F != scm_keyword_p(sobj)) + SCM symb = scm_keyword_dash_symbol(sobj); + // We want to remove the leading '-' from the keyword, so that + // it can be used in keyword function arguments in a natural way. + const char *symchars = SCM_SYMBOL_CHARS(symb); + int symlength = SCM_SYMBOL_LENGTH(symb); + if (symlength < 1) { + scm_out_of_range("g2p_keyword2String",sobj); // NOT COVERED BY TESTS + // The symbol is too short to have dash. + // We allow symbols like '#:', which are converted here into empty + // strings. Guile does not accept them for version 1.8.x and later, + // but this does not matter here. + } + PyObject *pstr = PyString_FromStringAndSize(symchars+1,symlength-1); + if (NULL == pstr) { + scm_memory_error("g2p_keyword2String"); // NOT COVERED BY TESTS + } + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_keyword2String: successful conversion of ~S into a Python String value\n"),scm_list_1(sobj)); + } + return(pstr); + } + else { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_keyword2String: unsuccessful conversion: ~S is not a keyword value\n"),scm_list_1(sobj)); + } + return(NULL); + } +} + +///////////////////////// opaque datatypes ///////////////////////////// + +// scm_procedure_p +// scm_procedure_with_setter_p - not implemented yet. + +// Reasonable default template for procedure objects +static SCM g2p_procedure2PySCMObject_template_default; +// The above variable is to be assigned the value: +// scm_permanent_object(scm_vector(scm_list_5(...))) +// where the arguments of scm_list_5() are: +// scm_variable_ref(scm_c_lookup("python2guile")) +// scm_variable_ref(scm_c_lookup("python2guile")) +// guile2python_smob +// scm_variable_ref(scm_c_lookup("apply")) +// SCM_BOOL_F +// +// To clone the above and set other templates, use +// Scheme procedure: (set! cloned (copy-tree obj)) +// and C function: SCM scopy = scm_copy_tree(SCM sobj) + +PyObject * +g2p_procedure2PySCMObject(SCM sobj,SCM stemplate) +{ + if (SCM_EQ_P(stemplate,SCM_UNSPECIFIED)) { + stemplate = g2p_procedure2PySCMObject_template_default; + } + if (SCM_EQ_P(SCM_BOOL_F,scm_vector_p(stemplate))) { + // Bad template + scm_wrong_type_arg("g2p_procedure2PySCMObject",SCM_ARG2,stemplate); + } + if (SCM_EQ_P(SCM_BOOL_F,scm_procedure_p(sobj))) { + // Not a procedure + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_procedure2PySCMObject: unsuccessful conversion: ~S is not a procedure\n"),scm_list_1(sobj)); + } + return(NULL); + } + PyObject *pobj = wrap_scm(sobj,stemplate); + if (NULL != pobj) { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_procedure2PySCMObject: successful conversion: ~S has been wrapped\n"),scm_list_1(sobj)); + } + } + return(pobj); +} + + +PyObject * +g2p_opaque2Object(SCM sobj,SCM stemplate) +{ + if (IS_PYSMOBP(sobj)) { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_opaque2Object: the Python object inside opaque pysmob ~S is unwrapped\n"),scm_list_1(sobj)); + } + PyObject *pobj = unwrap_pysmob(sobj); + Py_INCREF(pobj); + return(pobj); + } + else { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_opaque2Object: unsuccessful conversion: ~S is not an opaque pysmob value\n"),scm_list_1(sobj)); + } + return(NULL); + } +} + + +//////////////////////////////////////////////////////////////////////// +// Big default conversion function +//////////////////////////////////////////////////////////////////////// +// The guile2python function chooses reasonable defaults, whenever +// there is a possibility for ambiguity concerning the desired +// Python datatype. + +static SCM guile2python_smob; // Used when we need it as hash default value. +static SCM guile2python_template_default; // used by guile2python(). +static SCM guile2python_pair_template_default; // used by guile2python(). +static SCM guileassoc2pythondict_default; // used by guileassoc2pythondict. + // It is set to an empty hash table. +static SCM g2p_alist_template_default; // used by g2p_alist2Dict. + +// This function does Py_INCREF() to its return values. +PyObject * +guile2python(SCM sobj,SCM stemplate) +{ + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# guile2python: entry: seeking to convert sobj=~S; unused stemplate=~S\n"),scm_list_2(sobj,stemplate)); + } + PyObject *pres = NULL; + + //////////// Scheme sequences + + // SCM '() is converted into Python None. + pres = g2p_null2PyNone(sobj,SCM_UNSPECIFIED); + if (NULL != pres) return(pres); + + // We check lists before pairs because every list in Scheme is + // also a pair. + //printf("# DEBUG: guile2python, before 1st call to g2p_list2List\n"); + pres = g2p_list2List(sobj,guile2python_template_default); + if (NULL != pres) return(pres); + + pres = g2p_pair2Tuple(sobj,guile2python_pair_template_default); + if (NULL != pres) return(pres); + + //////////// Scheme numbers + + pres = g2p_bool2Bool(sobj,SCM_UNSPECIFIED); + if (NULL != pres) return(pres); + + pres = g2p_num2Int(sobj,SCM_UNSPECIFIED); + if (NULL != pres) return(pres); + + pres = g2p_bignum2Long(sobj,SCM_UNSPECIFIED); + if (NULL != pres) return(pres); + + pres = g2p_real2Float(sobj,SCM_UNSPECIFIED); + if (NULL != pres) return(pres); + // NOTE: in Guile 1.6, rational numbers and real numbers are + // internally represented the same way. + + pres = g2p_complex2Complex(sobj,SCM_UNSPECIFIED); + if (NULL != pres) return(pres); + + //////////// Scheme strings + + pres = g2p_char2String(sobj,SCM_UNSPECIFIED); + if (NULL != pres) return(pres); + + pres = g2p_string2String(sobj,SCM_UNSPECIFIED); + if (NULL != pres) return(pres); + + pres = g2p_symbol2String(sobj,SCM_UNSPECIFIED); + if (NULL != pres) return(pres); + + pres = g2p_keyword2String(sobj,SCM_UNSPECIFIED); + if (NULL != pres) return(pres); + + //////////// Other Scheme data types + + pres = g2p_procedure2PySCMObject(sobj,SCM_UNSPECIFIED); + if (NULL != pres) return(pres); + + pres = g2p_opaque2Object(sobj,SCM_UNSPECIFIED); + if (NULL != pres) return(pres); + + // !!! More simple data types: regexp + // !!! To wrap: procedures, macros(opaque), variables, + // !!! opaque: asyncs, dynamic roots, fluids, hooks, ports + + // !!! Scheme objects + + // !!! More complex data types: vectors,records,structures,arrays, + + + else { + scm_wrong_type_arg("guile2python",SCM_ARG1,sobj); + } +} + +//////////////////////////////////////////////////////////////////////// +// Quick conversion from alist into Dict +//////////////////////////////////////////////////////////////////////// +// Treat the SCM object as an association list and convert it into +// Python hash, whose keys are strings corresponding to the +// association list's keys (which must be keywords). +// +// This version is meant for quick conversion of keyword arguments, +// and it allows only keywords as keys. +// +// Checks: +// 1. Validity of keys. +// 2. No duplicate keys. +// +// The stemplate argument must be an hash table (keys are checked +// using eq?). + +PyObject * +guileassoc2pythondict(SCM sobj,SCM stemplate) +{ + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# guileassoc2pythondict: entry: seeking to convert sobj=~S using stemplate=~S\n"),scm_list_2(sobj,stemplate)); + } + + if (SCM_UNBNDP(stemplate)) { + stemplate = guileassoc2pythondict_default; + } + PyObject *pdict = NULL; + PyObject *pkey = NULL; + PyObject *pval = NULL; + + if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(sobj))) { + scm_wrong_type_arg("guileassoc2pythondict",SCM_ARG1,sobj); + } + // TODO: add here a call to SCM_HASHP(stemplate) to validate that the + // template is hashtable. + + long listlen = scm_num2long(scm_length(sobj),0,"guileassoc2pythondict"); + pdict = PyDict_New(); + if (NULL == pdict) { + scm_memory_error("guileassoc2pythondict"); // NOT COVERED BY TESTS + } + long ind; + for (ind = 0,pkey = NULL,pval = NULL; ind < listlen; ++ind) { + SCM spair = SCM_CAR(sobj); + if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(spair))) { + // Not a pair. + Py_DECREF(pdict); + scm_wrong_type_arg("guileassoc2pythondict",ind+1,spair); + } + + pkey = g2p_keyword2String(SCM_CAR(spair),SCM_UNSPECIFIED); + if (NULL == pkey) { + // Illegal key - it must be a keyword. + scm_wrong_type_arg("guileassoc2pythondict",ind+1,SCM_CAR(spair)); + } + if (0 != PyDict_Contains(pdict,pkey)) { + // Duplicate key or some error + Py_DECREF(pdict); + Py_DECREF(pkey); + scm_misc_error("guileassoc2pythondict","duplicate key (~S)", + scm_list_1(SCM_CAR(spair))); + } + + SCM sitem_template = scm_hashq_ref(stemplate,SCM_CAR(spair),guile2python_smob); + pval = g2p_apply(SCM_CDR(spair),sitem_template); + if (NULL == pval) { + Py_DECREF(pdict); + Py_DECREF(pkey); + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# guileassoc2pythondict: unsuccessful conversion of element ~A: ~S does not match template\n"),scm_list_2(SCM_CAR(spair),SCM_CDR(spair))); + } + return(NULL); // Conversion failure. + } + + if (-1 == PyDict_SetItem(pdict,pkey,pval)) { + Py_XDECREF(pdict); // NOT COVERED BY TESTS + //Py_XDECREF(pkey); + //Py_XDECREF(pval); + scm_misc_error("guileassoc2pythondict","PyDict_SetItem failure (~S : ~S)", // NOT COVERED BY TESTS + scm_list_2(SCM_CAR(spair),SCM_CDR(spair))); + } + sobj = SCM_CDR(sobj); + } + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# guileassoc2pythondict: successful conversion of ~S\n"),scm_list_1(sobj)); + } + return(pdict); +} + +//////////////////////////////////////////////////////////////////////// +// Generalized conversion from alist into Dict +//////////////////////////////////////////////////////////////////////// +// The following function is meant for conversion of alists into +// general Python Dicts. +// It allows any immutable object as key. +// NOTE: no check for immutability is made here! +// +// The stemplate argument must be a list of pairs (i.e. structured +// like an alist). + +PyObject * +g2p_alist2Dict(SCM sobj,SCM stemplate) +{ + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_alist2Dict sobj=~S stemplate=~S\n"),scm_list_2(sobj,stemplate)); + } + if (SCM_UNBNDP(stemplate) || SCM_EQ_P(stemplate,SCM_EOL)) { + stemplate = g2p_alist_template_default; + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_alist2Dict - default template was chosen: ~S\n"),scm_list_1(stemplate)); + } + } + + if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(sobj))) { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_alist2Dict: unsuccessful conversion: argument is not list, let alone alist\n"),SCM_EOL); + } + return(NULL); // Conversion failure. + } + if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(stemplate))) { + scm_wrong_type_arg("g2p_alist2Dict",SCM_ARG2,sobj); // Bad template. + } + + long listlen = scm_num2long(scm_length(sobj),0,"g2p_alist2Dict"); + PyObject *pdict = PyDict_New(); + if (NULL == pdict) { + scm_memory_error("g2p_alist2Dict"); // NOT COVERED BY TESTS + } + + long ind; + SCM stemp = SCM_EOL; // We loop over stemplate again and again as needed. + for (ind = 0; ind < listlen; + sobj = SCM_CDR(sobj),stemp=SCM_CDR(stemp), ++ind) { + if (SCM_EQ_P(stemp,SCM_EOL)) { + stemp = stemplate; // Loop back to stemplate's beginning. + } + + if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(SCM_CAR(stemp)))) { + // Not a template pair - bad template. + Py_DECREF(pdict); + scm_wrong_type_arg("g2p_alist2Dict",SCM_ARG2,SCM_CAR(stemp)); + } + SCM spair = SCM_CAR(sobj); + if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(spair))) { + // Not a data pair - conversion failure. + Py_DECREF(pdict); + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_alist2Dict: unsuccessful conversion: alist item is not pair: ~S\n"),scm_list_1(spair)); + } + return(NULL); + } + + PyObject *pkey = g2p_apply(SCM_CAR(spair),SCM_CAAR(stemp)); + if (NULL == pkey) { + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_alist2Dict: unsuccessful conversion: key ~S, template ~S\n"),scm_list_2(SCM_CAR(spair),SCM_CAAR(stemp))); + } + return(NULL); // Conversion failure. + } + if (0 != PyDict_Contains(pdict,pkey)) { + // Duplicate key or some error. + Py_DECREF(pdict); + Py_DECREF(pkey); + scm_misc_error("g2p_alist2Dict","duplicate key (~S)", + scm_list_1(SCM_CAR(spair))); + } + + PyObject *pval = g2p_apply(SCM_CDR(spair),SCM_CDAR(stemp)); + if (NULL == pval) { + Py_DECREF(pdict); + Py_DECREF(pkey); + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_alist2Dict: unsuccessful conversion: value ~S, template ~S\n"),scm_list_2(SCM_CDR(spair),SCM_CDAR(stemp))); + } + return(NULL); // Conversion failure. + } + + if (-1 == PyDict_SetItem(pdict,pkey,pval)) { + Py_XDECREF(pdict); // NOT COVERED BY TESTS + //Py_XDECREF(pkey); + //Py_XDECREF(pval); + scm_misc_error("g2p_alist2Dict","PyDict_SetItem failure (~S : ~S)", // NOT COVERED BY TESTS + scm_list_2(SCM_CAR(spair),SCM_CDR(spair))); + } + } + if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) { + scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_alist2Dict: successful conversion\n"),SCM_EOL); + } + return(pdict); +} + +// NOTE: +// After all, we'll not support hash tables. +// This is because I found no function to identify an hash table. +// Hash table can be converted into alist using: +// (hash-fold acons '() hashtable) + +//////////////////////////////////////////////////////////////////////// +// Register all g2p_* functions + +#include "guiletopy.inc" + +// The following must happen after registration of all g2p_* and p2g_* +// functions. +void +init_default_guiletopy_templates(void) +{ + SCM s_python2guile = scm_variable_ref(scm_c_lookup("python2guile")); + g2p_procedure2PySCMObject_template_default + = scm_permanent_object(scm_vector(scm_list_5( + s_python2guile, + s_python2guile, + guile2python_smob, + scm_variable_ref(scm_c_lookup("apply")), + SCM_BOOL_F))); + scm_c_define("pyscm-default-template",g2p_procedure2PySCMObject_template_default); +} + +//////////////////////////////////////////////////////////////////////// +// End of guiletopy.c |