[gcalctool] Get some more variable powers working



commit e379fed63f8921e66bac5a6a6f8f7bef4163b4e3
Author: Robert Ancell <robert ancell gmail com>
Date:   Thu Mar 18 21:38:06 2010 +1100

    Get some more variable powers working

 src/display.c             |   22 ++++++++++++
 src/mp-equation-parser.y  |   65 ++++++++++++++++++++++++++++++-----
 src/mp-equation-private.h |    6 +++
 src/mp-equation.c         |   83 ++++++++++++++++++++++++++-------------------
 src/mp-equation.h         |    6 +++
 src/unittest.c            |   22 +++++++++---
 6 files changed, 154 insertions(+), 50 deletions(-)
---
diff --git a/src/display.c b/src/display.c
index 35c228f..07565fd 100644
--- a/src/display.c
+++ b/src/display.c
@@ -747,6 +747,27 @@ display_make_number(GCDisplay *display, char *target, int target_len, const MPNu
     }
 }
 
+
+static int
+variable_is_defined(const char *name)
+{
+    char *c, *lower_name;
+
+    lower_name = strdup(name);
+    for (c = lower_name; *c; c++)
+        *c = tolower(*c);
+
+    if (strcmp(lower_name, "rand") == 0 || 
+        strcmp(lower_name, "ans") == 0) {
+        g_free (lower_name);
+        return 1;
+    }
+    g_free (lower_name);
+
+    return register_get_value(name) != NULL;
+}
+
+
 static int
 get_variable(const char *name, MPNumber *z, void *data)
 {
@@ -964,6 +985,7 @@ parse(GCDisplay *display, const char *text, MPNumber *z, char **error_token)
     memset(&options, 0, sizeof(options));
     options.wordlen = display->word_size;
     options.angle_units = display->angle_unit;
+    options.variable_is_defined = variable_is_defined;
     options.get_variable = get_variable;
     options.set_variable = set_variable;
     options.convert = convert;
diff --git a/src/mp-equation-parser.y b/src/mp-equation-parser.y
index 89c6cc3..dce2f23 100644
--- a/src/mp-equation-parser.y
+++ b/src/mp-equation-parser.y
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <math.h>
 #include <errno.h>
+#include <assert.h>
 
 #include "mp-equation-private.h"
 #include "mp-equation-parser.h"
@@ -43,14 +44,60 @@ static void set_result(yyscan_t yyscanner, const MPNumber *x)
     mp_set_from_mp(x, &(_mp_equation_get_extra(yyscanner))->ret);
 }
 
-static int get_variable(yyscan_t yyscanner, const char *name, MPNumber *z)
+char *
+utf8_next_char (const char *c)
 {
-    if (!_mp_equation_get_extra(yyscanner)->get_variable(_mp_equation_get_extra(yyscanner), name, z)) {
-        set_error(yyscanner, PARSER_ERR_UNKNOWN_VARIABLE, name);
-        return 0;
+    c++;
+    while ((*c & 0xC0) == 0x80)
+        c++;
+    return (char *)c;
+}
+
+static int get_variable(yyscan_t yyscanner, const char *name, int power, MPNumber *z)
+{
+    int result = 0;
+
+    /* If defined, then get the variable */
+    if (_mp_equation_get_extra(yyscanner)->get_variable(_mp_equation_get_extra(yyscanner), name, z)) {
+        mp_xpowy_integer(z, power, z);
+        return 1;
     }
     
-    return 1;
+    /* If has more than one character then assume a multiplication of variables */
+    if (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 (!_mp_equation_get_extra(yyscanner)->get_variable(_mp_equation_get_extra(yyscanner), buffer, &t)) {
+                result = 0;
+                break;
+            }
+
+            /* If last term do power */
+            if (*next == '\0')
+                mp_xpowy_integer(&t, power, &t);
+
+            mp_multiply(&value, &t, &value);
+        }
+
+        free(buffer);
+        if (result)
+            mp_set_from_mp(&value, z);
+    }
+
+    if (!result)
+        set_error(yyscanner, PARSER_ERR_UNKNOWN_VARIABLE, name);
+
+    return result;
 }
 
 static void set_variable(yyscan_t yyscanner, const char *name, MPNumber *x)
@@ -132,8 +179,8 @@ statement:
 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... */
