mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Generalize xaccParseAmount.
Add a GnuCash-specific interface to the src/calculation expression parser. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@2736 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
@@ -8,7 +8,7 @@
|
|||||||
# This script requires the programs 'makepatch', 'gzip',
|
# This script requires the programs 'makepatch', 'gzip',
|
||||||
# 'diff', and 'uuencode'.
|
# 'diff', and 'uuencode'.
|
||||||
#
|
#
|
||||||
# Author: Dave Peticolas <peticola@cs.ucdavis.edu>
|
# Author: Dave Peticolas <dave@krondo.com>
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ gnucash_SOURCES = \
|
|||||||
Destroy.c \
|
Destroy.c \
|
||||||
EuroUtils.c \
|
EuroUtils.c \
|
||||||
FileDialog.c \
|
FileDialog.c \
|
||||||
Refresh.c
|
Refresh.c \
|
||||||
|
gnc-exp-parser.c
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
AccWindow.h \
|
AccWindow.h \
|
||||||
@@ -55,6 +56,7 @@ noinst_HEADERS = \
|
|||||||
RegWindow.h \
|
RegWindow.h \
|
||||||
SplitLedger.h \
|
SplitLedger.h \
|
||||||
file-history.h \
|
file-history.h \
|
||||||
|
gnc-exp-parser.h \
|
||||||
gnc-ui-common.h \
|
gnc-ui-common.h \
|
||||||
messages.h \
|
messages.h \
|
||||||
messages_i18n.h \
|
messages_i18n.h \
|
||||||
@@ -67,6 +69,7 @@ EXTRA_DIST = \
|
|||||||
CFLAGS = @CFLAGS@ ${GLIB_CFLAGS} ${GNOME_CFLAGS} ${GUILE_COMPILE_ARGS}
|
CFLAGS = @CFLAGS@ ${GLIB_CFLAGS} ${GNOME_CFLAGS} ${GUILE_COMPILE_ARGS}
|
||||||
|
|
||||||
INCLUDES = \
|
INCLUDES = \
|
||||||
|
-I./calculation \
|
||||||
-I./engine \
|
-I./engine \
|
||||||
-I./guile \
|
-I./guile \
|
||||||
-I./register
|
-I./register
|
||||||
|
|||||||
@@ -129,10 +129,10 @@ bin_PROGRAMS = gnucash
|
|||||||
LDADD = gnome/libgncgnome.a register/libgncregister.a register/gnome/libgncregistergnome.a guile/libgncguile.a gnome/libgncgnome.a calculation/libgnccalc.a engine/libgncengine.la @GNOME_LIBS@ @G_WRAP_LINK_ARGS@ @GUILE_LINK_ARGS@ @INTLLIBS@
|
LDADD = gnome/libgncgnome.a register/libgncregister.a register/gnome/libgncregistergnome.a guile/libgncguile.a gnome/libgncgnome.a calculation/libgnccalc.a engine/libgncengine.la @GNOME_LIBS@ @G_WRAP_LINK_ARGS@ @GUILE_LINK_ARGS@ @INTLLIBS@
|
||||||
|
|
||||||
|
|
||||||
gnucash_SOURCES = MultiLedger.c SplitLedger.c Destroy.c EuroUtils.c FileDialog.c Refresh.c
|
gnucash_SOURCES = MultiLedger.c SplitLedger.c Destroy.c EuroUtils.c FileDialog.c Refresh.c gnc-exp-parser.c
|
||||||
|
|
||||||
|
|
||||||
noinst_HEADERS = AccWindow.h AdjBWindow.h Destroy.h EuroUtils.h FileBox.h FileDialog.h MainWindow.h MultiLedger.h RecnWindow.h Refresh.h RegWindow.h SplitLedger.h file-history.h gnc-ui-common.h messages.h messages_i18n.h top-level.h ui-callbacks.h
|
noinst_HEADERS = AccWindow.h AdjBWindow.h Destroy.h EuroUtils.h FileBox.h FileDialog.h MainWindow.h MultiLedger.h RecnWindow.h Refresh.h RegWindow.h SplitLedger.h file-history.h gnc-exp-parser.h gnc-ui-common.h messages.h messages_i18n.h top-level.h ui-callbacks.h
|
||||||
|
|
||||||
|
|
||||||
EXTRA_DIST = .cvsignore
|
EXTRA_DIST = .cvsignore
|
||||||
@@ -140,7 +140,7 @@ EXTRA_DIST = .cvsignore
|
|||||||
|
|
||||||
CFLAGS = @CFLAGS@ ${GLIB_CFLAGS} ${GNOME_CFLAGS} ${GUILE_COMPILE_ARGS}
|
CFLAGS = @CFLAGS@ ${GLIB_CFLAGS} ${GNOME_CFLAGS} ${GUILE_COMPILE_ARGS}
|
||||||
|
|
||||||
INCLUDES = -I./engine -I./guile -I./register
|
INCLUDES = -I./calculation -I./engine -I./guile -I./register
|
||||||
|
|
||||||
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
|
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
|
||||||
CONFIG_HEADER = ../config.h
|
CONFIG_HEADER = ../config.h
|
||||||
@@ -157,7 +157,7 @@ X_LIBS = @X_LIBS@
|
|||||||
X_EXTRA_LIBS = @X_EXTRA_LIBS@
|
X_EXTRA_LIBS = @X_EXTRA_LIBS@
|
||||||
X_PRE_LIBS = @X_PRE_LIBS@
|
X_PRE_LIBS = @X_PRE_LIBS@
|
||||||
gnucash_OBJECTS = MultiLedger.o SplitLedger.o Destroy.o EuroUtils.o \
|
gnucash_OBJECTS = MultiLedger.o SplitLedger.o Destroy.o EuroUtils.o \
|
||||||
FileDialog.o Refresh.o
|
FileDialog.o Refresh.o gnc-exp-parser.o
|
||||||
gnucash_LDADD = $(LDADD)
|
gnucash_LDADD = $(LDADD)
|
||||||
gnucash_DEPENDENCIES = gnome/libgncgnome.a register/libgncregister.a \
|
gnucash_DEPENDENCIES = gnome/libgncgnome.a register/libgncregister.a \
|
||||||
register/gnome/libgncregistergnome.a guile/libgncguile.a \
|
register/gnome/libgncregistergnome.a guile/libgncguile.a \
|
||||||
@@ -177,7 +177,8 @@ DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
|
|||||||
TAR = gtar
|
TAR = gtar
|
||||||
GZIP_ENV = --best
|
GZIP_ENV = --best
|
||||||
DEP_FILES = .deps/Destroy.P .deps/EuroUtils.P .deps/FileDialog.P \
|
DEP_FILES = .deps/Destroy.P .deps/EuroUtils.P .deps/FileDialog.P \
|
||||||
.deps/MultiLedger.P .deps/Refresh.P .deps/SplitLedger.P
|
.deps/MultiLedger.P .deps/Refresh.P .deps/SplitLedger.P \
|
||||||
|
.deps/gnc-exp-parser.P
|
||||||
SOURCES = $(gnucash_SOURCES)
|
SOURCES = $(gnucash_SOURCES)
|
||||||
OBJECTS = $(gnucash_OBJECTS)
|
OBJECTS = $(gnucash_OBJECTS)
|
||||||
|
|
||||||
|
|||||||
@@ -25,16 +25,17 @@
|
|||||||
* Author: Linas Vepstas (linas@linas.org) *
|
* Author: Linas Vepstas (linas@linas.org) *
|
||||||
\********************************************************************/
|
\********************************************************************/
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <locale.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
/* #include <glib.h> */
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "messages.h"
|
#include "messages.h"
|
||||||
#include "gnc-common.h"
|
#include "gnc-common.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@@ -59,6 +60,10 @@ gncLogLevel loglevel[MOD_LAST + 1] =
|
|||||||
GNC_LOG_WARNING, /* QUERY */
|
GNC_LOG_WARNING, /* QUERY */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* This static indicates the debugging module that this .o belongs to. */
|
||||||
|
static short module = MOD_ENGINE;
|
||||||
|
|
||||||
|
|
||||||
/* Set the logging level of the given module. */
|
/* Set the logging level of the given module. */
|
||||||
void
|
void
|
||||||
gnc_set_log_level(gncModuleType module, gncLogLevel level)
|
gnc_set_log_level(gncModuleType module, gncLogLevel level)
|
||||||
@@ -434,10 +439,12 @@ gnc_localeconv()
|
|||||||
|
|
||||||
gnc_lconv_set(&lc.decimal_point, ".");
|
gnc_lconv_set(&lc.decimal_point, ".");
|
||||||
gnc_lconv_set(&lc.thousands_sep, ",");
|
gnc_lconv_set(&lc.thousands_sep, ",");
|
||||||
|
gnc_lconv_set(&lc.grouping, "\003");
|
||||||
gnc_lconv_set(&lc.int_curr_symbol, "USD ");
|
gnc_lconv_set(&lc.int_curr_symbol, "USD ");
|
||||||
gnc_lconv_set(&lc.currency_symbol, CURRENCY_SYMBOL);
|
gnc_lconv_set(&lc.currency_symbol, CURRENCY_SYMBOL);
|
||||||
gnc_lconv_set(&lc.mon_decimal_point, ".");
|
gnc_lconv_set(&lc.mon_decimal_point, ".");
|
||||||
gnc_lconv_set(&lc.mon_thousands_sep, ",");
|
gnc_lconv_set(&lc.mon_thousands_sep, ",");
|
||||||
|
gnc_lconv_set(&lc.mon_grouping, "\003");
|
||||||
gnc_lconv_set(&lc.negative_sign, "-");
|
gnc_lconv_set(&lc.negative_sign, "-");
|
||||||
|
|
||||||
gnc_lconv_set_char(&lc.frac_digits, 2);
|
gnc_lconv_set_char(&lc.frac_digits, 2);
|
||||||
@@ -821,176 +828,427 @@ xaccPrintAmountArgs (double val, gboolean print_currency_symbol,
|
|||||||
* xaccParseAmount *
|
* xaccParseAmount *
|
||||||
* parses amount strings using locale data *
|
* parses amount strings using locale data *
|
||||||
* *
|
* *
|
||||||
* Args: str -- pointer to string rep of num *
|
* Args: in_str -- pointer to string rep of num *
|
||||||
monetary -- boolean indicating whether value is monetary *
|
* monetary -- boolean indicating whether value is monetary *
|
||||||
* Return: double -- the parsed amount *
|
* result -- pointer to result location, may be NULL *
|
||||||
|
* endstr -- used to store first digit not used in parsing *
|
||||||
|
* Return: gboolean -- TRUE if a number found and parsed *
|
||||||
|
* If FALSE, result is not changed *
|
||||||
\********************************************************************/
|
\********************************************************************/
|
||||||
|
|
||||||
double xaccParseAmount (const char * instr, gboolean monetary)
|
/* Parsing state machine states */
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
START_ST, /* Parsing initial whitespace */
|
||||||
|
NEG_ST, /* Parsed a negative sign */
|
||||||
|
PRE_GROUP_ST, /* Parsing digits before grouping and decimal characters */
|
||||||
|
START_GROUP_ST, /* Start of a digit group encountered (possibly) */
|
||||||
|
IN_GROUP_ST, /* Within a digit group */
|
||||||
|
FRAC_ST, /* Parsing the fractional portion of a number */
|
||||||
|
DONE_ST, /* Finished, number is correct module grouping constraints */
|
||||||
|
NO_NUM_ST /* Finished, number was malformed */
|
||||||
|
} ParseState;
|
||||||
|
|
||||||
|
#define done_state(state) (((state) == DONE_ST) || ((state) == NO_NUM_ST))
|
||||||
|
|
||||||
|
G_INLINE_FUNC double
|
||||||
|
fractional_multiplier (int num_decimals)
|
||||||
|
{
|
||||||
|
switch (num_decimals)
|
||||||
|
{
|
||||||
|
case 8:
|
||||||
|
return 0.00000001;
|
||||||
|
case 7:
|
||||||
|
return 0.0000001;
|
||||||
|
case 6:
|
||||||
|
return 0.000001;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
return 0.00001;
|
||||||
|
case 4:
|
||||||
|
return 0.0001;
|
||||||
|
case 3:
|
||||||
|
return 0.001;
|
||||||
|
case 2:
|
||||||
|
return 0.01;
|
||||||
|
case 1:
|
||||||
|
return 0.1;
|
||||||
|
default:
|
||||||
|
PERR("bad fraction length");
|
||||||
|
g_assert_not_reached();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
xaccParseAmount (const char * in_str, gboolean monetary, double *result,
|
||||||
|
char **endstr)
|
||||||
{
|
{
|
||||||
struct lconv *lc = gnc_localeconv();
|
struct lconv *lc = gnc_localeconv();
|
||||||
gboolean isneg = FALSE;
|
gboolean is_negative;
|
||||||
char *mstr, *str, *tok;
|
gboolean got_decimal;
|
||||||
double amount = 0.0;
|
GList *group_data;
|
||||||
|
int group_count;
|
||||||
|
double value;
|
||||||
|
|
||||||
|
ParseState state;
|
||||||
|
|
||||||
char negative_sign;
|
char negative_sign;
|
||||||
char thousands_sep;
|
|
||||||
char decimal_point;
|
char decimal_point;
|
||||||
int len, i;
|
char group_separator;
|
||||||
|
const char *in;
|
||||||
|
char *out_str;
|
||||||
|
char *out;
|
||||||
|
|
||||||
if (!instr) return 0.0;
|
/* Initialize *endstr to in_str */
|
||||||
if (*instr == '\0') return 0.0;
|
if (endstr != NULL)
|
||||||
|
*endstr = (char *) in_str;
|
||||||
|
|
||||||
mstr = strdup (instr);
|
if (in_str == NULL)
|
||||||
str = mstr;
|
return FALSE;
|
||||||
|
|
||||||
negative_sign = lc->negative_sign[0];
|
negative_sign = lc->negative_sign[0];
|
||||||
if (monetary)
|
if (monetary)
|
||||||
{
|
{
|
||||||
thousands_sep = lc->mon_thousands_sep[0];
|
group_separator = lc->mon_thousands_sep[0];
|
||||||
decimal_point = lc->mon_decimal_point[0];
|
decimal_point = lc->mon_decimal_point[0];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
thousands_sep = lc->thousands_sep[0];
|
group_separator = lc->thousands_sep[0];
|
||||||
decimal_point = lc->decimal_point[0];
|
decimal_point = lc->decimal_point[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* strip off garbage at the beginning of the line */
|
/* 'out_str' will be used to store digits for numeric conversion.
|
||||||
while (*str != '\0')
|
* 'out' will be used to traverse out_str. */
|
||||||
|
out = out_str = g_new(char, strlen(in_str) + 1);
|
||||||
|
|
||||||
|
/* 'in' is used to traverse 'in_str'. */
|
||||||
|
in = in_str;
|
||||||
|
|
||||||
|
is_negative = FALSE;
|
||||||
|
got_decimal = FALSE;
|
||||||
|
group_data = NULL;
|
||||||
|
group_count = 0;
|
||||||
|
value = 0.0;
|
||||||
|
|
||||||
|
/* Initialize the state machine */
|
||||||
|
state = START_ST;
|
||||||
|
|
||||||
|
/* This while loop implements a state machine for parsing numbers. */
|
||||||
|
while (TRUE)
|
||||||
{
|
{
|
||||||
switch (*str)
|
ParseState next_state = state;
|
||||||
|
|
||||||
|
/* Note we never need to check for then end of 'in_str' explicitly.
|
||||||
|
* The 'else' clauses on all the state transitions will handle that. */
|
||||||
|
switch (state)
|
||||||
{
|
{
|
||||||
case '\r':
|
/* START_ST means we have parsed 0 or more whitespace characters */
|
||||||
case '\n':
|
case START_ST:
|
||||||
case ' ':
|
if (isdigit(*in))
|
||||||
case '\t':
|
{
|
||||||
str++;
|
*out++ = *in; /* we record the digits themselves in out_str
|
||||||
continue;
|
* for later conversion by libc routines */
|
||||||
break;
|
next_state = PRE_GROUP_ST;
|
||||||
|
}
|
||||||
|
else if (isspace(*in))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else if (*in == negative_sign)
|
||||||
|
{
|
||||||
|
is_negative = TRUE;
|
||||||
|
next_state = NEG_ST;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next_state = NO_NUM_ST;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
/* look for a negative sign */
|
/* NEG_ST means we have just parsed a negative sign. For now,
|
||||||
if (*str == negative_sign) {
|
* we only recognize formats where the negative sign comes first. */
|
||||||
isneg = TRUE;
|
case NEG_ST:
|
||||||
str++;
|
if (isdigit(*in))
|
||||||
}
|
|
||||||
|
|
||||||
if (*str == '\0') return 0.0;
|
|
||||||
|
|
||||||
/* go to end of string */
|
|
||||||
for (tok = str; *tok != '\0'; tok++)
|
|
||||||
;
|
|
||||||
|
|
||||||
/* strip off garbage at end of the line */
|
|
||||||
while (--tok != str)
|
|
||||||
{
|
{
|
||||||
switch (*tok)
|
*out++ = *in;
|
||||||
|
next_state = PRE_GROUP_ST;
|
||||||
|
}
|
||||||
|
else if (isspace(*in))
|
||||||
{
|
{
|
||||||
case '\r':
|
}
|
||||||
case '\n':
|
else
|
||||||
case ' ':
|
{
|
||||||
case '\t':
|
next_state = NO_NUM_ST;
|
||||||
continue;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/* PRE_GROUP_ST means we have started parsing the number, but
|
||||||
|
* have not encountered a decimal point or a grouping character. */
|
||||||
|
case PRE_GROUP_ST:
|
||||||
|
if (isdigit(*in))
|
||||||
|
{
|
||||||
|
*out++ = *in;
|
||||||
|
}
|
||||||
|
else if (*in == decimal_point)
|
||||||
|
{
|
||||||
|
next_state = FRAC_ST;
|
||||||
|
}
|
||||||
|
else if (*in == group_separator)
|
||||||
|
{
|
||||||
|
next_state = START_GROUP_ST;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next_state = DONE_ST;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* look for a negative sign at the end, some locales allow it,
|
break;
|
||||||
* we'll just allow it everywhere. */
|
|
||||||
if (*tok == negative_sign) {
|
/* START_GROUP_ST means we have just parsed a group character.
|
||||||
isneg = TRUE;
|
* Note that group characters might be whitespace!!! In general,
|
||||||
*tok = '\0';
|
* if a decimal point or a group character is whitespace, we
|
||||||
|
* try to interpret it in the fashion that will allow parsing
|
||||||
|
* of the current number to continue. */
|
||||||
|
case START_GROUP_ST:
|
||||||
|
if (isdigit(*in))
|
||||||
|
{
|
||||||
|
*out++ = *in;
|
||||||
|
group_count++; /* We record the number of digits
|
||||||
|
* in the group for later checking. */
|
||||||
|
next_state = IN_GROUP_ST;
|
||||||
|
}
|
||||||
|
else if (*in == decimal_point)
|
||||||
|
{
|
||||||
|
/* If we now get a decimal point, and both the decimal
|
||||||
|
* and the group separator are also whitespace, assume
|
||||||
|
* the last group separator was actually whitespace and
|
||||||
|
* stop parsing. Otherwise, there's a problem. */
|
||||||
|
if (isspace(group_separator) && isspace(decimal_point))
|
||||||
|
next_state = DONE_ST;
|
||||||
|
else
|
||||||
|
next_state = NO_NUM_ST;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* If the last group separator is also whitespace,
|
||||||
|
* assume it was intended as such and stop parsing.
|
||||||
|
* Otherwise, there is a problem. */
|
||||||
|
if (isspace(group_separator))
|
||||||
|
next_state = DONE_ST;
|
||||||
|
else
|
||||||
|
next_state = NO_NUM_ST;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*str == '\0') return 0.0;
|
break;
|
||||||
|
|
||||||
/* remove thousands separator */
|
/* IN_GROUP_ST means we are in the middle of parsing
|
||||||
tok = strchr (str, thousands_sep);
|
* a group of digits. */
|
||||||
while (tok) {
|
case IN_GROUP_ST:
|
||||||
*tok = '\0';
|
if (isdigit(*in))
|
||||||
amount *= 1000.0;
|
{
|
||||||
amount += ((double) (1000 * atoi (str)));
|
*out++ = *in;
|
||||||
str = tok + sizeof(char);
|
group_count++; /* We record the number of digits
|
||||||
tok = strchr (str, thousands_sep);
|
* in the group for later checking. */
|
||||||
|
}
|
||||||
|
else if (*in == decimal_point)
|
||||||
|
{
|
||||||
|
next_state = FRAC_ST;
|
||||||
|
}
|
||||||
|
else if (*in == group_separator)
|
||||||
|
{
|
||||||
|
next_state = START_GROUP_ST;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next_state = DONE_ST;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* search for a decimal point */
|
break;
|
||||||
tok = strchr (str, decimal_point);
|
|
||||||
if (tok) {
|
|
||||||
*tok = '\0';
|
|
||||||
amount += ((double) (atoi (str)));
|
|
||||||
str = tok + sizeof(char);
|
|
||||||
|
|
||||||
/* if there is anything trailing the decimal
|
/* FRAC_ST means we are now parsing fractional digits. */
|
||||||
* point, convert it */
|
case FRAC_ST:
|
||||||
if (str[0]) {
|
if (isdigit(*in))
|
||||||
|
{
|
||||||
|
*out++ = *in;
|
||||||
|
}
|
||||||
|
else if (*in == decimal_point)
|
||||||
|
{
|
||||||
|
/* If a subsequent decimal point is also whitespace,
|
||||||
|
* assume it was intended as such and stop parsing.
|
||||||
|
* Otherwise, there is a problem. */
|
||||||
|
if (isspace(decimal_point))
|
||||||
|
next_state = DONE_ST;
|
||||||
|
else
|
||||||
|
next_state = NO_NUM_ST;
|
||||||
|
}
|
||||||
|
else if (*in == group_separator)
|
||||||
|
{
|
||||||
|
/* If a subsequent group separator is also whitespace,
|
||||||
|
* assume it was intended as such and stop parsing.
|
||||||
|
* Otherwise, there is a problem. */
|
||||||
|
if (isspace(group_separator))
|
||||||
|
next_state = DONE_ST;
|
||||||
|
else
|
||||||
|
next_state = NO_NUM_ST;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next_state = DONE_ST;
|
||||||
|
}
|
||||||
|
|
||||||
/* strip off garbage at end of the line */
|
break;
|
||||||
tok = strchr (str, ' ');
|
|
||||||
if (tok) *tok = '\0';
|
|
||||||
|
|
||||||
/* adjust for number of decimal places */
|
default:
|
||||||
len = strlen(str);
|
PERR("bad state");
|
||||||
|
g_assert_not_reached();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we're moving out of the IN_GROUP_ST, record data for the group */
|
||||||
|
if ((state == IN_GROUP_ST) && (next_state != IN_GROUP_ST))
|
||||||
|
{
|
||||||
|
group_data = g_list_prepend(group_data, GINT_TO_POINTER(group_count));
|
||||||
|
group_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we're moving into the FRAC_ST or out of the machine
|
||||||
|
* without going through FRAC_ST, record the integral value. */
|
||||||
|
if (((next_state == FRAC_ST) && (state != FRAC_ST)) ||
|
||||||
|
((next_state == DONE_ST) && !got_decimal))
|
||||||
|
{
|
||||||
|
*out = '\0';
|
||||||
|
value = strtod(out_str, NULL);
|
||||||
|
|
||||||
|
if (value == HUGE_VAL)
|
||||||
|
{
|
||||||
|
next_state = NO_NUM_ST;
|
||||||
|
}
|
||||||
|
else if (next_state == FRAC_ST)
|
||||||
|
{
|
||||||
|
/* reset the out pointer to record the fraction */
|
||||||
|
out = out_str;
|
||||||
|
*out = '\0';
|
||||||
|
|
||||||
|
got_decimal = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state = next_state;
|
||||||
|
if (done_state (state))
|
||||||
|
break;
|
||||||
|
|
||||||
|
in++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there was an error, just quit */
|
||||||
|
if (state == NO_NUM_ST)
|
||||||
|
{
|
||||||
|
g_free(out_str);
|
||||||
|
g_list_free(group_data);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there were groups, validate them */
|
||||||
|
if (group_data != NULL)
|
||||||
|
{
|
||||||
|
gboolean good_grouping = TRUE;
|
||||||
|
GList *node;
|
||||||
|
char *group;
|
||||||
|
|
||||||
|
group = monetary ? lc->mon_grouping : lc->grouping;
|
||||||
|
|
||||||
|
/* The groups were built in reverse order. This
|
||||||
|
* is the easiest order to verify them in. */
|
||||||
|
for (node = group_data; node; node = node->next)
|
||||||
|
{
|
||||||
|
/* Verify group size */
|
||||||
|
if (*group != GPOINTER_TO_INT(node->data))
|
||||||
|
{
|
||||||
|
good_grouping = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Peek ahead at the next group code */
|
||||||
|
switch (group[1])
|
||||||
|
{
|
||||||
|
/* A null char means repeat the last group indefinitely */
|
||||||
|
case '\0':
|
||||||
|
break;
|
||||||
|
/* CHAR_MAX means no more grouping allowed */
|
||||||
|
case CHAR_MAX:
|
||||||
|
if (node->next != NULL)
|
||||||
|
good_grouping = FALSE;
|
||||||
|
break;
|
||||||
|
/* Anything else means another group size */
|
||||||
|
default:
|
||||||
|
group++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!good_grouping)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_list_free(group_data);
|
||||||
|
|
||||||
|
if (!good_grouping)
|
||||||
|
{
|
||||||
|
g_free(out_str);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cap the end of the fraction string, if any */
|
||||||
|
*out = '\0';
|
||||||
|
|
||||||
|
/* Add in fractional value */
|
||||||
|
if (got_decimal && (*out_str != '\0'))
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = strlen(out_str);
|
||||||
|
|
||||||
if (len > 8)
|
if (len > 8)
|
||||||
{
|
{
|
||||||
str[8] = '\0';
|
out_str[8] = '\0';
|
||||||
len = 8;
|
len = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (8 == len) {
|
value += fractional_multiplier(len) * strtod(out_str, NULL);
|
||||||
amount += 0.00000001 * ((double) atoi (str));
|
|
||||||
} else
|
if (value == HUGE_VAL)
|
||||||
if (7 == len) {
|
{
|
||||||
amount += 0.0000001 * ((double) atoi (str));
|
g_free(out_str);
|
||||||
} else
|
return FALSE;
|
||||||
if (6 == len) {
|
|
||||||
amount += 0.000001 * ((double) atoi (str));
|
|
||||||
} else
|
|
||||||
if (5 == len) {
|
|
||||||
amount += 0.00001 * ((double) atoi (str));
|
|
||||||
} else
|
|
||||||
if (4 == len) {
|
|
||||||
amount += 0.0001 * ((double) atoi (str));
|
|
||||||
} else
|
|
||||||
if (3 == len) {
|
|
||||||
amount += 0.001 * ((double) atoi (str));
|
|
||||||
} else
|
|
||||||
if (2 == len) {
|
|
||||||
amount += 0.01 * ((double) atoi (str));
|
|
||||||
} else
|
|
||||||
if (1 == len) {
|
|
||||||
amount += 0.1 * ((double) atoi (str));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (auto_decimal_enabled && !got_decimal)
|
||||||
} else if( auto_decimal_enabled ) {
|
{
|
||||||
/* No decimal point and auto decimal point enabled, so assume that
|
/* No decimal point and auto decimal point enabled, so assume
|
||||||
* the value is an integer number of cents or a cent-type unit.
|
* that the value is an integer number of cents or a cent-type
|
||||||
* For each auto decimal place requested, move the final decimal
|
* unit. For each auto decimal place requested, move the final
|
||||||
* point one place to the left.
|
* decimal point one place to the left. */
|
||||||
*/
|
if ((auto_decimal_places > 0) && (auto_decimal_places < 9))
|
||||||
amount += ((double) (atoi (str)));
|
value *= fractional_multiplier(auto_decimal_places);
|
||||||
for( i = 0; i < auto_decimal_places; i++ )
|
|
||||||
amount *= 0.1;
|
|
||||||
|
|
||||||
/* NOTE: further additions to amount after this point will
|
|
||||||
* generate incorrect results.
|
|
||||||
*/
|
|
||||||
|
|
||||||
} else {
|
|
||||||
amount += ((double) (atoi (str)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isneg) amount = -amount;
|
if (is_negative)
|
||||||
|
value = -value;
|
||||||
|
|
||||||
free (mstr);
|
if (result != NULL)
|
||||||
return amount;
|
*result = value;
|
||||||
|
|
||||||
|
if (endstr != NULL)
|
||||||
|
*endstr = (char *) in;
|
||||||
|
|
||||||
|
g_free (out_str);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -180,7 +180,6 @@ extern char *strcasestr(const char *str1, const char *str2);
|
|||||||
* An alernate (better??) implementation might be
|
* An alernate (better??) implementation might be
|
||||||
* #define strpskip(s,r) (s+strspn(s,r))
|
* #define strpskip(s,r) (s+strspn(s,r))
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extern char * strpskip (const char * s, const char *reject);
|
extern char * strpskip (const char * s, const char *reject);
|
||||||
|
|
||||||
/********************************************************/
|
/********************************************************/
|
||||||
@@ -188,7 +187,6 @@ extern char * strpskip (const char * s, const char *reject);
|
|||||||
* It accepts a number and prints it in the indicated base.
|
* It accepts a number and prints it in the indicated base.
|
||||||
* The returned string should be freed when done.
|
* The returned string should be freed when done.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
char * ultostr (unsigned long val, int base);
|
char * ultostr (unsigned long val, int base);
|
||||||
|
|
||||||
/* Returns true if string s is a number, possibly
|
/* Returns true if string s is a number, possibly
|
||||||
@@ -268,8 +266,21 @@ const char * xaccPrintAmountArgs (double val,
|
|||||||
gboolean is_shares_value,
|
gboolean is_shares_value,
|
||||||
const char *curr_code);
|
const char *curr_code);
|
||||||
|
|
||||||
/* Parse i18n amount strings */
|
|
||||||
double xaccParseAmount (const char * instr, gboolean monetary);
|
/* xaccParseAmount parses in_str to obtain a numeric result. The
|
||||||
|
* routine will parse as much of in_str as it can to obtain a single
|
||||||
|
* number. The number is parsed using the current locale information
|
||||||
|
* and the 'monetary' flag. The routine will return TRUE if it
|
||||||
|
* successfully parsed a number and FALSE otherwise. If TRUE is
|
||||||
|
* returned and result is non-NULL, the value of the parsed number
|
||||||
|
* is stored in *result. If FALSE is returned, *result is
|
||||||
|
* unchanged. If TRUE is returned and endstr is non-NULL, the
|
||||||
|
* location of the first character in in_str not used by the
|
||||||
|
* parser will be returned in *endstr. If FALSE is returned
|
||||||
|
* and endstr is non-NULL, *endstr will point to in_str.
|
||||||
|
*/
|
||||||
|
gboolean xaccParseAmount (const char * in_str, gboolean monetary,
|
||||||
|
double *result, char **endstr);
|
||||||
|
|
||||||
|
|
||||||
/** TEMPLATES ******************************************************/
|
/** TEMPLATES ******************************************************/
|
||||||
|
|||||||
336
src/gnc-exp-parser.c
Normal file
336
src/gnc-exp-parser.c
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
/********************************************************************\
|
||||||
|
* gnc-exp-parser.c -- Implementation of expression parsing for *
|
||||||
|
* GnuCash using the routines in 'calculation'. *
|
||||||
|
* Copyright (C) 2000 Dave Peticolas <dave@krondo.com> *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License as *
|
||||||
|
* published by the Free Software Foundation; either version 2 of *
|
||||||
|
* the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program 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 General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License*
|
||||||
|
* along with this program; if not, write to the Free Software *
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
|
||||||
|
\********************************************************************/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "finproto.h"
|
||||||
|
#include "fin_spl_protos.h"
|
||||||
|
#include "gnc-exp-parser.h"
|
||||||
|
#include "messages.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
/** Data Types *****************************************************/
|
||||||
|
|
||||||
|
typedef struct ParserNum
|
||||||
|
{
|
||||||
|
double value;
|
||||||
|
} ParserNum;
|
||||||
|
|
||||||
|
|
||||||
|
/** Static Globals *************************************************/
|
||||||
|
static GHashTable *variable_bindings = NULL;
|
||||||
|
static ParseError last_error = PARSER_NO_ERROR;
|
||||||
|
static gboolean parser_inited = FALSE;
|
||||||
|
|
||||||
|
|
||||||
|
/** Implementations ************************************************/
|
||||||
|
|
||||||
|
void
|
||||||
|
gnc_exp_parser_init (void)
|
||||||
|
{
|
||||||
|
if (parser_inited)
|
||||||
|
gnc_exp_parser_shutdown ();
|
||||||
|
|
||||||
|
variable_bindings = g_hash_table_new (g_str_hash, g_str_equal);
|
||||||
|
|
||||||
|
parser_inited = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
remove_binding (gpointer key, gpointer value, gpointer not_used)
|
||||||
|
{
|
||||||
|
g_free(key);
|
||||||
|
g_free(value);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gnc_exp_parser_shutdown (void)
|
||||||
|
{
|
||||||
|
if (!parser_inited)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_hash_table_foreach_remove (variable_bindings, remove_binding, NULL);
|
||||||
|
g_hash_table_destroy (variable_bindings);
|
||||||
|
variable_bindings = NULL;
|
||||||
|
|
||||||
|
last_error = PARSER_NO_ERROR;
|
||||||
|
|
||||||
|
parser_inited = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
prepend_name (gpointer key, gpointer value, gpointer data)
|
||||||
|
{
|
||||||
|
GList **list = data;
|
||||||
|
|
||||||
|
*list = g_list_prepend (*list, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
GList *
|
||||||
|
gnc_exp_parser_get_variable_names (void)
|
||||||
|
{
|
||||||
|
GList *names = NULL;
|
||||||
|
|
||||||
|
if (!parser_inited)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
g_hash_table_foreach (variable_bindings, prepend_name, &names);
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gnc_exp_parser_remove_variable (const char *variable_name)
|
||||||
|
{
|
||||||
|
gpointer key;
|
||||||
|
gpointer value;
|
||||||
|
|
||||||
|
if (!parser_inited)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (variable_name == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (g_hash_table_lookup_extended (variable_bindings, variable_name,
|
||||||
|
&key, &value))
|
||||||
|
{
|
||||||
|
g_hash_table_remove (variable_bindings, key);
|
||||||
|
g_free(key);
|
||||||
|
g_free(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gnc_exp_parser_remove_variable_names (GList * variable_names)
|
||||||
|
{
|
||||||
|
if (!parser_inited)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (variable_names != NULL)
|
||||||
|
{
|
||||||
|
gnc_exp_parser_remove_variable (variable_names->data);
|
||||||
|
variable_names = variable_names->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gnc_exp_parser_get_value (const char * variable_name, double *value_p)
|
||||||
|
{
|
||||||
|
ParserNum *pnum;
|
||||||
|
|
||||||
|
if (!parser_inited)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (variable_name == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
pnum = g_hash_table_lookup (variable_bindings, variable_name);
|
||||||
|
if (pnum == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (value_p != NULL)
|
||||||
|
*value_p = pnum->value;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gnc_exp_parser_set_value (const char * variable_name, double value)
|
||||||
|
{
|
||||||
|
char *key;
|
||||||
|
ParserNum *pnum;
|
||||||
|
|
||||||
|
if (variable_name == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!parser_inited)
|
||||||
|
gnc_exp_parser_init ();
|
||||||
|
|
||||||
|
gnc_exp_parser_remove_variable (variable_name);
|
||||||
|
|
||||||
|
key = g_strdup (variable_name);
|
||||||
|
|
||||||
|
pnum = g_new(ParserNum, 1);
|
||||||
|
pnum->value = value;
|
||||||
|
|
||||||
|
g_hash_table_insert (variable_bindings, key, pnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
make_predefined_vars_helper (gpointer key, gpointer value, gpointer data)
|
||||||
|
{
|
||||||
|
var_store_ptr *vars_p = data;
|
||||||
|
var_store_ptr var;
|
||||||
|
|
||||||
|
var = g_new0 (var_store, 1);
|
||||||
|
|
||||||
|
var->variable_name = key;
|
||||||
|
var->value = value;
|
||||||
|
var->next_var = *vars_p;
|
||||||
|
|
||||||
|
*vars_p = var;
|
||||||
|
}
|
||||||
|
|
||||||
|
static var_store_ptr
|
||||||
|
make_predefined_variables (void)
|
||||||
|
{
|
||||||
|
var_store_ptr vars = NULL;
|
||||||
|
|
||||||
|
g_hash_table_foreach (variable_bindings, make_predefined_vars_helper, &vars);
|
||||||
|
|
||||||
|
return vars;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
trans_numeric(const char *digit_str,
|
||||||
|
char radix_point,
|
||||||
|
char group_char,
|
||||||
|
char **rstr)
|
||||||
|
{
|
||||||
|
ParserNum *pnum;
|
||||||
|
double value;
|
||||||
|
|
||||||
|
if (digit_str == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!xaccParseAmount (digit_str, TRUE, &value, rstr))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
pnum = g_new0(ParserNum, 1);
|
||||||
|
pnum->value = value;
|
||||||
|
|
||||||
|
return pnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
numeric_ops(char op_sym,
|
||||||
|
void *left_value,
|
||||||
|
void *right_value)
|
||||||
|
{
|
||||||
|
ParserNum *left = left_value;
|
||||||
|
ParserNum *right = right_value;
|
||||||
|
ParserNum *result;
|
||||||
|
|
||||||
|
if ((left == NULL) || (right == NULL))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
result = (op_sym == ASN_OP) ? left : g_new(ParserNum, 1);
|
||||||
|
|
||||||
|
switch (op_sym)
|
||||||
|
{
|
||||||
|
case ADD_OP:
|
||||||
|
result->value = left->value + right->value;
|
||||||
|
break;
|
||||||
|
case SUB_OP:
|
||||||
|
result->value = left->value - right->value;
|
||||||
|
break;
|
||||||
|
case DIV_OP:
|
||||||
|
result->value = left->value / right->value;
|
||||||
|
break;
|
||||||
|
case MUL_OP:
|
||||||
|
result->value = left->value * right->value;
|
||||||
|
break;
|
||||||
|
case ASN_OP:
|
||||||
|
result->value = right->value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
negate_numeric(void *value)
|
||||||
|
{
|
||||||
|
ParserNum *result = value;
|
||||||
|
|
||||||
|
if (value == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
result->value = -result->value;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gnc_exp_parser_parse (const char * expression, double *value_p,
|
||||||
|
char **error_loc_p)
|
||||||
|
{
|
||||||
|
parser_env_ptr pe;
|
||||||
|
var_store_ptr vars;
|
||||||
|
struct lconv *lc;
|
||||||
|
var_store result;
|
||||||
|
char * error_loc;
|
||||||
|
ParserNum *pnum;
|
||||||
|
|
||||||
|
if (expression == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!parser_inited)
|
||||||
|
gnc_exp_parser_init ();
|
||||||
|
|
||||||
|
result.variable_name = NULL;
|
||||||
|
result.value = NULL;
|
||||||
|
result.next_var = NULL;
|
||||||
|
|
||||||
|
vars = make_predefined_variables ();
|
||||||
|
lc = gnc_localeconv ();
|
||||||
|
|
||||||
|
pe = init_parser (vars, *lc->mon_decimal_point, *lc->mon_thousands_sep,
|
||||||
|
trans_numeric, numeric_ops, negate_numeric, g_free);
|
||||||
|
|
||||||
|
error_loc = parse_string (&result, expression, pe);
|
||||||
|
|
||||||
|
pnum = result.value;
|
||||||
|
|
||||||
|
if (error_loc == NULL)
|
||||||
|
{
|
||||||
|
if ((value_p != NULL) && (pnum != NULL))
|
||||||
|
*value_p = pnum->value;
|
||||||
|
|
||||||
|
if (error_loc_p != NULL)
|
||||||
|
*error_loc_p = NULL;
|
||||||
|
|
||||||
|
last_error = PARSER_NO_ERROR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (error_loc_p != NULL)
|
||||||
|
*error_loc_p = error_loc;
|
||||||
|
|
||||||
|
last_error = get_parse_error (pe);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fixme: update variables and free predefined variables */
|
||||||
|
|
||||||
|
exit_parser (pe);
|
||||||
|
|
||||||
|
return (error_loc == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
gnc_exp_parser_error_string (void)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
71
src/gnc-exp-parser.h
Normal file
71
src/gnc-exp-parser.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/********************************************************************\
|
||||||
|
* gnc-exp-parser.h -- Interface to expression parsing for GnuCash *
|
||||||
|
* Copyright (C) 2000 Dave Peticolas <dave@krondo.com> *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License as *
|
||||||
|
* published by the Free Software Foundation; either version 2 of *
|
||||||
|
* the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program 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 General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License*
|
||||||
|
* along with this program; if not, write to the Free Software *
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
|
||||||
|
\********************************************************************/
|
||||||
|
|
||||||
|
#ifndef __GNC_EXP_PARSER_H__
|
||||||
|
#define __GNC_EXP_PARSER_H__
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* Initialize the expression parser. If this function is not
|
||||||
|
* called before one of the other parsing routines (other than
|
||||||
|
* gnc_exp_parser_shutdown), it will be called if needed. */
|
||||||
|
void gnc_exp_parser_init (void);
|
||||||
|
|
||||||
|
/* Shutdown the expression parser and free any associated memory. */
|
||||||
|
void gnc_exp_parser_shutdown (void);
|
||||||
|
|
||||||
|
/* Return a list of variable names which are currently defined
|
||||||
|
* in the parser. The names should not be modified or freed. */
|
||||||
|
GList * gnc_exp_parser_get_variable_names (void);
|
||||||
|
|
||||||
|
/* Undefine the variable name if it is already defined. */
|
||||||
|
void gnc_exp_parser_remove_variable (const char *variable_name);
|
||||||
|
|
||||||
|
/* Undefine every variable name appearing in the list. Variables in
|
||||||
|
* the list which are not defined are ignored. */
|
||||||
|
void gnc_exp_parser_remove_variable_names (GList * variable_names);
|
||||||
|
|
||||||
|
/* Return TRUE if the variable is defined, FALSE otherwise. If defined
|
||||||
|
* and value_p != NULL, return the value in *value_p, otherwise, *value_p
|
||||||
|
* is unchanged. */
|
||||||
|
gboolean gnc_exp_parser_get_value (const char * variable_name,
|
||||||
|
double *value_p);
|
||||||
|
|
||||||
|
/* Set the value of the variable to the given value. If the variable is
|
||||||
|
* not already defined, it will be after the call. */
|
||||||
|
void gnc_exp_parser_set_value (const char * variable_name, double value);
|
||||||
|
|
||||||
|
/* Parse the given expression using the current variable definitions.
|
||||||
|
* If the parse was successful, return TRUE and, if value_p is
|
||||||
|
* non-NULL, return the value of the resulting expression in *value_p.
|
||||||
|
* Otherwise, return FALSE and *value_p is unchanged. If FALSE is
|
||||||
|
* returned and error_loc_p is non-NULL, *error_loc_p is set to the
|
||||||
|
* character in expression where parsing aborted. If TRUE is returned
|
||||||
|
* and error_loc_p is non-NULL, *error_loc_p is set to NULL. */
|
||||||
|
gboolean gnc_exp_parser_parse (const char * expression, double *value_p,
|
||||||
|
char **error_loc_p);
|
||||||
|
|
||||||
|
/* If the last parse returned FALSE, return an error string describing
|
||||||
|
* the problem. Otherwise, return NULL. */
|
||||||
|
const char * gnc_exp_parser_error_string (void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -864,7 +864,8 @@ subentry_amount_entry_focus_out(GtkWidget *widget, GdkEventFocus *event,
|
|||||||
if ((string == NULL) || (*string == '\0'))
|
if ((string == NULL) || (*string == '\0'))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
value = xaccParseAmount(string, TRUE);
|
value = 0.0;
|
||||||
|
xaccParseAmount(string, TRUE, &value, NULL);
|
||||||
|
|
||||||
new_string = xaccPrintAmount(value, PRTSEP, NULL);
|
new_string = xaccPrintAmount(value, PRTSEP, NULL);
|
||||||
|
|
||||||
|
|||||||
@@ -170,16 +170,17 @@ gui_to_fi(FinCalcDialog *fcd)
|
|||||||
fcd->financial_info.npp = strtol(string, NULL, 10);
|
fcd->financial_info.npp = strtol(string, NULL, 10);
|
||||||
|
|
||||||
string = gtk_entry_get_text(GTK_ENTRY(fcd->entries[INTEREST_RATE]));
|
string = gtk_entry_get_text(GTK_ENTRY(fcd->entries[INTEREST_RATE]));
|
||||||
fcd->financial_info.ir = xaccParseAmount(string, FALSE);
|
xaccParseAmount(string, FALSE, &fcd->financial_info.ir, NULL);
|
||||||
|
|
||||||
string = gtk_entry_get_text(GTK_ENTRY(fcd->entries[PRESENT_VALUE]));
|
string = gtk_entry_get_text(GTK_ENTRY(fcd->entries[PRESENT_VALUE]));
|
||||||
fcd->financial_info.pv = xaccParseAmount(string, TRUE);
|
xaccParseAmount(string, TRUE, &fcd->financial_info.pv, NULL);
|
||||||
|
|
||||||
string = gtk_entry_get_text(GTK_ENTRY(fcd->entries[PERIODIC_PAYMENT]));
|
string = gtk_entry_get_text(GTK_ENTRY(fcd->entries[PERIODIC_PAYMENT]));
|
||||||
fcd->financial_info.pmt = xaccParseAmount(string, TRUE);
|
xaccParseAmount(string, TRUE, &fcd->financial_info.pmt, NULL);
|
||||||
|
|
||||||
string = gtk_entry_get_text(GTK_ENTRY(fcd->entries[FUTURE_VALUE]));
|
string = gtk_entry_get_text(GTK_ENTRY(fcd->entries[FUTURE_VALUE]));
|
||||||
fcd->financial_info.fv = -xaccParseAmount(string, TRUE);
|
if (xaccParseAmount(string, TRUE, &fcd->financial_info.fv, NULL))
|
||||||
|
fcd->financial_info.fv = -fcd->financial_info.fv;
|
||||||
|
|
||||||
i = gnc_option_menu_get_active(fcd->compounding_menu);
|
i = gnc_option_menu_get_active(fcd->compounding_menu);
|
||||||
fcd->financial_info.CF = periods[i];
|
fcd->financial_info.CF = periods[i];
|
||||||
@@ -327,7 +328,8 @@ can_calc_value(FinCalcDialog *fcd, FinCalcValue value)
|
|||||||
case PERIODIC_PAYMENT:
|
case PERIODIC_PAYMENT:
|
||||||
case FUTURE_VALUE:
|
case FUTURE_VALUE:
|
||||||
string = gtk_entry_get_text(GTK_ENTRY(fcd->entries[INTEREST_RATE]));
|
string = gtk_entry_get_text(GTK_ENTRY(fcd->entries[INTEREST_RATE]));
|
||||||
dvalue = xaccParseAmount(string, FALSE);
|
dvalue = 0.0;
|
||||||
|
xaccParseAmount(string, FALSE, &dvalue, NULL);
|
||||||
if (DEQ(dvalue, 0.0))
|
if (DEQ(dvalue, 0.0))
|
||||||
return CALC_INTEREST_MSG;
|
return CALC_INTEREST_MSG;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -158,7 +158,8 @@ gnc_xfer_update_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
|
|||||||
if ((string == NULL) || (*string == 0))
|
if ((string == NULL) || (*string == 0))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
value = xaccParseAmount(string, TRUE);
|
value = 0.0;
|
||||||
|
xaccParseAmount(string, TRUE, &value, NULL);
|
||||||
|
|
||||||
currency = xaccAccountGetCurrency(account);
|
currency = xaccAccountGetCurrency(account);
|
||||||
|
|
||||||
@@ -325,7 +326,8 @@ gnc_xfer_dialog_ok_cb(GtkWidget * widget, gpointer data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
string = gtk_entry_get_text(GTK_ENTRY(xferData->amount_entry));
|
string = gtk_entry_get_text(GTK_ENTRY(xferData->amount_entry));
|
||||||
amount = xaccParseAmount(string, TRUE);
|
amount = 0.0;
|
||||||
|
xaccParseAmount(string, TRUE, &amount, NULL);
|
||||||
|
|
||||||
time = gnc_date_edit_get_date(GNC_DATE_EDIT(xferData->date_entry));
|
time = gnc_date_edit_get_date(GNC_DATE_EDIT(xferData->date_entry));
|
||||||
|
|
||||||
|
|||||||
@@ -110,15 +110,16 @@ gnc_ui_AdjBWindow_cancel_cb(GtkWidget * widget, gpointer data)
|
|||||||
static void
|
static void
|
||||||
gnc_ui_AdjBWindow_ok_cb(GtkWidget * widget, gpointer data)
|
gnc_ui_AdjBWindow_ok_cb(GtkWidget * widget, gpointer data)
|
||||||
{
|
{
|
||||||
AdjBWindow *adjBData = (AdjBWindow *) data;
|
AdjBWindow *adjBData = data;
|
||||||
Transaction *trans;
|
Transaction *trans;
|
||||||
Split *source_split;
|
Split *source_split;
|
||||||
time_t time;
|
double new_balance = 0.0;
|
||||||
double new_balance, current_balance;
|
double current_balance;
|
||||||
gchar * string;
|
gchar * string;
|
||||||
|
time_t time;
|
||||||
|
|
||||||
string = gtk_entry_get_text(GTK_ENTRY(adjBData->balance_entry));
|
string = gtk_entry_get_text(GTK_ENTRY(adjBData->balance_entry));
|
||||||
new_balance = xaccParseAmount(string, TRUE);
|
xaccParseAmount(string, TRUE, &new_balance, NULL);
|
||||||
if (gnc_reverse_balance(adjBData->account))
|
if (gnc_reverse_balance(adjBData->account))
|
||||||
new_balance = -new_balance;
|
new_balance = -new_balance;
|
||||||
|
|
||||||
@@ -164,7 +165,8 @@ gnc_adjust_update_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
|
|||||||
|
|
||||||
string = gtk_entry_get_text(entry);
|
string = gtk_entry_get_text(entry);
|
||||||
|
|
||||||
value = xaccParseAmount(string, TRUE);
|
value = 0.0;
|
||||||
|
xaccParseAmount(string, TRUE, &value, NULL);
|
||||||
|
|
||||||
currency = xaccAccountGetCurrency(account);
|
currency = xaccAccountGetCurrency(account);
|
||||||
|
|
||||||
|
|||||||
@@ -269,7 +269,8 @@ gnc_start_recn_update_cb(GtkWidget *widget, GdkEventFocus *event,
|
|||||||
|
|
||||||
string = gtk_entry_get_text(entry);
|
string = gtk_entry_get_text(entry);
|
||||||
|
|
||||||
value = xaccParseAmount(string, TRUE);
|
value = 0.0;
|
||||||
|
xaccParseAmount(string, TRUE, &value, NULL);
|
||||||
|
|
||||||
account_type = xaccAccountGetType(account);
|
account_type = xaccAccountGetType(account);
|
||||||
if ((account_type == STOCK) || (account_type == MUTUAL) ||
|
if ((account_type == STOCK) || (account_type == MUTUAL) ||
|
||||||
@@ -413,7 +414,8 @@ startRecnWindow(GtkWidget *parent, Account *account,
|
|||||||
|
|
||||||
string = gtk_entry_get_text(GTK_ENTRY(end_value));
|
string = gtk_entry_get_text(GTK_ENTRY(end_value));
|
||||||
|
|
||||||
*new_ending = xaccParseAmount(string, TRUE);
|
*new_ending = 0.0;
|
||||||
|
xaccParseAmount(string, TRUE, new_ending, NULL);
|
||||||
*statement_date = gnc_date_edit_get_date(GNC_DATE_EDIT(date_value));
|
*statement_date = gnc_date_edit_get_date(GNC_DATE_EDIT(date_value));
|
||||||
|
|
||||||
if (gnc_reverse_balance(account))
|
if (gnc_reverse_balance(account))
|
||||||
|
|||||||
@@ -131,8 +131,8 @@ PriceMV (BasicCell *_cell,
|
|||||||
if (1 < count) return NULL;
|
if (1 < count) return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* parse the float pt value and store it */
|
/* parse the value and store it */
|
||||||
cell->amount = xaccParseAmount (newval, cell->monetary);
|
xaccParseAmount (newval, cell->monetary, &cell->amount, NULL);
|
||||||
SET ((&(cell->cell)), newval);
|
SET ((&(cell->cell)), newval);
|
||||||
return newval;
|
return newval;
|
||||||
}
|
}
|
||||||
@@ -144,7 +144,17 @@ PriceLeave (BasicCell *_cell, const char *val)
|
|||||||
{
|
{
|
||||||
PriceCell *cell = (PriceCell *) _cell;
|
PriceCell *cell = (PriceCell *) _cell;
|
||||||
char *newval;
|
char *newval;
|
||||||
|
double amount;
|
||||||
|
|
||||||
|
if (val == NULL)
|
||||||
|
val = "";
|
||||||
|
|
||||||
|
if (*val == '\0')
|
||||||
|
amount = 0.0;
|
||||||
|
else if (!xaccParseAmount (val, cell->monetary, &amount, NULL))
|
||||||
|
amount = 0.0;
|
||||||
|
|
||||||
|
cell->amount = amount;
|
||||||
newval = xaccPriceCellPrintValue(cell);
|
newval = xaccPriceCellPrintValue(cell);
|
||||||
|
|
||||||
/* If they are identical, return the original */
|
/* If they are identical, return the original */
|
||||||
@@ -168,7 +178,10 @@ PriceHelp (BasicCell *bcell)
|
|||||||
{
|
{
|
||||||
char *help_str;
|
char *help_str;
|
||||||
|
|
||||||
|
if (xaccParseAmount(bcell->value, cell->monetary, NULL, NULL))
|
||||||
help_str = xaccPriceCellPrintValue(cell);
|
help_str = xaccPriceCellPrintValue(cell);
|
||||||
|
else
|
||||||
|
help_str = bcell->value;
|
||||||
|
|
||||||
return g_strdup(help_str);
|
return g_strdup(help_str);
|
||||||
}
|
}
|
||||||
@@ -371,12 +384,10 @@ PriceSetValue (BasicCell *_cell, const char *str)
|
|||||||
if (str == NULL)
|
if (str == NULL)
|
||||||
str = "";
|
str = "";
|
||||||
|
|
||||||
if (!cell->blank_zero && (*str == '\0'))
|
if (*str == '\0')
|
||||||
xaccSetPriceCellBlank(cell);
|
xaccSetPriceCellValue (cell, 0.0);
|
||||||
else {
|
else if (xaccParseAmount (str, cell->monetary, &amount, NULL))
|
||||||
amount = xaccParseAmount (str, cell->monetary);
|
|
||||||
xaccSetPriceCellValue (cell, amount);
|
xaccSetPriceCellValue (cell, amount);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------- end of file ---------------------- */
|
/* --------------- end of file ---------------------- */
|
||||||
|
|||||||
Reference in New Issue
Block a user