[gcalctool] Handle variables better



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]