/* -*- mode: c; c-basic-offset: 8 -*- */ #include #include #include #include #include #include #include "../../src/gnumeric.h" #include "../../src/symbol.h" #include "../../src/plugin.h" #include "../../src/expr.h" #include "../../src/func.h" static int scm_num2int (SCM num) { return (int)scm_num2long(num, (char*)SCM_ARG1, "scm_num2int"); /* may I use scm_num2long? */ } static SCM scm_symbolfrom0str (char *name) { return SCM_CAR(scm_intern0(name)); } static SCM list_to_scm (GList *list, CellRef eval_cell) { /* FIXME: implement this */ return SCM_EOL; } static SCM cell_ref_to_scm (CellRef cell, CellRef eval_cell) { int col = cell.col_relative ? cell.col + eval_cell.col : cell.col, row = cell.row_relative ? cell.row + eval_cell.row : cell.row; return scm_cons(scm_symbolfrom0str("cell-ref"), scm_cons(scm_long2num(col), scm_long2num(row))); /* FIXME: we need the relative-flags also */ } static CellRef scm_to_cell_ref (SCM scm) { CellRef cell = { 0, 0, 0, 0 }; if (SCM_NIMP(scm) && SCM_CONSP(scm) && SCM_NFALSEP(scm_eq_p(SCM_CAR(scm), scm_symbolfrom0str("cell-ref"))) && SCM_NIMP(SCM_CDR(scm)) && SCM_CONSP(SCM_CDR(scm)) && SCM_NFALSEP(scm_number_p(SCM_CADR(scm))) && SCM_NFALSEP(scm_number_p(SCM_CDDR(scm)))) { cell.col = (int)scm_num2int(SCM_CADR(scm)); cell.row = (int)scm_num2int(SCM_CDDR(scm)); } else ; /* FIXME: should report error */ return cell; } static SCM value_to_scm (Value *val, CellRef cell_ref) { if (val == NULL) return SCM_EOL; switch (val->type) { case VALUE_STRING : return scm_makfrom0str(val->v.str->str); case VALUE_INTEGER : return scm_long2num(val->v.v_int); case VALUE_FLOAT : return scm_dbl2big(val->v.v_float); case VALUE_CELLRANGE : return scm_cons(scm_symbolfrom0str("cell-range"), scm_cons(cell_ref_to_scm(val->v.cell_range.cell_a, cell_ref), cell_ref_to_scm(val->v.cell_range.cell_b, cell_ref))); case VALUE_ARRAY : return SCM_UNSPECIFIED; } return SCM_UNSPECIFIED; } static Value* scm_to_value (SCM scm) { if (SCM_NIMP(scm) && SCM_STRINGP(scm)) { Value *val = g_new(Value, 1); val->type = VALUE_STRING; val->v.str = string_get(SCM_CHARS(scm)); /* assuming (wrongly?) that scm strings are zero-terminated */ return val; } else if (SCM_NFALSEP(scm_number_p(scm))) { if (scm_integer_p(scm)) return value_int((int)scm_num2int(scm)); else return value_float((float)scm_num2dbl(scm, 0)); } else if (SCM_NIMP(scm) && SCM_CONSP(scm)) { if (scm_eq_p(SCM_CAR(scm), scm_symbolfrom0str("cell-range")) && SCM_NIMP(SCM_CDR(scm)) && SCM_CONSP(SCM_CDR(scm))) { Value *val = g_new(Value, 1); val->type = VALUE_CELLRANGE; val->v.cell_range.cell_a = scm_to_cell_ref(SCM_CADR(scm)); val->v.cell_range.cell_b = scm_to_cell_ref(SCM_CDDR(scm)); return val; } } return NULL; /* maybe we should return something more meaningful!? */ } static SCM expr_to_scm (ExprTree *expr, CellRef cell_ref) { switch (expr->oper) { case OPER_EQUAL : return SCM_LIST3(scm_symbolfrom0str("="), expr_to_scm(expr->u.binary.value_a, cell_ref), expr_to_scm(expr->u.binary.value_b, cell_ref)); case OPER_GT : return SCM_LIST3(scm_symbolfrom0str(">"), expr_to_scm(expr->u.binary.value_a, cell_ref), expr_to_scm(expr->u.binary.value_b, cell_ref)); case OPER_LT : return SCM_LIST3(scm_symbolfrom0str("<"), expr_to_scm(expr->u.binary.value_a, cell_ref), expr_to_scm(expr->u.binary.value_b, cell_ref)); case OPER_GTE : return SCM_LIST3(scm_symbolfrom0str(">="), expr_to_scm(expr->u.binary.value_a, cell_ref), expr_to_scm(expr->u.binary.value_b, cell_ref)); case OPER_LTE : return SCM_LIST3(scm_symbolfrom0str("<="), expr_to_scm(expr->u.binary.value_a, cell_ref), expr_to_scm(expr->u.binary.value_b, cell_ref)); case OPER_NOT_EQUAL : return SCM_LIST3(scm_symbolfrom0str("<>"), expr_to_scm(expr->u.binary.value_a, cell_ref), expr_to_scm(expr->u.binary.value_b, cell_ref)); case OPER_ADD : return SCM_LIST3(scm_symbolfrom0str("+"), expr_to_scm(expr->u.binary.value_a, cell_ref), expr_to_scm(expr->u.binary.value_b, cell_ref)); case OPER_SUB : return SCM_LIST3(scm_symbolfrom0str("-"), expr_to_scm(expr->u.binary.value_a, cell_ref), expr_to_scm(expr->u.binary.value_b, cell_ref)); case OPER_NEG : return SCM_LIST2(scm_symbolfrom0str("neg"), expr_to_scm(expr->u.value, cell_ref)); case OPER_MULT : return SCM_LIST3(scm_symbolfrom0str("*"), expr_to_scm(expr->u.binary.value_a, cell_ref), expr_to_scm(expr->u.binary.value_b, cell_ref)); case OPER_DIV : return SCM_LIST3(scm_symbolfrom0str("/"), expr_to_scm(expr->u.binary.value_a, cell_ref), expr_to_scm(expr->u.binary.value_b, cell_ref)); case OPER_EXP : return SCM_LIST3(scm_symbolfrom0str("expt"), expr_to_scm(expr->u.binary.value_a, cell_ref), expr_to_scm(expr->u.binary.value_b, cell_ref)); case OPER_CONCAT : return SCM_LIST3(scm_symbolfrom0str("string-append"), expr_to_scm(expr->u.binary.value_a, cell_ref), expr_to_scm(expr->u.binary.value_b, cell_ref)); case OPER_FUNCALL : return SCM_LIST3(scm_symbolfrom0str("funcall"), scm_makfrom0str(expr->u.function.symbol->str), list_to_scm(expr->u.function.arg_list, cell_ref)); case OPER_CONSTANT : return value_to_scm(expr->u.constant, cell_ref); case OPER_VAR : return scm_cons(scm_symbolfrom0str("var"), cell_ref_to_scm(expr->u.constant->v.cell, cell_ref)); } return SCM_UNSPECIFIED; } static Value* func_scm_eval (FunctionDefinition *fn, Value *argv[], char **error_string) { SCM result; if (argv[0]->type != VALUE_STRING) { *error_string = _("Argument must be a Guile expression"); return NULL; } result = scm_eval_0str(argv[0]->v.str->str); return scm_to_value(result); } static Value* func_scm_apply (void *tsheet, GList *expr_node_list, int eval_col, int eval_row, char **error_string) { int i; Value *value; char *symbol; SCM args = SCM_EOL, function, result; if (g_list_length(expr_node_list) < 1) { *error_string = _("Invalid number of arguments"); return NULL; } value = eval_expr(tsheet, (ExprTree*)expr_node_list->data, eval_col, eval_row, error_string); if (value == NULL) { *error_string = _("First argument to SCM must be a Guile expression"); return NULL; } symbol = value_string(value); if (symbol == NULL) { *error_string = _("First argument to SCM must be a Guile expression"); return NULL; } function = scm_eval_0str(symbol); if (SCM_UNBNDP(function)) { *error_string = _("Undefined scheme function"); return NULL; } value_release(value); for (i = g_list_length(expr_node_list) - 1; i >= 1; --i) { CellRef eval_cell; eval_cell.col = eval_col; eval_cell.row = eval_row; eval_cell.col_relative = 0; eval_cell.row_relative = 0; eval_cell.sheet = NULL; value = eval_expr(tsheet, (ExprTree*)g_list_nth(expr_node_list, i)->data, eval_col, eval_row, error_string); if (value == NULL) { *error_string = _("Could not evaluate argument"); return NULL; } args = scm_cons(value_to_scm(value, eval_cell), args); value_release(value); } result = scm_apply(function, args, SCM_EOL); return scm_to_value(result); } static SCM scm_cell_value (SCM scm) { CellRef cell_ref = scm_to_cell_ref(scm); Cell *cell = sheet_cell_get(workbook_get_current_sheet(current_workbook), cell_ref.col, cell_ref.row); if (cell == NULL) return SCM_EOL; assert(cell->value != NULL); return value_to_scm(cell->value, cell_ref); } static SCM scm_cell_expr (SCM scm) { CellRef cell_ref = scm_to_cell_ref(scm); Cell *cell = sheet_cell_get(workbook_get_current_sheet(current_workbook), cell_ref.col, cell_ref.row); if (cell == NULL || cell->parsed_node == NULL) return SCM_EOL; return expr_to_scm(cell->parsed_node, cell_ref); } static SCM scm_set_cell_string (SCM scm_cell_ref, SCM scm_string) { CellRef cell_ref = scm_to_cell_ref(scm_cell_ref); Cell *cell = sheet_cell_fetch(workbook_get_current_sheet(current_workbook), cell_ref.col, cell_ref.row); SCM_ASSERT(SCM_NIMP(scm_string) && SCM_STRINGP(scm_string), scm_string, SCM_ARG2, "set-cell-string!"); if (cell == NULL) return SCM_UNSPECIFIED; cell_set_text(cell, SCM_CHARS(scm_string)); return SCM_UNSPECIFIED; } static SCM scm_gnumeric_funcall(SCM funcname, SCM arglist) { char *error_string; int i, num_args; Value **values; CellRef cell_ref = { 0, 0, 0, 0 }; SCM_ASSERT(SCM_NIMP(funcname) && SCM_STRINGP(funcname), funcname, SCM_ARG1, "gnumeric-funcall"); SCM_ASSERT(SCM_NFALSEP(scm_list_p(arglist)), arglist, SCM_ARG2, "gnumeric-funcall"); num_args = scm_ilength(arglist); values = g_new(Value*, num_args); for (i = 0; i < num_args; ++i) { values[i] = scm_to_value(SCM_CAR(arglist)); arglist = SCM_CDR(arglist); } return value_to_scm(function_call_with_values(workbook_get_current_sheet(current_workbook), SCM_CHARS(funcname), num_args, values, &error_string), cell_ref); } typedef struct { FunctionDefinition *fndef; SCM function; } FuncData; static GList *funclist = NULL; static int fndef_compare(FuncData *fdata, FunctionDefinition *fndef) { return (fdata->fndef != fndef); } static Value* func_marshal_func (FunctionDefinition *fndef, Value *argv[], char **error_string) { GList *l; SCM args = SCM_EOL, result, function; CellRef dummy = { 0, 0, 0, 0 }; int i, count = strlen(fndef->args); l = g_list_find_custom(funclist, fndef, (GCompareFunc)fndef_compare); if (l == NULL) { *error_string = _("Unable to lookup Guile function."); return NULL; } function = ((FuncData*)l->data)->function; for (i = count - 1; i >= 0; --i) args = scm_cons(value_to_scm(argv[i], dummy), args); result = scm_apply(function, args, SCM_EOL); return scm_to_value(result); } static SCM scm_register_function(SCM scm_name, SCM scm_args, SCM scm_help, SCM scm_function) { FunctionDefinition *fndef; FuncData *fdata; SCM_ASSERT(SCM_NIMP(scm_name) && SCM_STRINGP(scm_name), scm_name, SCM_ARG1, "scm_register_function"); SCM_ASSERT(SCM_NIMP(scm_args) && SCM_STRINGP(scm_args), scm_args, SCM_ARG2, "scm_register_function"); SCM_ASSERT(SCM_NIMP(scm_help) && SCM_STRINGP(scm_help), scm_help, SCM_ARG3, "scm_register_function"); SCM_ASSERT(scm_procedure_p(scm_function), scm_function, SCM_ARG4, "scm_register_function"); scm_permanent_object(scm_function); /* is this correct? */ fndef = g_new0(FunctionDefinition, 1); fdata = g_new(FuncData, 1); fdata->fndef = fndef; fdata->function = scm_function; funclist = g_list_append(funclist, fdata); fndef->name = g_strdup(SCM_CHARS(scm_name)); fndef->args = g_strdup(SCM_CHARS(scm_args)); fndef->named_arguments = NULL; fndef->help = g_new(char*, 1); *fndef->help = g_strdup(SCM_CHARS(scm_help)); fndef->fn = func_marshal_func; symbol_install(global_symbol_table, fndef->name, SYMBOL_FUNCTION, fndef); return SCM_UNSPECIFIED; } static FunctionDefinition plugin_functions[] = { { "scm_eval", "s", "expr", NULL, NULL, func_scm_eval }, { "scm_apply", 0, "symbol", NULL, func_scm_apply, NULL }, { NULL, NULL, NULL, NULL, NULL, NULL } }; static int no_unloading_for_me (PluginData *pd) { return 0; } int init_plugin (PluginData *pd); int init_plugin (PluginData *pd) { char *init_file_name; install_symbols(plugin_functions); pd->can_unload = no_unloading_for_me; pd->title = g_strdup(_("Guile Plugin")); scm_make_gsubr("cell-value", 1, 0, 0, scm_cell_value); scm_make_gsubr("cell-expr", 1, 0, 0, scm_cell_expr); scm_make_gsubr("set-cell-string!", 2, 0, 0, scm_set_cell_string); scm_make_gsubr("gnumeric-funcall", 2, 0, 0, scm_gnumeric_funcall); scm_make_gsubr("register-function", 4, 0, 0, scm_register_function); init_file_name = gnome_unconditional_datadir_file("gnumeric/guile/gnumeric_startup.scm"); scm_apply(scm_eval_0str("(lambda (filename)" " (if (access? filename R_OK)" " (load filename)" " (display (string-append \"could not read Guile plug-in init file\" filename \"\n\"))))"), scm_cons(scm_makfrom0str(init_file_name), SCM_EOL), SCM_EOL); return 0; }