+| '|' tVARIABLE '|' {get_variable(yyscanner, $2, 1, &$$); mp_abs(&$$, &$$); free($2);} /* FIXME: Shouldn't need this rule but doesn't parse without it... */
+| '|' tNUMBER tVARIABLE '|' {get_variable(yyscanner, $3, 1, &$$); 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 tNSUPNUM {mp_xpowy_integer(&$1, $2, &$$);}
@@ -160,13 +207,13 @@ exp:
 
 variable:
   tVARIABLE exp {if (!get_function(yyscanner, $1, &$2, &$$)) YYABORT; free($1);}
-| tVARIABLE tSUPNUM {MPNumber t; if (!get_variable(yyscanner, $1, &t)) YYABORT; mp_xpowy_integer(&t, $2, &$$);; free($1);}
+| tVARIABLE tSUPNUM {MPNumber t; if (!get_variable(yyscanner, $1, $2, &$$)) YYABORT; free($1);}
 | tVARIABLE tSUPNUM exp {if (!get_function(yyscanner, $1, &$3, &$$)) YYABORT; 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 {if (!get_variable(yyscanner, $1, &$$)) YYABORT; free($1);}
+| tVARIABLE {if (!get_variable(yyscanner, $1, 1, &$$)) YYABORT; free($1);}
 ;
 
 %%
diff --git a/src/mp-equation-private.h b/src/mp-equation-private.h
index 403ce2d..190c71a 100644
--- a/src/mp-equation-private.h
+++ b/src/mp-equation-private.h
@@ -29,12 +29,18 @@ struct MPEquationParserState {
     /* User provided options */
     MPEquationOptions *options;
 
+    /* Function to check if a variable is defined */
+    int (*variable_is_defined)(MPEquationParserState *state, const char *name);
+
     /* Function to get variable values */
     int (*get_variable)(MPEquationParserState *state, const char *name, MPNumber *z);
 
     /* Function to set variable values */
     void (*set_variable)(MPEquationParserState *state, const char *name, const MPNumber *x);
 
+    /* Function to check if a function is defined */
+    int (*function_is_defined)(MPEquationParserState *state, const char *name);
+
     /* Function to solve functions */
     int (*get_function)(MPEquationParserState *state, const char *name, const MPNumber *x, MPNumber *z);
 
diff --git a/src/mp-equation.c b/src/mp-equation.c
index 831dee6..180fff1 100644
--- a/src/mp-equation.c
+++ b/src/mp-equation.c
@@ -26,13 +26,15 @@
 extern int _mp_equation_parse(yyscan_t yyscanner);
 
 
-char *
-utf8_next_char (const char *c)
+static int
+variable_is_defined(MPEquationParserState *state, const char *name)
 {
-    c++;
-    while ((*c & 0xC0) == 0x80)
-        c++;
-    return (char *)c;
+    /* FIXME: Make more generic */
+    if (strcmp(name, "e") == 0 || strcmp(name, "i") == 0 || strcmp(name, "Ï?") == 0)
+        return 1;
+    if (state->options->variable_is_defined)
+        return state->options->variable_is_defined(name);
+    return 0;
 }
 
 
@@ -52,34 +54,6 @@ 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;
 }
 
@@ -109,7 +83,7 @@ sub_atoi(const char *data)
     do {
         for(i = 0; digits[i] != NULL && strncmp(data, digits[i], strlen(digits[i])) != 0; i++);
         if(digits[i] == NULL)
-            return 0;
+            return -1;
         data += strlen(digits[i]);
         value = value * 10 + i;
     } while(*data != '\0');
@@ -139,6 +113,43 @@ super_atoi(const char *data)
    return sign * value;
 }
 
+
+static int
+function_is_defined(MPEquationParserState *state, const char *name)
+{
+    char *c, *lower_name;
+    int result = 1;
+
+    lower_name = strdup(name);
+    for (c = lower_name; *c; c++)
+        *c = tolower(*c);
+
+    /* FIXME: Make more generic */
+    if (strcmp(lower_name, "log") == 0 ||
+        (strncmp(lower_name, "log", 3) == 0 && sub_atoi(lower_name + 3) >= 0) ||
+        strcmp(lower_name, "ln") == 0 ||
+        strcmp(lower_name, "sqrt") == 0 ||
+        strcmp(lower_name, "abs") == 0 ||
+        strcmp(lower_name, "int") == 0 ||
+        strcmp(lower_name, "frac") == 0 ||
+        strcmp(lower_name, "sin") == 0 || strcmp(lower_name, "cos") == 0 || strcmp(lower_name, "tan") == 0 ||
+        strcmp(lower_name, "sin�¹") == 0 || strcmp(lower_name, "cos�¹") == 0 || strcmp(lower_name, "tan�¹") == 0 ||
+        strcmp(lower_name, "sinh") == 0 || strcmp(lower_name, "cosh") == 0 || strcmp(lower_name, "tanh") == 0 ||
+        strcmp(lower_name, "sinh�¹") == 0 || strcmp(lower_name, "cosh�¹") == 0 || strcmp(lower_name, "tanh�¹") == 0 ||
+        strcmp(lower_name, "asinh") == 0 || strcmp(lower_name, "acosh") == 0 || strcmp(lower_name, "atanh") == 0 ||
+        strcmp(lower_name, "ones") == 0 ||
+        strcmp(lower_name, "twos") == 0) {
+        g_free (lower_name);
+        return 1;
+    }
+    g_free (lower_name);
+
+    if (state->options->function_is_defined)
+        return state->options->function_is_defined(name);
+    return 0;
+}
+
+
 static int
 get_function(MPEquationParserState *state, const char *name, const MPNumber *x, MPNumber *z)
 {
@@ -224,8 +235,10 @@ mp_equation_parse(const char *expression, MPEquationOptions *options, MPNumber *
 
     memset(&state, 0, sizeof(MPEquationParserState));
     state.options = options;
+    state.variable_is_defined = variable_is_defined;
     state.get_variable = get_variable;
     state.set_variable = set_variable;
+    state.function_is_defined = function_is_defined;
     state.get_function = get_function;
     state.error = 0;
 
diff --git a/src/mp-equation.h b/src/mp-equation.h
index 4f1ad2e..5a51f43 100644
--- a/src/mp-equation.h
+++ b/src/mp-equation.h
@@ -46,6 +46,9 @@ typedef struct {
 
     /* Data to pass to callbacks */
     void *callback_data;
+  
+    /* Function to check if a variable is defined */
+    int (*variable_is_defined)(const char *name);
 
     /* Function to get variable values */
     int (*get_variable)(const char *name, MPNumber *z, void *data);
@@ -53,6 +56,9 @@ typedef struct {
     /* Function to set variable values */
     void (*set_variable)(const char *name, const MPNumber *x, void *data);
 
+    /* Function to check if a function is defined */
+    int (*function_is_defined)(const char *name);
+
     /* Function to solve functions */
     int (*get_function)(const char *name, const MPNumber *x, MPNumber *z, void *data);
 
diff --git a/src/unittest.c b/src/unittest.c
index d3a140e..f7bcba0 100644
--- a/src/unittest.c
+++ b/src/unittest.c
@@ -86,7 +86,7 @@ test(char *expression, char *expected, int expected_error)
     if(error == 0) {
         mp_cast_to_string(&result, base, 9, 1, result_str, 1024);
         if(expected_error != 0)
-            fail("'%s' -> %s, expected error %d", expression, result_str, expected_error);
+            fail("'%s' -> %s, expected error %s", expression, result_str, error_code_to_string(expected_error));
         else if(strcmp(result_str, expected) != 0)
             fail("'%s' -> '%s', expected '%s'", expression, result_str, expected);
         else
@@ -103,6 +103,13 @@ test(char *expression, char *expected, int expected_error)
 
 
 int
+variable_is_defined(const char *name)
+{
+    return strcmp (name, "x") == 0 || strcmp (name, "y") == 0;
+}
+
+
+int
 get_variable(const char *name, MPNumber *z, void *data)
 {
     if (strcmp (name, "x") == 0) {
@@ -130,6 +137,7 @@ test_parser()
     base = 10;
     options.wordlen = 32;
     options.angle_units = MP_DEGREES;
+    options.variable_is_defined = variable_is_defined;  
     options.get_variable = get_variable;
     options.set_variable = set_variable;
 
@@ -223,15 +231,17 @@ test_parser()
     test("y2", "", PARSER_ERR_UNKNOWN_FUNCTION);
     test("y²", "9", 0);
     test("2y²", "18", 0);
+    test("xÃ?y", "6", 0);
     test("xy", "6", 0);
     test("yx", "6", 0);
     test("2xy", "12", 0);
-    //test("x²y", "12", 0);
-    //test("xy²", "18", 0);
+    test("x²y", "12", 0);
+    test("xy²", "18", 0);
     test("(xy)²", "36", 0);
-    //test("2x²y", "24", 0);
-    //test("2xy²", "36", 0);
-    //test("2x²y²", "72", 0);
+    test("2x²y", "24", 0);
+    test("2xy²", "36", 0);
+    test("2x²y²", "72", 0);
+    test("x²yx²y", "324", 0);
 
     test("Ï?", "3.141592654", 0);
     test("e", "2.718281828", 0);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]