/* libctl: flexible Guile-based control files for scientific software * Copyright (C) 1998-2020 Massachusetts Institute of Technology and Steven G. Johnson * * 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 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; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Steven G. Johnson can be contacted at stevenj@alum.mit.edu. */ #include #include #include #include #include "ctl.h" /**************************************************************************/ /* Functions missing from Guile 1.2: */ #if !defined(HAVE_GH_BOOL2SCM) && !defined(HAVE_NO_GH) /* Guile 1.2 is missing gh_bool2scm for some reason; redefine: */ SCM ctl_gh_bool2scm(boolean b) { return (b ? SCM_BOOL_T : SCM_BOOL_F); } #endif #if defined(HAVE_NO_GH) #define gh_length(x) scm_to_long(scm_length(x)) #elif !defined(HAVE_GH_LENGTH) #define gh_length gh_list_length #endif #if defined(HAVE_NO_GH) #define list_ref(l, index) scm_list_ref(l, scm_from_int(index)) #elif !defined(HAVE_GH_LIST_REF) /* Guile 1.2 doesn't have the gh_list_ref function. Sigh. */ /* Note: index must be in [0,list_length(l) - 1]. We don't check! */ static SCM list_ref(list l, int index) { SCM cur = SCM_UNSPECIFIED, rest = l; while (index >= 0) { cur = gh_car(rest); rest = gh_cdr(rest); --index; } return cur; } #else /* HAVE_GH_LIST_REF */ #define list_ref(l, index) gh_list_ref(l, gh_int2scm(index)) #endif #if defined(HAVE_NO_GH) #define vector_ref(v, i) scm_c_vector_ref(v, i) #elif !defined(HAVE_GH_VECTOR_REF) #define vector_ref(v, i) gh_vref(v, gh_int2scm(i)) #else #define vector_ref(v, i) gh_vector_ref(v, gh_int2scm(i)) #endif /**************************************************************************/ /* Scheme file loading (don't use gh_load directly because subsequent loads won't use the correct path name). Uses our "include" function from include.scm, or defaults to gh_load if this function isn't defined. */ void ctl_include(const char *filename) { SCM include_proc = gh_lookup("include"); if (include_proc == SCM_UNDEFINED) gh_load(filename); else #ifdef HAVE_NO_GH scm_call_1(include_proc, ctl_convert_string_to_scm(filename)); #else gh_call1(include_proc, gh_str02scm(filename)); #endif } /* convert a pathname into one relative to the current include dir */ char *ctl_fix_path(const char *path) { char *newpath; if (path[0] != '/') { SCM include_dir = gh_lookup("include-dir"); if (include_dir != SCM_UNDEFINED) { char *dir = ctl_convert_string_to_c(include_dir); newpath = (char *)malloc(sizeof(char) * (strlen(dir) + strlen(path) + 2)); strcpy(newpath, dir); free(dir); if (newpath[0] && newpath[strlen(newpath) - 1] != '/') strcat(newpath, "/"); strcat(newpath, path); return newpath; } } newpath = (char *)malloc(sizeof(char) * (strlen(path) + 1)); strcpy(newpath, path); return newpath; } /**************************************************************************/ /* type conversion */ vector3 scm2vector3(SCM sv) { vector3 v; v.x = ctl_convert_number_to_c(vector_ref(sv, 0)); v.y = ctl_convert_number_to_c(vector_ref(sv, 1)); v.z = ctl_convert_number_to_c(vector_ref(sv, 2)); return v; } matrix3x3 scm2matrix3x3(SCM sm) { matrix3x3 m; m.c0 = scm2vector3(vector_ref(sm, 0)); m.c1 = scm2vector3(vector_ref(sm, 1)); m.c2 = scm2vector3(vector_ref(sm, 2)); return m; } static SCM make_vector3(SCM x, SCM y, SCM z) { SCM vscm; vscm = scm_c_make_vector(3, SCM_UNSPECIFIED); #ifdef SCM_SIMPLE_VECTOR_SET SCM_SIMPLE_VECTOR_SET(vscm, 0, x); SCM_SIMPLE_VECTOR_SET(vscm, 1, y); SCM_SIMPLE_VECTOR_SET(vscm, 2, z); #else { SCM *data; data = SCM_VELTS(vscm); data[0] = x; data[1] = y; data[2] = z; } #endif return vscm; } SCM vector32scm(vector3 v) { return make_vector3(ctl_convert_number_to_scm(v.x), ctl_convert_number_to_scm(v.y), ctl_convert_number_to_scm(v.z)); } SCM matrix3x32scm(matrix3x3 m) { return make_vector3(vector32scm(m.c0), vector32scm(m.c1), vector32scm(m.c2)); } cnumber scm2cnumber(SCM sx) { #ifdef HAVE_SCM_COMPLEXP if (scm_real_p(sx) && !(SCM_COMPLEXP(sx))) return make_cnumber(ctl_convert_number_to_c(sx), 0.0); else return make_cnumber(SCM_COMPLEX_REAL(sx), SCM_COMPLEX_IMAG(sx)); #else if (scm_real_p(sx) && !(SCM_NIMP(sx) && SCM_INEXP(sx) && SCM_CPLXP(sx))) return make_cnumber(ctl_convert_number_to_c(sx), 0.0); else return make_cnumber(SCM_REALPART(sx), SCM_IMAG(sx)); #endif } SCM cnumber2scm(cnumber x) { #if defined(HAVE_SCM_C_MAKE_RECTANGULAR) /* Guile 1.6.5 */ return scm_c_make_rectangular(x.re, x.im); /* Guile 1.5 */ #elif defined(HAVE_SCM_MAKE_COMPLEX) return scm_make_complex(x.re, x.im); /* Guile 1.5 */ #else if (x.im == 0.0) return ctl_convert_number_to_scm(x.re); else return scm_makdbl(x.re, x.im); #endif } cvector3 scm2cvector3(SCM sv) { cvector3 v; v.x = scm2cnumber(vector_ref(sv, 0)); v.y = scm2cnumber(vector_ref(sv, 1)); v.z = scm2cnumber(vector_ref(sv, 2)); return v; } cmatrix3x3 scm2cmatrix3x3(SCM sm) { cmatrix3x3 m; m.c0 = scm2cvector3(vector_ref(sm, 0)); m.c1 = scm2cvector3(vector_ref(sm, 1)); m.c2 = scm2cvector3(vector_ref(sm, 2)); return m; } SCM cvector32scm(cvector3 v) { return make_vector3(cnumber2scm(v.x), cnumber2scm(v.y), cnumber2scm(v.z)); } SCM cmatrix3x32scm(cmatrix3x3 m) { return make_vector3(cvector32scm(m.c0), cvector32scm(m.c1), cvector32scm(m.c2)); } /**************************************************************************/ /* variable get/set functions */ /**** Getters ****/ integer ctl_get_integer(const char *identifier) { return (ctl_convert_integer_to_c(gh_lookup(identifier))); } number ctl_get_number(const char *identifier) { return (ctl_convert_number_to_c(gh_lookup(identifier))); } cnumber ctl_get_cnumber(const char *identifier) { return (scm2cnumber(gh_lookup(identifier))); } boolean ctl_get_boolean(const char *identifier) { return (ctl_convert_boolean_to_c(gh_lookup(identifier))); } char *ctl_get_string(const char *identifier) { return (ctl_convert_string_to_c(gh_lookup(identifier))); } vector3 ctl_get_vector3(const char *identifier) { return (scm2vector3(gh_lookup(identifier))); } matrix3x3 ctl_get_matrix3x3(const char *identifier) { return (scm2matrix3x3(gh_lookup(identifier))); } cvector3 ctl_get_cvector3(const char *identifier) { return (scm2cvector3(gh_lookup(identifier))); } cmatrix3x3 ctl_get_cmatrix3x3(const char *identifier) { return (scm2cmatrix3x3(gh_lookup(identifier))); } list ctl_get_list(const char *identifier) { return (gh_lookup(identifier)); } object ctl_get_object(const char *identifier) { return (gh_lookup(identifier)); } function ctl_get_function(const char *identifier) { return (gh_lookup(identifier)); } SCM ctl_get_SCM(const char *identifier) { return (gh_lookup(identifier)); } /**** Setters ****/ /* UGLY hack alert! There doesn't seem to be any clean way of setting Scheme variables from C in Guile (e.g. no gh_* interface). One option is to use scm_symbol_set_x (symbol-set! in Scheme), but I'm not sure how to get this to work in Guile 1.3 because of the %&*@^-ing module system (I need to pass some module for the first parameter, but I don't know what to pass). Instead, I hacked together the following my_symbol_set_x routine, using the functions scm_symbol_value0 and scm_symbol_set_x from the Guile 1.3 sources. (scm_symbol_value0 has the virtue of looking in the correct module somehow; I also used this function to replace gh_lookup, which broke in Guile 1.3 as well...sigh.) Note that I can't call "set!" because it is really a macro. All the ugliness is confined to the set_value() routine, though. Update: in Guile 1.5, we can call scm_variable_set_x (equivalent to variable-set!) to set values of variables, which are looked up via scm_c_lookup (which doesn't exist in Guile 1.3.x). */ #if !(defined(HAVE_SCM_VARIABLE_SET_X) && defined(HAVE_SCM_C_LOOKUP)) #define USE_MY_SYMBOL_SET_X 1 /* use the hack */ #endif #ifdef USE_MY_SYMBOL_SET_X static SCM my_symbol_set_x(const char *name, SCM v) { /* code swiped from scm_symbol_value0 and scm_symbol_set_x */ SCM symbol = scm_intern_obarray_soft(name, strlen(name), scm_symhash, 0); SCM vcell = scm_sym2vcell(SCM_CAR(symbol), SCM_CDR(scm_top_level_lookup_closure_var), SCM_BOOL_F); if (SCM_FALSEP(vcell)) return SCM_UNDEFINED; SCM_SETCDR(vcell, v); return SCM_UNSPECIFIED; } #endif static void set_value(const char *identifier, SCM value) { #if defined(USE_SCM_SYMBOL_SET_X) /* worked in Guile 1.1, 1.2 */ scm_symbol_set_x(SCM_BOOL_F, gh_symbol2scm(identifier), value); #elif defined(HAVE_SCM_VARIABLE_SET_X) && defined(HAVE_SCM_C_LOOKUP) scm_variable_set_x(scm_c_lookup(identifier), value); #elif defined(USE_MY_SYMBOL_SET_X) my_symbol_set_x(identifier, value); #endif } void ctl_set_integer(const char *identifier, integer value) { set_value(identifier, ctl_convert_integer_to_scm(value)); } void ctl_set_number(const char *identifier, number value) { set_value(identifier, ctl_convert_number_to_scm(value)); } void ctl_set_cnumber(const char *identifier, cnumber value) { set_value(identifier, cnumber2scm(value)); } void ctl_set_boolean(const char *identifier, boolean value) { set_value(identifier, ctl_convert_boolean_to_scm(value)); } void ctl_set_string(const char *identifier, const char *value) { set_value(identifier, ctl_convert_string_to_scm(value)); } void ctl_set_vector3(const char *identifier, vector3 value) { set_value(identifier, vector32scm(value)); } void ctl_set_matrix3x3(const char *identifier, matrix3x3 value) { set_value(identifier, matrix3x32scm(value)); } void ctl_set_cvector3(const char *identifier, cvector3 value) { set_value(identifier, cvector32scm(value)); } void ctl_set_cmatrix3x3(const char *identifier, cmatrix3x3 value) { set_value(identifier, cmatrix3x32scm(value)); } void ctl_set_list(const char *identifier, list value) { set_value(identifier, value); } void ctl_set_object(const char *identifier, object value) { set_value(identifier, value); } void ctl_set_function(const char *identifier, function value) { set_value(identifier, value); } void ctl_set_SCM(const char *identifier, SCM value) { set_value(identifier, value); } /**************************************************************************/ /* list traversal */ int list_length(list l) { return (gh_length(l)); } integer integer_list_ref(list l, int index) { return (ctl_convert_integer_to_c(list_ref(l, index))); } number number_list_ref(list l, int index) { return (ctl_convert_number_to_c(list_ref(l, index))); } cnumber cnumber_list_ref(list l, int index) { return (scm2cnumber(list_ref(l, index))); } boolean boolean_list_ref(list l, int index) { return (SCM_BOOL_F != list_ref(l, index)); } char *string_list_ref(list l, int index) { return (ctl_convert_string_to_c(list_ref(l, index))); } vector3 vector3_list_ref(list l, int index) { return (scm2vector3(list_ref(l, index))); } matrix3x3 matrix3x3_list_ref(list l, int index) { return (scm2matrix3x3(list_ref(l, index))); } cvector3 cvector3_list_ref(list l, int index) { return (scm2cvector3(list_ref(l, index))); } cmatrix3x3 cmatrix3x3_list_ref(list l, int index) { return (scm2cmatrix3x3(list_ref(l, index))); } list list_list_ref(list l, int index) { return (list_ref(l, index)); } object object_list_ref(list l, int index) { return (list_ref(l, index)); } function function_list_ref(list l, int index) { return (list_ref(l, index)); } SCM SCM_list_ref(list l, int index) { return (list_ref(l, index)); } /**************************************************************************/ /* list creation */ #define MAKE_LIST(conv) \ { \ int i; \ list cur_list = SCM_EOL; \ for (i = num_items - 1; i >= 0; --i) \ cur_list = gh_cons(conv(items[i]), cur_list); \ return (cur_list); \ } #ifdef HAVE_NO_GH list make_integer_list(int num_items, const integer *items) MAKE_LIST(scm_from_int) list make_boolean_list(int num_items, const boolean *items) MAKE_LIST(scm_from_bool) list make_string_list(int num_items, const char **items) MAKE_LIST(scm_from_locale_string) list make_number_list(int num_items, const number *items) MAKE_LIST(scm_from_double) #else /* ! HAVE_NO_GH */ list make_integer_list(int num_items, const integer *items) MAKE_LIST(gh_int2scm) list make_boolean_list(int num_items, const boolean *items) MAKE_LIST(gh_bool2scm) list make_string_list(int num_items, const char **items) MAKE_LIST(gh_str02scm) list make_number_list(int num_items, const number *items) MAKE_LIST(gh_double2scm) #endif /* ! HAVE_NO_GH */ list make_cnumber_list(int num_items, const cnumber *items) MAKE_LIST(cnumber2scm) list make_vector3_list(int num_items, const vector3 *items) MAKE_LIST(vector32scm) list make_matrix3x3_list(int num_items, const matrix3x3 *items) MAKE_LIST(matrix3x32scm) list make_cvector3_list(int num_items, const cvector3 *items) MAKE_LIST(cvector32scm) list make_cmatrix3x3_list(int num_items, const cmatrix3x3 *items) MAKE_LIST(cmatrix3x32scm) #define NO_CONVERSION list make_list_list(int num_items, const list *items) MAKE_LIST(NO_CONVERSION) list make_object_list(int num_items, const object *items) MAKE_LIST(NO_CONVERSION) list make_function_list(int num_items, const object *items) MAKE_LIST(NO_CONVERSION) list make_SCM_list(int num_items, const object *items) MAKE_LIST(NO_CONVERSION) /**************************************************************************/ /* object properties */ boolean object_is_member(const char *type_name, object o) { return (SCM_BOOL_F != gh_call2(gh_lookup("object-member?"), gh_symbol2scm(type_name), o)); } static SCM object_property_value(object o, const char *property_name) { return (gh_call2(gh_lookup("object-property-value"), o, gh_symbol2scm(property_name))); } integer integer_object_property(object o, const char *property_name) { return (ctl_convert_integer_to_c(object_property_value(o, property_name))); } number number_object_property(object o, const char *property_name) { return (ctl_convert_number_to_c(object_property_value(o, property_name))); } cnumber cnumber_object_property(object o, const char *property_name) { return (scm2cnumber(object_property_value(o, property_name))); } boolean boolean_object_property(object o, const char *property_name) { return (SCM_BOOL_F != object_property_value(o, property_name)); } char *string_object_property(object o, const char *property_name) { return (ctl_convert_string_to_c(object_property_value(o, property_name))); } vector3 vector3_object_property(object o, const char *property_name) { return (scm2vector3(object_property_value(o, property_name))); } matrix3x3 matrix3x3_object_property(object o, const char *property_name) { return (scm2matrix3x3(object_property_value(o, property_name))); } cvector3 cvector3_object_property(object o, const char *property_name) { return (scm2cvector3(object_property_value(o, property_name))); } cmatrix3x3 cmatrix3x3_object_property(object o, const char *property_name) { return (scm2cmatrix3x3(object_property_value(o, property_name))); } list list_object_property(object o, const char *property_name) { return (object_property_value(o, property_name)); } object object_object_property(object o, const char *property_name) { return (object_property_value(o, property_name)); } function function_object_property(object o, const char *property_name) { return (object_property_value(o, property_name)); } SCM SCM_object_property(object o, const char *property_name) { return (object_property_value(o, property_name)); }