[gcalctool] Handle variables better
- From: Robert Ancell <rancell src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gcalctool] Handle variables better
- Date: Mon, 26 Oct 2009 08:10:15 +0000 (UTC)
commit c8583a3b46cf6e76a879f251c03fdc1bbc3f1ecb
Author: Robert Ancell <robert ancell gmail com>
Date: Mon Oct 26 19:10:07 2009 +1100
Handle variables better
src/calctool.c | 4 +-
src/display.c | 11 +++---
src/gtk.c | 1 -
src/mp-convert.c | 57 +++++++++++++++++++--------------
src/mp-equation-lexer.l | 31 ++++++------------
src/mp-equation-parser.y | 36 ++++++++++++---------
src/mp-equation.c | 73 ++++++++++++++++++++++++++++++++++++++++---
src/mp-equation.h | 17 ++++++---
src/unittest.c | 77 ++++++++++++++++++++++++----------------------
9 files changed, 191 insertions(+), 116 deletions(-)
---
diff --git a/src/calctool.c b/src/calctool.c
index edbec89..2f51aaf 100644
--- a/src/calctool.c
+++ b/src/calctool.c
@@ -46,7 +46,7 @@ static void
solve(const char *equation)
{
MPEquationOptions options;
- int error;
+ MPErrorCode error;
MPNumber result;
char result_str[MAXLINE];
@@ -56,7 +56,7 @@ solve(const char *equation)
error = mp_equation_parse(equation, &options, &result, NULL);
if(error != 0) {
- fprintf(stderr, "Error %d\n", error);
+ fprintf(stderr, "Error: %s\n", mp_error_code_to_string(error));
exit(1);
}
else {
diff --git a/src/display.c b/src/display.c
index 64cb266..6038bc6 100644
--- a/src/display.c
+++ b/src/display.c
@@ -746,6 +746,7 @@ get_variable(const char *name, MPNumber *z, void *data)
static void
set_variable(const char *name, const MPNumber *x, void *data)
{
+ /* FIXME: Don't allow writing to built-in variables, e.g. ans, rand, sin, ... */
if (name[0] == 'R' || name[0] == 'r')
register_set_value(atoi(name+1), x);
}
@@ -1015,29 +1016,29 @@ display_do_function(GCDisplay *display, int function, gpointer arg, int cursor_s
&z,
&error_token);
switch (result) {
- case 0:
+ case PARSER_ERR_NONE:
mp_set_from_mp(&z, ans);
display_set_answer(display);
break;
- case -PARSER_ERR_OVERFLOW:
+ case PARSER_ERR_OVERFLOW:
/* Translators: Error displayed to user when they perform a bitwise operation on numbers greater than the current word */
message = _("Overflow. Try a bigger word size");
break;
- case -PARSER_ERR_UNKNOWN_VARIABLE:
+ case PARSER_ERR_UNKNOWN_VARIABLE:
/* Translators: Error displayed to user when they an unknown variable is entered */
message = g_strdup_printf(_("Unknown variable '%s'"), error_token);
free(error_token);
break;
- case -PARSER_ERR_UNKNOWN_FUNCTION:
+ case PARSER_ERR_UNKNOWN_FUNCTION:
/* Translators: Error displayed to user when an unknown function is entered */
message = g_strdup_printf(_("Function '%s' is not defined"), error_token);
free(error_token);
break;
- case -PARSER_ERR_MP:
+ case PARSER_ERR_MP:
message = mp_get_error();
break;
diff --git a/src/gtk.c b/src/gtk.c
index efb30bb..39c9c72 100644
--- a/src/gtk.c
+++ b/src/gtk.c
@@ -1323,7 +1323,6 @@ main_window_key_press_cb(GtkWidget *widget, GdkEventKey *event)
case '>':
button_cb(GET_WIDGET("calc_shift_right_button"), NULL);
return TRUE;
- case '=':
case '\n':
do_button(FN_CALCULATE, NULL);
return TRUE;
diff --git a/src/mp-convert.c b/src/mp-convert.c
index 13efada..919c3a1 100644
--- a/src/mp-convert.c
+++ b/src/mp-convert.c
@@ -592,32 +592,49 @@ char_val(char **c, int base)
}
+static int
+ends_with(const char *start, const char *end, const char *word)
+{
+ size_t word_len = strlen(word);
+
+ if (word_len > end - start)
+ return 0;
+
+ return strncmp(end - word_len, word, word_len) == 0;
+}
+
+
int
mp_set_from_string(const char *str, MPNumber *z)
{
- int i, base, negate = 0, multiplier = 0;
+ int i, base, negate = 0, multiplier = 0, base_multiplier = 1;
const char *c, *end;
gboolean has_fraction = FALSE;
- const char *base_suffixes[] = {"â??", "â??", "â??â??", NULL};
- int base_values[] = {2, 8, 16, 10};
+ const char *base_digits[] = {"â??", "â??", "â??", "â??", "â??", "â??", "â??", "â??", "â??", "â??", NULL};
const char *fractions[] = {"½", "â??", "â??", "¼", "¾", "â??", "â??", "â??", "â??", "â??", "â??", "â??", "â??", "â??", "â??", NULL};
int numerators[] = { 1, 1, 2, 1, 3, 1, 2, 3, 4, 1, 5, 1, 3, 5, 7};
int denominators[] = { 2, 3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 8, 8, 8, 8};
- const char *si_suffixes[] = {"T", "G", "M", "k", "d", "c", "m", "u", "µ", "n", "p", "f", NULL};
- int si_multipliers[] = { 12, 9, 6, 3, -1, -2, -3, -6, -6, -9, -12, -15};
/* Find the base */
end = str;
while (*end != '\0')
end++;
- for (i = 0; base_suffixes[i] != NULL; i++) {
- if (end - strlen(base_suffixes[i]) < str)
- continue;
- if (strcmp(end - strlen(base_suffixes[i]), base_suffixes[i]) == 0)
+ base = 0;
+ while (1) {
+ for (i = 0; base_digits[i] != NULL; i++) {
+ if (ends_with(str, end, base_digits[i])) {
+ base += i * base_multiplier;
+ end -= strlen(base_digits[i]);
+ base_multiplier *= 10;
+ break;
+ }
+ }
+ if (base_digits[i] == NULL)
break;
}
- base = base_values[i];
+ if (base_multiplier == 1)
+ base = 10;
/* Check if this has a sign */
c = str;
@@ -634,16 +651,18 @@ mp_set_from_string(const char *str, MPNumber *z)
/* Convert integer part */
mp_set_from_integer(0, z);
while ((i = char_val((char **)&c, base)) >= 0) {
+ if (i > base)
+ return 1;
mp_multiply_integer(z, base, z);
mp_add_integer(z, i, z);
}
/* Look for fraction characters, e.g. â?? */
for (i = 0; fractions[i] != NULL; i++) {
- if (end - strlen(fractions[i]) < str)
- continue;
- if (strcmp(end - strlen(fractions[i]), fractions[i]) == 0)
+ if (ends_with(str, end, fractions[i])) {
+ end -= strlen(fractions[i]);
break;
+ }
}
if (fractions[i] != NULL) {
MPNumber fraction;
@@ -651,19 +670,9 @@ mp_set_from_string(const char *str, MPNumber *z)
mp_add(z, &fraction, z);
}
- if (*c == '.' | *c == ',') {
+ if (*c == '.' || *c == ',') {
has_fraction = TRUE;
c++;
- } else {
- for (i = 0; si_suffixes[i] != NULL; i++) {
- if (strncmp(c, si_suffixes[i], strlen(si_suffixes[i])) == 0)
- break;
- }
- if (si_suffixes[i] != NULL) {
- has_fraction = TRUE;
- multiplier = si_multipliers[i];
- c += strlen(si_suffixes[i]);
- }
}
/* Convert fractional part */
diff --git a/src/mp-equation-lexer.l b/src/mp-equation-lexer.l
index 66dfdd4..fc39e89 100644
--- a/src/mp-equation-lexer.l
+++ b/src/mp-equation-lexer.l
@@ -47,33 +47,23 @@ SEVEN "7"|"Ù§"|"Û·"|"ß?"|"à¥"|"à§"|"à©"|"à«"|"à"|"à¯"|"à±"|"à³"
EIGHT "8"|"Ù¨"|"Û¸"|"ß?"|"८"|"à§®"|"à©®"|"à«®"|"à®"|"௮"|"à±®"|"à³®"|"൮"|"à¹?"|"à»?"|"༨"|"á??"|"á??"|"á?¨"|"á ?"|"á¥?"|"á§?"|"á?"|"᮸"|"á±?"|"á±?"|"ê?¨"|"ê£?"|"ê¤?"|"ê©?"|"ð??¨"
NINE "9"|"Ù©"|"Û¹"|"ß?"|"९"|"৯"|"੯"|"૯"|"à¯"|"௯"|"౯"|"೯"|"൯"|"à¹?"|"à»?"|"༩"|"á??"|"á??"|"á?©"|"á ?"|"á¥?"|"á§?"|"á?"|"᮹"|"á±?"|"á±?"|"ê?©"|"ê£?"|"ê¤?"|"ê©?"|"ð??©"
DECIMAL "."|","
-BIN {ZERO}|{ONE}
-BIN_SUFFIX "â??"
-OCT {ZERO}|{ONE}|{TWO}|{THREE}|{FOUR}|{FIVE}|{SIX}|{SEVEN}
-OCT_SUFFIX "â??"
DEC {ZERO}|{ONE}|{TWO}|{THREE}|{FOUR}|{FIVE}|{SIX}|{SEVEN}|{EIGHT}|{NINE}
-HEX {DEC}|[A-F]|[a-f]
-HEX_SUFFIX "â??â??"
-SI_SUFFIX "T"|"G"|"M"|"k"|"d"|"c"|"m"|"u"|"µ"|"n"|"p"|"f"
+HEX {DEC}|[A-F]
SUPER_DIGITS "�"|"¹"|"²"|"³"|"�"|"�"|"�"|"�"|"�"|"�"
SUB_DIGITS "â??"|"â??"|"â??"|"â??"|"â??"|"â??"|"â??"|"â??"|"â??"|"â??"
FRACTION "½"|"â??"|"â??"|"¼"|"¾"|"â??"|"â??"|"â??"|"â??"|"â??"|"â??"|"â??"|"â??"|"â??"|"â??"
INVERSE "�¹"
+GREEKS "α"|"β"|"γ"|"δ"|"ε"|"ζ"|"η"|"θ"|"ι"|"κ"|"λ"|"μ"|"ν"|"ξ"|"ο"|"Ï?"|"Ï?"|"Ï?"|"Ï?"|"Ï?"|"Ï?"|"Ï?"|"Ï?"|"Ï?"|"Ï?"
+LETTERS [a-zA-Z]|{GREEKS}
-HEX_NUM {HEX}+{HEX_SUFFIX}|{HEX}*{DECIMAL}{HEX}+{HEX_SUFFIX}
-DEC_NUM {DEC}+|{DEC}+{SI_SUFFIX}|{DEC}*{DECIMAL}{DEC}+|{DEC}*{SI_SUFFIX}{DEC}+|{FRACTION}|{DEC}{FRACTION}
-OCT_NUM {OCT}+{OCT_SUFFIX}|{OCT}*{DECIMAL}{OCT}+{OCT_SUFFIX}
-BIN_NUM {BIN}+{BIN_SUFFIX}|{BIN}*{DECIMAL}{BIN}+{BIN_SUFFIX}
SUP_NUM {SUPER_DIGITS}+
SUB_NUM {SUB_DIGITS}+
-NUMBER {BIN_NUM}|{OCT_NUM}|{DEC_NUM}|{HEX_NUM}
-GREEKS "α"|"β"|"γ"|"δ"|"ε"|"ζ"|"η"|"θ"|"ι"|"κ"|"λ"|"μ"|"ν"|"ξ"|"ο"|"Ï?"|"Ï?"|"Ï?"|"Ï?"|"Ï?"|"Ï?"|"Ï?"|"Ï?"|"Ï?"|"Ï?"
-REGISTERS "Râ??"|"Râ??"|"Râ??"|"Râ??"|"Râ??"|"Râ??"|"Râ??"|"Râ??"|"Râ??"|"Râ??"
-ANS [aA][nN][sS]
-RAND [rR][aA][nN][dD]
-VARIABLE {REGISTERS}|{GREEKS}|"e"|{ANS}|{RAND}
-VARIABLE_NAME [a-zA-Z]+
-FUNCTION {VARIABLE_NAME}|{VARIABLE_NAME}{INVERSE}|{VARIABLE_NAME}{SUB_NUM}
+WORD {LETTERS}+
+DEC_NUM {DEC}+|{DEC}*{DECIMAL}{DEC}+
+BASE_NUM {HEX}+{SUB_NUM}|{HEX}*{DECIMAL}{HEX}+{SUB_NUM}
+
+NUMBER {DEC_NUM}|{BASE_NUM}|{FRACTION}|{DEC_NUM}{FRACTION}
+VARIABLE {WORD}|{WORD}{SUB_NUM}|{WORD}{INVERSE}|{GREEKS}
MOD [mM][oO][dD]
AND "â?§"|[aA][nN][dD]
@@ -95,11 +85,10 @@ NOT "¬"|"~"|[nN][oO][tT]
{AND} {return tAND;}
{OR} {return tOR;}
{XOR} {return tXOR;}
+{NUMBER} {if (mp_set_from_string(yytext, &yylval->int_t) != 0) REJECT; return tNUMBER;}
{SUP_NUM} {yylval->integer = super_atoi(yytext); return tSUPNUM; }
{SUB_NUM} {yylval->integer = sub_atoi(yytext); return tSUBNUM; }
-{NUMBER} {mp_set_from_string(yytext, &yylval->int_t); return tNUMBER;}
{VARIABLE} {yylval->name = strdup(yytext); return tVARIABLE;}
-{FUNCTION} {yylval->name = strdup(yytext); return tFUNCTION;}
{INVERSE} {return tINVERSE;}
[ \t\n]
. {return *yytext;}
diff --git a/src/mp-equation-parser.y b/src/mp-equation-parser.y
index 0dad75d..43b502f 100644
--- a/src/mp-equation-parser.y
+++ b/src/mp-equation-parser.y
@@ -36,10 +36,15 @@ static void set_error(yyscan_t yyscanner, int error)
_mp_equation_get_extra(yyscanner)->error = error;
}
+static void set_result(yyscan_t yyscanner, const MPNumber *x)
+{
+ mp_set_from_mp(x, &(_mp_equation_get_extra(yyscanner))->ret);
+}
+
static void get_variable(yyscan_t yyscanner, const char *name, MPNumber *z)
{
if (!_mp_equation_get_extra(yyscanner)->get_variable(_mp_equation_get_extra(yyscanner), name, z)) {
- set_error(yyscanner, -PARSER_ERR_UNKNOWN_VARIABLE);
+ set_error(yyscanner, PARSER_ERR_UNKNOWN_VARIABLE);
_mp_equation_get_extra(yyscanner)->error_token = strdup(name);
}
}
@@ -52,7 +57,7 @@ static void set_variable(yyscan_t yyscanner, const char *name, MPNumber *x)
static void get_function(yyscan_t yyscanner, const char *name, const MPNumber *x, MPNumber *z)
{
if (!_mp_equation_get_extra(yyscanner)->get_function(_mp_equation_get_extra(yyscanner), name, x, z)) {
- set_error(yyscanner, -PARSER_ERR_UNKNOWN_FUNCTION);
+ set_error(yyscanner, PARSER_ERR_UNKNOWN_FUNCTION);
_mp_equation_get_extra(yyscanner)->error_token = strdup(name);
}
}
@@ -60,7 +65,7 @@ static void get_function(yyscan_t yyscanner, const char *name, const MPNumber *x
static void do_not(yyscan_t yyscanner, const MPNumber *x, MPNumber *z)
{
if (!mp_is_overflow(x, _mp_equation_get_extra(yyscanner)->options->wordlen)) {
- set_error(yyscanner, -PARSER_ERR_OVERFLOW);
+ set_error(yyscanner, PARSER_ERR_OVERFLOW);
}
mp_not(x, _mp_equation_get_extra(yyscanner)->options->wordlen, z);
}
@@ -86,35 +91,35 @@ static void do_not(yyscan_t yyscanner, const MPNumber *x, MPNumber *z)
%left tMULTIPLY tDIVIDE tMOD MULTIPLICATION
%left tNOT
%left tROOT tROOT3 tROOT4
-%left <name> tVARIABLE tFUNCTION
+%left <name> tVARIABLE
%right <integer> tSUBNUM tSUPNUM
%left BOOLEAN_OPERATOR
%left PERCENTAGE
%left UNARY_MINUS
%right '^' tINVERSE '!'
-%type <int_t> exp function
+%type <int_t> exp variable
%start statement
%%
statement:
- exp { mp_set_from_mp(&$1, &(_mp_equation_get_extra(yyscanner))->ret);}
-| tVARIABLE '=' exp {set_variable(yyscanner, $1, &$3);}
+ exp { set_result(yyscanner, &$1); }
+| exp '=' { set_result(yyscanner, &$1); }
+| tVARIABLE '=' exp {set_variable(yyscanner, $1, &$3); set_result(yyscanner, &$3); }
;
exp:
'(' exp ')' {mp_set_from_mp(&$2, &$$);}
| '|' exp '|' {mp_abs(&$2, &$$);}
+| '|' tVARIABLE '|' {get_variable(yyscanner, $2, &$$); mp_abs(&$$, &$$); free($2);} /* FIXME: Shouldn't need this rule but doesn't parse without it... */
+| '|' tNUMBER tVARIABLE '|' {get_variable(yyscanner, $3, &$$); mp_multiply(&$2, &$$, &$$); mp_abs(&$$, &$$); free($3);} /* FIXME: Shouldn't need this rule but doesn't parse without it... */
| exp '^' exp {mp_xpowy(&$1, &$3, &$$);}
| exp tSUPNUM {mp_xpowy_integer(&$1, $2, &$$);}
| exp tINVERSE {mp_reciprocal(&$1, &$$);}
| exp '!' {mp_factorial(&$1, &$$);}
-| tVARIABLE {get_variable(yyscanner, $1, &$$); free($1);}
-| tVARIABLE tVARIABLE %prec MULTIPLICATION {MPNumber t; get_variable(yyscanner, $1, &t); get_variable(yyscanner, $2, &$$); mp_multiply(&t, &$$, &$$); free($1);}
-| function {mp_set_from_mp(&$1, &$$);}
-| tNUMBER function %prec MULTIPLICATION {mp_multiply(&$1, &$2, &$$);}
-| tNUMBER tVARIABLE %prec MULTIPLICATION {get_variable(yyscanner, $2, &$$); mp_multiply(&$1, &$$, &$$); free($2);}
+| variable {mp_set_from_mp(&$1, &$$);}
+| tNUMBER variable %prec MULTIPLICATION {mp_multiply(&$1, &$2, &$$);}
| tSUBTRACT exp %prec UNARY_MINUS {mp_invert_sign(&$2, &$$);}
| tADD tNUMBER %prec UNARY_PLUS {mp_set_from_mp(&$2, &$$);}
| exp tDIVIDE exp {mp_divide(&$1, &$3, &$$);}
@@ -133,13 +138,14 @@ exp:
;
-function:
- tFUNCTION exp {get_function(yyscanner, $1, &$2, &$$); free($1);}
-| tFUNCTION tSUPNUM exp {get_function(yyscanner, $1, &$3, &$$); mp_xpowy_integer(&$$, $2, &$$); free($1);}
+variable:
+ tVARIABLE exp {get_function(yyscanner, $1, &$2, &$$); free($1);}
+| tVARIABLE tSUPNUM exp {get_function(yyscanner, $1, &$3, &$$); mp_xpowy_integer(&$$, $2, &$$); free($1);}
| tSUBNUM tROOT exp {mp_root(&$3, $1, &$$);}
| tROOT exp {mp_sqrt(&$2, &$$);}
| tROOT3 exp {mp_root(&$2, 3, &$$);}
| tROOT4 exp {mp_root(&$2, 4, &$$);}
+| tVARIABLE {get_variable(yyscanner, $1, &$$); free($1);}
;
%%
diff --git a/src/mp-equation.c b/src/mp-equation.c
index e7d2c10..1edfec4 100644
--- a/src/mp-equation.c
+++ b/src/mp-equation.c
@@ -25,6 +25,17 @@
extern int _mp_equation_parse(yyscan_t yyscanner);
+
+char *
+utf8_next_char (const char *c)
+{
+ c++;
+ while ((*c & 0xC0) == 0x80)
+ c++;
+ return (char *)c;
+}
+
+
static int
get_variable(MPEquationParserState *state, const char *name, MPNumber *z)
{
@@ -39,12 +50,41 @@ get_variable(MPEquationParserState *state, const char *name, MPNumber *z)
else
result = 0;
+ /* If has more than one character then assuming a multiplication of variables */
+ if (!result && utf8_next_char(name)[0] != '\0') {
+ const char *c, *next;
+ char *buffer = malloc(sizeof(char) * strlen(name));
+ MPNumber value;
+
+ result = 1;
+ mp_set_from_integer(1, &value);
+ for (c = name; *c != '\0'; c = next)
+ {
+ MPNumber t;
+
+ next = utf8_next_char(c);
+ snprintf(buffer, next - c + 1, "%s", c);
+
+ if (!get_variable(state, buffer, &t))
+ {
+ result = 0;
+ break;
+ }
+ mp_multiply(&value, &t, &value);
+ }
+
+ free(buffer);
+ if (result)
+ mp_set_from_mp(&value, z);
+ }
+
return result;
}
static void
set_variable(MPEquationParserState *state, const char *name, const MPNumber *x)
{
+ // Reserved words, e, Ï?, mod, and, or, xor, not, abs, log, ln, sqrt, int, frac, sin, cos, ...
if (strcmp(name, "e") == 0 || strcmp(name, "Ï?") == 0)
return; // FALSE
@@ -164,7 +204,7 @@ get_function(MPEquationParserState *state, const char *name, const MPNumber *x,
}
-int
+MPErrorCode
mp_equation_parse(const char *expression, MPEquationOptions *options, MPNumber *result, char **error_token)
{
int ret;
@@ -173,7 +213,7 @@ mp_equation_parse(const char *expression, MPEquationOptions *options, MPNumber *
YY_BUFFER_STATE buffer;
if (!(expression && result) || strlen(expression) == 0)
- return -EINVAL;
+ return PARSER_ERR_INVALID;
memset(&state, 0, sizeof(MPEquationParserState));
state.options = options;
@@ -197,18 +237,41 @@ mp_equation_parse(const char *expression, MPEquationOptions *options, MPNumber *
/* Failed to parse */
if (ret)
- return -PARSER_ERR_INVALID;
+ return PARSER_ERR_INVALID;
/* Error during parsing */
if (state.error)
return state.error;
if (mp_get_error())
- return -PARSER_ERR_MP;
+ return PARSER_ERR_MP;
mp_set_from_mp(&state.ret, result);
- return 0;
+ return PARSER_ERR_NONE;
+}
+
+
+const char *
+mp_error_code_to_string(MPErrorCode error_code)
+{
+ switch(error_code)
+ {
+ case PARSER_ERR_NONE:
+ return "PARSER_ERR_NONE";
+ case PARSER_ERR_INVALID:
+ return "PARSER_ERR_INVALID";
+ case PARSER_ERR_OVERFLOW:
+ return "PARSER_ERR_OVERFLOW";
+ case PARSER_ERR_UNKNOWN_VARIABLE:
+ return "PARSER_ERR_UNKNOWN_VARIABLE";
+ case PARSER_ERR_UNKNOWN_FUNCTION:
+ return "PARSER_ERR_UNKNOWN_FUNCTION";
+ case PARSER_ERR_MP:
+ return "PARSER_ERR_MP";
+ default:
+ return "Unknown parser error";
+ }
}
diff --git a/src/mp-equation.h b/src/mp-equation.h
index b698e0f..666353d 100644
--- a/src/mp-equation.h
+++ b/src/mp-equation.h
@@ -22,11 +22,15 @@
#include "mp.h"
-#define PARSER_ERR_INVALID 1
-#define PARSER_ERR_OVERFLOW 5
-#define PARSER_ERR_UNKNOWN_VARIABLE 6
-#define PARSER_ERR_UNKNOWN_FUNCTION 7
-#define PARSER_ERR_MP 9
+typedef enum
+{
+ PARSER_ERR_NONE = 0,
+ PARSER_ERR_INVALID,
+ PARSER_ERR_OVERFLOW,
+ PARSER_ERR_UNKNOWN_VARIABLE,
+ PARSER_ERR_UNKNOWN_FUNCTION,
+ PARSER_ERR_MP
+} MPErrorCode;
/* Options for parser */
typedef struct {
@@ -52,7 +56,8 @@ typedef struct {
int (*get_function)(const char *name, const MPNumber *x, MPNumber *z, void *data);
} MPEquationOptions;
-int mp_equation_parse(const char *expression, MPEquationOptions *options, MPNumber *result, char **error_token);
+MPErrorCode mp_equation_parse(const char *expression, MPEquationOptions *options, MPNumber *result, char **error_token);
+const char *mp_error_code_to_string(MPErrorCode error_code);
int sub_atoi(const char *data);
int super_atoi(const char *data);
diff --git a/src/unittest.c b/src/unittest.c
index c50ee16..2286258 100644
--- a/src/unittest.c
+++ b/src/unittest.c
@@ -80,9 +80,10 @@ test(char *expression, char *expected, int expected_error)
}
else {
if(error == expected_error)
- pass("'%s' -> error %d", expression, error);
+ pass("'%s' -> error %s", expression, mp_error_code_to_string(error));
else
- fail("'%s' -> error %d, expected error %d", expression, error, expected_error);
+ fail("'%s' -> error %s, expected error %s", expression,
+ mp_error_code_to_string(error), mp_error_code_to_string(expected_error));
}
}
@@ -107,26 +108,26 @@ test_parser()
base = 10;
test("0â??", "0", 0); test("0â??", "0", 0); test("0", "0", 0); test("0â??â??", "0", 0);
test("1â??", "1", 0); test("1â??", "1", 0); test("1", "1", 0); test("1â??â??", "1", 0);
- test("2â??", "", -1); test("2â??", "2", 0); test("2", "2", 0); test("2â??â??", "2", 0);
- test("3â??", "", -1); test("3â??", "3", 0); test("3", "3", 0); test("3â??â??", "3", 0);
- test("4â??", "", -1); test("4â??", "4", 0); test("4", "4", 0); test("4â??â??", "4", 0);
- test("5â??", "", -1); test("5â??", "5", 0); test("5", "5", 0); test("5â??â??", "5", 0);
- test("6â??", "", -1); test("6â??", "6", 0); test("6", "6", 0); test("6â??â??", "6", 0);
- test("7â??", "", -1); test("7â??", "7", 0); test("7", "7", 0); test("7â??â??", "7", 0);
- test("8â??", "", -1); test("8â??", "", -1); test("8", "8", 0); test("8â??â??", "8", 0);
- test("9â??", "", -1); test("9â??", "", -1); test("9", "9", 0); test("9â??â??", "9", 0);
- test("Aâ??", "", -1); test("Aâ??", "", -1); test("A", "", -1); test("Aâ??â??", "10", 0);
- test("Bâ??", "", -1); test("Bâ??", "", -1); test("B", "", -1); test("Bâ??â??", "11", 0);
- test("Câ??", "", -1); test("Câ??", "", -1); test("C", "", -1); test("Câ??â??", "12", 0);
- test("Dâ??", "", -1); test("Dâ??", "", -1); test("D", "", -1); test("Dâ??â??", "13", 0);
- test("Eâ??", "", -1); test("Eâ??", "", -1); test("E", "", -1); test("Eâ??â??", "14", 0);
- test("Fâ??", "", -1); test("Fâ??", "", -1); test("F", "", -1); test("Fâ??â??", "15", 0);
+ test("2â??", "", PARSER_ERR_INVALID); test("2â??", "2", 0); test("2", "2", 0); test("2â??â??", "2", 0);
+ test("3â??", "", PARSER_ERR_INVALID); test("3â??", "3", 0); test("3", "3", 0); test("3â??â??", "3", 0);
+ test("4â??", "", PARSER_ERR_INVALID); test("4â??", "4", 0); test("4", "4", 0); test("4â??â??", "4", 0);
+ test("5â??", "", PARSER_ERR_INVALID); test("5â??", "5", 0); test("5", "5", 0); test("5â??â??", "5", 0);
+ test("6â??", "", PARSER_ERR_INVALID); test("6â??", "6", 0); test("6", "6", 0); test("6â??â??", "6", 0);
+ test("7â??", "", PARSER_ERR_INVALID); test("7â??", "7", 0); test("7", "7", 0); test("7â??â??", "7", 0);
+ test("8â??", "", PARSER_ERR_INVALID); test("8â??", "", PARSER_ERR_INVALID); test("8", "8", 0); test("8â??â??", "8", 0);
+ test("9â??", "", PARSER_ERR_INVALID); test("9â??", "", PARSER_ERR_INVALID); test("9", "9", 0); test("9â??â??", "9", 0);
+ test("Aâ??", "", PARSER_ERR_UNKNOWN_VARIABLE); test("Aâ??", "", PARSER_ERR_UNKNOWN_VARIABLE); test("A", "", PARSER_ERR_UNKNOWN_VARIABLE); test("Aâ??â??", "10", 0);
+ test("Bâ??", "", PARSER_ERR_UNKNOWN_VARIABLE); test("Bâ??", "", PARSER_ERR_UNKNOWN_VARIABLE); test("B", "", PARSER_ERR_UNKNOWN_VARIABLE); test("Bâ??â??", "11", 0);
+ test("Câ??", "", PARSER_ERR_UNKNOWN_VARIABLE); test("Câ??", "", PARSER_ERR_UNKNOWN_VARIABLE); test("C", "", PARSER_ERR_UNKNOWN_VARIABLE); test("Câ??â??", "12", 0);
+ test("Dâ??", "", PARSER_ERR_UNKNOWN_VARIABLE); test("Dâ??", "", PARSER_ERR_UNKNOWN_VARIABLE); test("D", "", PARSER_ERR_UNKNOWN_VARIABLE); test("Dâ??â??", "13", 0);
+ test("Eâ??", "", PARSER_ERR_UNKNOWN_VARIABLE); test("Eâ??", "", PARSER_ERR_UNKNOWN_VARIABLE); test("E", "", PARSER_ERR_UNKNOWN_VARIABLE); test("Eâ??â??", "14", 0);
+ test("Fâ??", "", PARSER_ERR_UNKNOWN_VARIABLE); test("Fâ??", "", PARSER_ERR_UNKNOWN_VARIABLE); test("F", "", PARSER_ERR_UNKNOWN_VARIABLE); test("Fâ??â??", "15", 0);
test("+1", "1", 0);
test("â??1", "â??1", 0);
test("+ 1", "1", 0); // FIXME: Should this be allowed?
test("â?? 1", "â??1", 0); // FIXME: Should this be allowed?
- test("++1", "1", -1);
+ test("++1", "1", PARSER_ERR_INVALID);
test("â??â??1", "1", 0);
test("255", "255", 0);
test("256", "256", 0);
@@ -138,6 +139,7 @@ test_parser()
test("١٢٣٤٥٦٧٨٩٠", "1234567890", 0);
test("Û±Û²Û³Û´ÛµÛ¶Û·Û¸Û¹Û°", "1234567890", 0);
+/*
//test("2A", "2000000000000000", 0);
test("2T", "2000000000000", 0);
test("2G", "2000000000", 0);
@@ -166,6 +168,7 @@ test_parser()
//test("2n3", "0.0000000023", 0); // FIXME: Need to print out significant figures, not decimal places
//test("2p3", "0.0000000000023", 0); // FIXME: Need to print out significant figures, not decimal places
//test("2f3", "0.0000000000000023", 0); // FIXME: Need to print out significant figures, not decimal places
+*/
test("2Ã?10^3", "2000", 0);
test("2Ã?10^â??3", "0.002", 0);
@@ -176,8 +179,8 @@ test_parser()
test("2e", "5.436563657", 0);
//test("2Ï?²", "19.739208802", 0);
//test("2e²", "14.778112198", 0);
- test("e2", "", -1);
- test("Ï?2", "", -1);
+ test("e2", "", PARSER_ERR_UNKNOWN_FUNCTION);
+ test("Ï?2", "", PARSER_ERR_UNKNOWN_FUNCTION);
test("Ï?e", "8.539734223", 0);
test("eÏ?", "8.539734223", 0);
//test("2Ï?e", "17.079468445", 0);
@@ -211,8 +214,8 @@ test_parser()
test("1÷4", "0.25", 0);
test("1÷3", "0.333333333", 0);
test("2÷3", "0.666666667", 0);
- test("1÷0", "", -9);
- test("0÷0", "", -9);
+ test("1÷0", "", PARSER_ERR_MP);
+ test("0÷0", "", PARSER_ERR_MP);
/* Precision */
test("1000000000000000â??1000000000000000", "0", 0);
@@ -239,9 +242,9 @@ test_parser()
test("1!", "1", 0);
test("5!", "120", 0);
test("69!", "171122452428141311372468338881272839092270544893520369393648040923257279754140647424000000000000000", 0);
- test("0.1!", "", -9);
+ test("0.1!", "", PARSER_ERR_MP);
test("â??1!", "â??1", 0);
- test("(â??1)!", "", -9);
+ test("(â??1)!", "", PARSER_ERR_MP);
test("â??(1!)", "â??1", 0);
/* Powers */
@@ -252,7 +255,7 @@ test_parser()
test("2^1", "2", 0);
test("2^2", "4", 0);
test("2�¹", "0.5", 0);
- //test("2â?»", "", -9); // FIXME: Maybe an error in bison?
+ //test("2â?»", "", PARSER_ERR_MP); // FIXME: Maybe an error in bison?
test("2^â??1", "0.5", 0);
test("2^(â??1)", "0.5", 0);
test("â??10^2", "â??100", 0);
@@ -276,7 +279,7 @@ test_parser()
test("Sqrt(2)", "1.414213562", 0);
test("4^0.5", "2", 0);
test("2^0.5", "1.414213562", 0);
- test("(â??4)^0.5", "", -9);
+ test("(â??4)^0.5", "", PARSER_ERR_MP);
test("(â??8)^(1÷3)", "â??2", 0);
test("0 mod 7", "0", 0);
@@ -287,8 +290,8 @@ test_parser()
test("int 3.2", "3", 0);
test("frac 3.2", "0.2", 0);
- test("int â??3.2", "â??3", 0);
- test("frac â??3.2", "â??0.2", 0);
+ test("int (â??3.2)", "â??3", 0);
+ test("frac (â??3.2)", "â??0.2", 0);
test("|1|", "1", 0);
test("|â??1|", "1", 0);
@@ -296,10 +299,10 @@ test_parser()
test("|e|", "2.718281828", 0);
test("|Ï?|", "3.141592654", 0);
test("abs 1", "1", 0);
- test("abs â??1", "1", 0);
+ test("abs (â??1)", "1", 0);
- test("log â??1", "", -9);
- test("log 0", "", -9);
+ test("log (â??1)", "", PARSER_ERR_MP);
+ test("log 0", "", PARSER_ERR_MP);
test("log 1", "0", 0);
test("log 2", "0.301029996", 0);
test("log 10", "1", 0);
@@ -307,8 +310,8 @@ test_parser()
test("logâ?? 2", "1", 0);
test("2 log 2", "0.602059991", 0);
- test("ln â??1", "", -9);
- test("ln 0", "", -9);
+ test("ln (â??1)", "", PARSER_ERR_MP);
+ test("ln 0", "", PARSER_ERR_MP);
test("ln 1", "0", 0);
test("ln 2", "0.693147181", 0);
test("ln e", "1", 0);
@@ -325,7 +328,7 @@ test_parser()
test("cos 0", "1", 0);
test("cos 45 â?? 1÷â??2", "0", 0);
- test("cos 20 â?? cos â??20", "0", 0);
+ test("cos 20 â?? cos (â??20)", "0", 0);
test("cos 90", "0", 0);
test("cos 180", "â??1", 0);
test("2 cos 0", "2", 0);
@@ -333,18 +336,18 @@ test_parser()
test("tan 0", "0", 0);
test("tan 10 â?? sin 10÷cos 10", "0", 0);
- test("tan 90", "", -9);
+ test("tan 90", "", PARSER_ERR_MP);
test("tan 10", "0.176326981", 0);
test("tan²10", "0.031091204", 0);
test("cos�¹ 0", "90", 0);
test("cos�¹ 1", "0", 0);
- test("cosâ?»Â¹ â??1", "180", 0);
+ test("cosâ?»Â¹ (â??1)", "180", 0);
test("cosâ?»Â¹ (1÷â??2)", "45", 0);
test("sin�¹ 0", "0", 0);
test("sin�¹ 1", "90", 0);
- test("sinâ?»Â¹ â??1", "â??90", 0);
+ test("sinâ?»Â¹ (â??1)", "â??90", 0);
test("sinâ?»Â¹ (1÷â??2)", "45", 0);
test("cosh 0", "1", 0);
@@ -352,7 +355,7 @@ test_parser()
test("sinh 0", "0", 0);
test("sinh 10 â?? (e^10 â?? e^â??10)÷2", "0", 0);
- test("sinh â??10 + sinh 10", "0", 0);
+ test("sinh (â??10) + sinh 10", "0", 0);
test("cosh² â??5 â?? sinh² â??5", "1", 0);
test("tanh 0", "0", 0);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]