24 #include <glib/gi18n.h> 32 #include "fin_spl_protos.h" 35 #include "gnc-hooks.h" 36 #include "gnc-exp-parser.h" 38 #include "gnc-locale-utils.h" 39 #include "guile-mappings.h" 41 #define GEP_GROUP_NAME "Variables" 43 static QofLogModule log_module = GNC_MOD_GUI;
54 static GHashTable *variable_bindings = NULL;
55 static ParseError last_error = PARSER_NO_ERROR;
56 static GNCParseError last_gncp_error = NO_ERR;
57 static gboolean parser_inited = FALSE;
63 gnc_exp_parser_filname (
void)
69 gnc_exp_parser_init (
void )
71 gnc_exp_parser_real_init( TRUE );
75 gnc_exp_parser_real_init ( gboolean addPredefined )
77 gchar *filename, **keys, **key, *str_value;
82 gnc_exp_parser_shutdown ();
85 scm_primitive_load_path(scm_from_utf8_string(
"gnucash/app-utils/fin"));
86 variable_bindings = g_hash_table_new (g_str_hash, g_str_equal);
93 filename = gnc_exp_parser_filname();
97 keys = g_key_file_get_keys(key_file, GEP_GROUP_NAME, NULL, NULL);
98 for (key = keys; key && *key; key++)
100 str_value = g_key_file_get_string(key_file, GEP_GROUP_NAME, *key, NULL);
106 g_key_file_free(key_file);
111 gnc_hook_add_dangler(HOOK_SHUTDOWN, (GFunc)gnc_exp_parser_shutdown, NULL, NULL);
115 remove_binding (gpointer key, gpointer value, gpointer not_used)
124 set_one_key (gpointer key, gpointer value, gpointer data)
131 g_key_file_set_string ((GKeyFile *)data, GEP_GROUP_NAME, name, num_str);
136 gnc_exp_parser_shutdown (
void)
144 filename = gnc_exp_parser_filname();
145 key_file = g_key_file_new();
146 g_hash_table_foreach (variable_bindings, set_one_key, key_file);
147 g_key_file_set_comment(key_file, GEP_GROUP_NAME, NULL,
148 " Variables are in the form 'name=value'",
151 g_key_file_free(key_file);
154 g_hash_table_foreach_remove (variable_bindings, remove_binding, NULL);
155 g_hash_table_destroy (variable_bindings);
156 variable_bindings = NULL;
158 last_error = PARSER_NO_ERROR;
159 last_gncp_error = NO_ERR;
161 parser_inited = FALSE;
163 gnc_hook_run(HOOK_SAVE_OPTIONS, NULL);
167 gnc_exp_parser_remove_variable (
const char *variable_name)
175 if (variable_name == NULL)
178 if (g_hash_table_lookup_extended (variable_bindings, variable_name,
181 g_hash_table_remove (variable_bindings, key);
188 gnc_exp_parser_set_value (
const char * variable_name, gnc_numeric value)
193 if (variable_name == NULL)
197 gnc_exp_parser_init ();
199 gnc_exp_parser_remove_variable (variable_name);
201 key = g_strdup (variable_name);
206 g_hash_table_insert (variable_bindings, key, pnum);
210 make_predefined_vars_helper (gpointer key, gpointer value, gpointer data)
212 var_store_ptr *vars_p = data;
222 var->variable_name = g_strdup(key);
224 var->next_var = *vars_p;
230 make_predefined_vars_from_external_helper( gpointer key, gpointer value, gpointer data )
234 pnum->value = *(gnc_numeric*)value;
236 make_predefined_vars_helper( key, pnum, data );
241 make_predefined_variables (
void)
243 var_store_ptr vars = NULL;
245 g_hash_table_foreach (variable_bindings, make_predefined_vars_helper, &vars);
251 free_predefined_variables (var_store_ptr vars)
257 next = vars->next_var;
259 g_free(vars->variable_name);
260 vars->variable_name = NULL;
272 update_variables (var_store_ptr vars)
274 for ( ; vars ; vars = vars->next_var )
278 gnc_exp_parser_set_value (vars->variable_name, pnum->value);
283 _exception_handler(
const char *error_message)
285 PERR(
"function eval error: [%s]\n", error_message);
290 func_op(
const char *fname,
int argc,
void **argv)
292 SCM scmFn, scmArgs, scmTmp;
296 gnc_numeric n, *result;
299 realFnName = g_string_sized_new( strlen(fname) + 5 );
300 g_string_printf( realFnName,
"gnc:%s", fname );
301 scmFn = scm_internal_catch(SCM_BOOL_T,
302 (scm_t_catch_body)scm_c_eval_string, realFnName->str,
303 scm_handle_by_message_noexit, NULL);
304 g_string_free( realFnName, TRUE );
305 if (!scm_is_procedure(scmFn))
308 printf(
"gnc:\"%s\" is not a scm procedure\n", fname );
311 scmArgs = scm_list_n (SCM_UNDEFINED);
312 for ( i = 0; i < argc; i++ )
319 n = *(gnc_numeric*)(vs->value);
323 str = (
char*)(vs->value);
324 scmTmp = scm_from_utf8_string( str );
328 printf(
"argument %d not a numeric or string [type = %d]\n",
333 scmArgs = scm_cons( scmTmp, scmArgs );
336 scmTmp = gfec_apply(scmFn, scmArgs, _exception_handler);
337 if (scmTmp == SCM_UNDEFINED)
340 if (!scm_is_number (scmTmp))
342 PERR(
"function gnc:%s does not return a number", fname);
346 result = g_new0( gnc_numeric, 1 );
352 PERR(
"Attempt to convert %f to GncNumeric Failed: %s",
353 scm_to_double(scmTmp),
359 return (
void*)result;
363 trans_numeric(
const char *digit_str,
371 if (digit_str == NULL)
384 numeric_ops(
char op_sym,
392 if ((left == NULL) || (right == NULL))
395 result = (op_sym == ASN_OP) ? left : g_new0(
ParserNum, 1);
416 result->value = right->value;
424 negate_numeric(
void *value)
438 gnc_ep_tmpvarhash_check_vals( gpointer key, gpointer value, gpointer user_data )
440 gboolean *allVarsHaveValues = (gboolean*)user_data;
441 gnc_numeric *num = (gnc_numeric*)value;
447 gnc_ep_tmpvarhash_clean( gpointer key, gpointer value, gpointer user_data )
451 g_free( (gchar*)key );
455 g_free( (gnc_numeric*)value );
460 gnc_exp_parser_parse(
const char * expression, gnc_numeric *value_p,
463 GHashTable *tmpVarHash;
464 gboolean ret, toRet = TRUE;
465 gboolean allVarsHaveValues = TRUE;
467 tmpVarHash = g_hash_table_new( g_str_hash, g_str_equal );
468 ret = gnc_exp_parser_parse_separate_vars( expression, value_p,
469 error_loc_p, tmpVarHash );
476 g_hash_table_foreach( tmpVarHash,
477 gnc_ep_tmpvarhash_check_vals,
478 &allVarsHaveValues );
479 if ( !allVarsHaveValues )
482 last_gncp_error = VARIABLE_IN_EXP;
486 g_hash_table_foreach( tmpVarHash, gnc_ep_tmpvarhash_clean, NULL );
487 g_hash_table_destroy( tmpVarHash );
493 gnc_exp_parser_parse_separate_vars (
const char * expression,
494 gnc_numeric *value_p,
496 GHashTable *varHash )
505 if (expression == NULL)
509 gnc_exp_parser_real_init ( (varHash == NULL) );
511 result.variable_name = NULL;
513 result.next_var = NULL;
515 vars = make_predefined_variables ();
517 if ( varHash != NULL )
519 g_hash_table_foreach( varHash, make_predefined_vars_from_external_helper, &vars);
522 lc = gnc_localeconv ();
524 pe = init_parser (vars, lc->mon_decimal_point, lc->mon_thousands_sep,
525 trans_numeric, numeric_ops, negate_numeric, g_free,
528 error_loc = parse_string (&result, expression, pe);
532 if (error_loc == NULL)
536 if (error_loc_p != NULL)
537 *error_loc_p = (
char *) expression;
539 last_error = NUMERIC_ERROR;
548 if (!result.variable_name)
552 if (error_loc_p != NULL)
555 last_error = PARSER_NO_ERROR;
560 if (error_loc_p != NULL)
561 *error_loc_p = error_loc;
563 last_error = get_parse_error (pe);
566 if ( varHash != NULL )
568 var_store_ptr newVars;
569 gpointer maybeKey, maybeValue;
570 gnc_numeric *numericValue;
572 newVars = parser_get_vars( pe );
573 for ( ; newVars ; newVars = newVars->next_var )
575 if ( g_hash_table_lookup_extended( varHash, newVars->variable_name,
576 &maybeKey, &maybeValue ) )
578 g_hash_table_remove( varHash, maybeKey );
580 g_free( maybeValue );
582 numericValue = g_new0( gnc_numeric, 1 );
583 *numericValue = ((
ParserNum*)newVars->value)->value;
586 g_hash_table_insert( varHash,
587 g_strdup(newVars->variable_name),
593 update_variables (vars);
596 free_predefined_variables (vars);
600 return last_error == PARSER_NO_ERROR;
604 gnc_exp_parser_error_string (
void)
606 if ( last_error == PARSER_NO_ERROR )
608 switch ( last_gncp_error )
614 case VARIABLE_IN_EXP:
615 return _(
"Illegal variable in expression." );
623 case PARSER_NO_ERROR:
625 case UNBALANCED_PARENS:
626 return _(
"Unbalanced parenthesis");
628 return _(
"Stack overflow");
629 case STACK_UNDERFLOW:
630 return _(
"Stack underflow");
631 case UNDEFINED_CHARACTER:
632 return _(
"Undefined character");
634 return _(
"Not a variable");
636 return _(
"Not a defined function");
637 case PARSER_OUT_OF_MEMORY:
638 return _(
"Out of memory");
640 return _(
"Numeric error");
gboolean xaccParseAmount(const char *in_str, gboolean monetary, gnc_numeric *result, char **endstr)
Parses in_str to obtain a numeric result.
gchar * gnc_build_userdata_path(const gchar *filename)
Make a path to filename in the user's gnucash data directory.
gnc_numeric double_to_gnc_numeric(double n, gint64 denom, gint how)
Convert a floating-point number to a gnc_numeric.
utility functions for the GnuCash UI
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
GKeyFile helper routines.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
Use any denominator which gives an exactly correct ratio of numerator to denominator.
gchar * gnc_numeric_to_string(gnc_numeric n)
Convert to string.
#define PERR(format, args...)
Log a serious error.
gnc_numeric gnc_numeric_reduce(gnc_numeric n)
Return input after reducing it by Greater Common Factor (GCF) elimination.
gboolean gnc_key_file_save_to_file(const gchar *filename, GKeyFile *key_file, GError **error)
Write a key/value file from memory to disk.
gdouble gnc_numeric_to_double(gnc_numeric n)
Convert numeric to floating-point value.
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Multiply a times b, returning the product.
const char * gnc_numeric_errorCode_to_string(GNCNumericErrorCode error_code)
Returns a string representation of the given GNCNumericErrorCode.
Argument is not a valid number.
gnc_numeric gnc_numeric_div(gnc_numeric x, gnc_numeric y, gint64 denom, gint how)
Division.
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a-b.
gnc_numeric gnc_numeric_from_string(const gchar *str)
Read a gnc_numeric from str, skipping any leading whitespace.
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
GNCNumericErrorCode gnc_numeric_check(gnc_numeric a)
Check for error signal in value.
File path resolution utility functions.
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
GKeyFile * gnc_key_file_load_from_file(const gchar *filename, gboolean ignore_error, gboolean return_empty_struct, GError **caller_error)
Open and read a key/value file from disk into memory.
#define GNC_HOW_DENOM_SIGFIGS(n)
Build a 'how' value that will generate a denominator that will keep at least n significant figures in...