[gcalctool/gcalctool-new-parser] Get conversions working



commit 919ac56fc39d1b660171e57ae33acdc50adb78ef
Author: Robert Ancell <robert ancell canonical com>
Date:   Sun Oct 16 18:35:23 2011 +1100

    Get conversions working

 src/math-equation.c    |   16 +---
 src/mp-equation.c      |  224 +++++++++++++++++++++++++++++++++---------------
 src/mp-equation.h      |    4 +-
 src/test-mp-equation.c |   34 ++++----
 4 files changed, 176 insertions(+), 102 deletions(-)
---
diff --git a/src/math-equation.c b/src/math-equation.c
index 0109a67..e1589ad 100644
--- a/src/math-equation.c
+++ b/src/math-equation.c
@@ -1132,19 +1132,9 @@ math_equation_solve_real(gpointer data)
                                _("Overflow. Try a bigger word size"));
             break;
 
-        case PARSER_ERR_UNKNOWN_VARIABLE:
-            solvedata->error = g_strdup_printf(/* Error displayed to user when they an unknown variable is entered */
-                                      _("Unknown variable '%s'"), error_token);
-            break;
-
-        case PARSER_ERR_UNKNOWN_FUNCTION:
-            solvedata->error = g_strdup_printf(/* Error displayed to user when an unknown function is entered */
-                                      _("Function '%s' is not defined"), error_token);
-            break;
-
-        case PARSER_ERR_UNKNOWN_CONVERSION:
-            solvedata->error = g_strdup(/* Error displayed to user when an conversion with unknown units is attempted */
-                               _("Unknown conversion"));
+        case PARSER_ERR_UNKNOWN_SYMBOL:
+            solvedata->error = g_strdup_printf(/* Error displayed to user when they an unknown symbol is entered */
+                                      _("Unknown symbol '%s'"), error_token);
             break;
 
         case PARSER_ERR_MP:
diff --git a/src/mp-equation.c b/src/mp-equation.c
index fa0e7da..32000c2 100644
--- a/src/mp-equation.c
+++ b/src/mp-equation.c
@@ -250,16 +250,6 @@ get_function(MPEquationOptions *options, const char *name, const MPNumber *x, MP
 }
 
 
-static int
-convert(MPEquationOptions *options, const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z)
-{
-    if (options->convert)
-        return options->convert(x, x_units, z_units, z, options->callback_data);
-    else
-        return 0;
-}
-
-
 typedef enum
 {
     TOKEN_NONE,
@@ -294,8 +284,9 @@ typedef enum
     TOKEN_RIGHT_CEILING,
     TOKEN_LEFT_FRACTION,
     TOKEN_RIGHT_FRACTION,
-    TOKEN_VARIABLE,
+    TOKEN_SYMBOL,
     TOKEN_FUNCTION,
+    TOKEN_VARIABLE,
     TOKEN_EXPRESSION
 } TokenType;
 
@@ -322,7 +313,7 @@ token_new(TokenType type, const gchar *start, const gchar *end)
 
 
 static gchar *
-token_get_string (Token *token)
+token_get_text (Token *token)
 {
     return g_strdup_printf("%.*s", (int)(token->end - token->start), token->start);
 }
@@ -391,7 +382,7 @@ parse(MPEquationOptions *options, const gchar *expression, GList **tokens)
             else if (unichar_issubdigit(c))
                 current_token = TOKEN_SUB_NUMBER;
             else if (g_unichar_isalpha(c))
-                current_token = TOKEN_VARIABLE;
+                current_token = TOKEN_SYMBOL;
             else if (c == '+')
                 current_token = TOKEN_ADD;
             else if (c == '-' || c == 0x2212 /* â */ )
@@ -472,7 +463,7 @@ parse(MPEquationOptions *options, const gchar *expression, GList **tokens)
 
                 current_token = TOKEN_NONE;
                 t = token_new(TOKEN_PERCENTAGE, token_start, i + 1);
-                string = token_get_string(t);
+                string = token_get_text(t);
                 string[strlen (string) - 1] = '\0';
                 if (!mp_set_from_string(string, options->base, &t->value))
                 {
@@ -490,12 +481,12 @@ parse(MPEquationOptions *options, const gchar *expression, GList **tokens)
 
                 current_token = TOKEN_NONE;
                 t = token_new(TOKEN_NUMBER, token_start, i);
-                string = token_get_string(t);
+                string = token_get_text(t);
                 if (!mp_set_from_string(string, options->base, &t->value))
                     *tokens = g_list_append(*tokens, t);
                 else if (g_unichar_isalpha(c)) {
-                    /* Try as a variable instead */
-                    current_token = TOKEN_VARIABLE;
+                    /* Decode as a symbol instead */
+                    current_token = TOKEN_SYMBOL;
                 }
                 else {
                     return PARSER_ERR_INVALID;
@@ -523,7 +514,7 @@ parse(MPEquationOptions *options, const gchar *expression, GList **tokens)
             }
             break;
 
-        case TOKEN_VARIABLE:
+        case TOKEN_SYMBOL:
             if (!g_unichar_isalpha(c) && !unichar_issubdigit(c)) {
                 gchar *name;
 
@@ -547,46 +538,9 @@ parse(MPEquationOptions *options, const gchar *expression, GList **tokens)
                 else if (strcmp(name, "xor") == 0) {
                     *tokens = g_list_append(*tokens, token_new(TOKEN_BOOLEAN_XOR, token_start, i));
                 }
-                else if (function_is_defined(options, name)) {
-                    *tokens = g_list_append(*tokens, token_new(TOKEN_FUNCTION, token_start, i));
-                }
-                else if (variable_is_defined(options, name)) {
-                    Token *t;
-                    t = token_new(TOKEN_VARIABLE, token_start, i);
-                    get_variable(options, name, &t->value);
-                    *tokens = g_list_append(*tokens, t);
-                }
                 else {
-                    const gchar *j;
-                    GString *v;
-                    GList *variables = NULL;
-
-                    /* If each value is defined then is a multiple of variables */
-                    v = g_string_new("");
-                    for (j = token_start; *j; j = g_utf8_next_char(j)) {
-                        Token *t;
-
-                        g_string_truncate(v, 0);
-                        g_string_append_unichar(v, g_utf8_get_char(j));
-                        if (!variable_is_defined(options, v->str) || j == i)
-                            break;
-
-                        t = token_new(TOKEN_VARIABLE, j, g_utf8_next_char(j));
-                        get_variable(options, v->str, &t->value);
-                        variables = g_list_append(variables, t);
-                    }
-                    g_string_free(v, TRUE);
-
-                    if (j != i)
-                    {
-                        g_list_foreach(variables, (GFunc) g_free, NULL);
-                        g_list_free(variables);
-                        return PARSER_ERR_UNKNOWN_VARIABLE;
-                    }
-
-                    *tokens = g_list_concat(*tokens, variables);
+                    *tokens = g_list_append(*tokens, token_new(TOKEN_SYMBOL, token_start, i));
                 }
-
                 g_free(name);
 
                 current_token = TOKEN_NONE;
@@ -674,10 +628,8 @@ replace_block(GList **first, GList **last, GList *start, GList *end, MPNumber *r
     link = start;
     do {
         GList *next = link->next;
-        link->prev = NULL;
-        link->next = NULL;
         g_free(link->data);
-        g_list_free(link);
+        g_list_free_1(link);
         link = next;
     } while (link != end);
 }
@@ -781,7 +733,7 @@ do_function(MPEquationOptions *options, GList **first, GList **last, GList *func
         exponent = 1;
     }
     else
-        string = token_get_string(name);
+        string = token_get_text(name);
 
     get_function(options, string, &arg->value, &result);      
     g_free(string);
@@ -913,6 +865,138 @@ print_tokens(GList *first, GList *last)
 
 
 static MPErrorCode
+solve_conversion(MPEquationOptions *options, GList *first, GList *last, MPNumber *z)
+{
+    GList *i;
+    Token *x, *x_unit, *in, *z_unit;
+    char *text, *x_unit_name, *z_unit_name;
+    gboolean is_in, result = FALSE;
+
+    i = first;
+
+    x = i->data;
+    if (i == last || !has_value (x))
+        return PARSER_ERR_INVALID;
+  
+    i = i->next;
+    x_unit = i->data;
+    if (i == last || x_unit->type != TOKEN_SYMBOL)
+        return PARSER_ERR_INVALID;
+
+    i = i->next;
+    in = i->data;
+    text = token_get_text (in);
+    is_in = strcmp (text, "in") == 0;
+    g_free (text);
+    if (i == last || !is_in)
+        return PARSER_ERR_INVALID;
+
+    i = i->next;
+    z_unit = i->data;
+    if (i != last || z_unit->type != TOKEN_SYMBOL)
+        return PARSER_ERR_INVALID;
+
+    x_unit_name = token_get_text (x_unit);
+    z_unit_name = token_get_text (z_unit);
+    if (options->convert)
+        result = options->convert(&x->value, x_unit_name, z_unit_name, z, options->callback_data);
+    g_free (x_unit_name);
+    g_free (z_unit_name);
+
+    if (result)
+        return PARSER_ERR_NONE;
+    else
+        return PARSER_ERR_MP;
+}
+
+
+static GList *
+expand_symbol (MPEquationOptions *options, Token *token)
+{
+    GList *tokens = NULL;
+    gchar *name;
+
+    name = token_get_text (token);
+    if (function_is_defined(options, name)) {
+        tokens = g_list_append(tokens, token_new(TOKEN_FUNCTION, token->start, token->end));
+    }
+    else if (variable_is_defined(options, name)) {
+        Token *t;
+        t = token_new(TOKEN_VARIABLE, token->start, token->end);
+        get_variable(options, name, &t->value);
+        tokens = g_list_append(tokens, t);
+    }
+    g_free (name);
+
+    /* Check if is a multiple of variables */
+    if (!tokens) {
+        const gchar *i;
+        GString *v;
+      
+        v = g_string_new("");
+        for (i = token->start; i != token->end; i = g_utf8_next_char(i)) {
+            Token *t;
+
+            g_string_truncate(v, 0);
+            g_string_append_unichar(v, g_utf8_get_char(i));
+            if (!variable_is_defined(options, v->str))
+            {
+                g_list_free_full(tokens, g_free);
+                tokens = NULL;
+                break;
+            }
+
+            t = token_new(TOKEN_VARIABLE, i, g_utf8_next_char(i));
+            get_variable(options, v->str, &t->value);
+            tokens = g_list_append(tokens, t);
+        }
+        g_string_free(v, TRUE);
+    }
+
+    return tokens;
+}
+
+
+static MPErrorCode
+replace_symbols(MPEquationOptions *options, GList **tokens)
+{
+    GList *i;
+  
+    for (i = *tokens; i; i = i->next)
+    {
+        Token *t = i->data;
+        GList *expanded, *last_expanded;
+
+        if (t->type != TOKEN_SYMBOL)
+            continue;
+
+        /* Expand this symbol into one or more tokens */
+        expanded = expand_symbol(options, t);
+        if (!expanded)
+            return PARSER_ERR_UNKNOWN_SYMBOL;
+        last_expanded = g_list_last (expanded);
+
+        /* Replace current token */
+        expanded->prev = i->prev;      
+        if (expanded->prev)
+            expanded->prev->next = expanded;
+        else
+            *tokens = expanded;
+        last_expanded->next = i->next;
+        if (last_expanded->next)
+            last_expanded->next->prev = last_expanded;
+
+        /* Free the replaced token */
+        g_free (t);
+        g_list_free_1 (i);
+        i = last_expanded;
+    }
+
+    return PARSER_ERR_NONE;
+}
+
+
+static MPErrorCode
 solve(MPEquationOptions *options, GList *first, GList *last, MPNumber *result)
 {
     while (TRUE) {
@@ -1216,7 +1300,7 @@ solve(MPEquationOptions *options, GList *first, GList *last, MPNumber *result)
 MPErrorCode
 mp_equation_parse(const char *expression, MPEquationOptions *options, MPNumber *result, char **error_token)
 {
-    GList *tokens, *last_token;
+    GList *tokens;
     MPErrorCode error;
 
     if (!(expression && result) || strlen(expression) == 0)
@@ -1228,8 +1312,14 @@ mp_equation_parse(const char *expression, MPEquationOptions *options, MPNumber *
     if (error)
         return error;
 
-    last_token = g_list_last(tokens);
-    error = solve(options, tokens, last_token, result);
+    error = solve_conversion(options, tokens, g_list_last(tokens), result);
+    if (!error)
+        return error;
+
+    error = replace_symbols (options, &tokens);
+    if (error)
+        return error;
+    error = solve(options, tokens, g_list_last(tokens), result);
     if (error)
         return error;
 
@@ -1251,12 +1341,8 @@ mp_error_code_to_string(MPErrorCode error_code)
         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_UNKNOWN_CONVERSION:
-        return "PARSER_ERR_UNKNOWN_CONVERSION";
+    case PARSER_ERR_UNKNOWN_SYMBOL:
+        return "PARSER_ERR_UNKNOWN_SYMBOL";
     case PARSER_ERR_MP:
         return "PARSER_ERR_MP";
     default:
diff --git a/src/mp-equation.h b/src/mp-equation.h
index d6c0f24..5672f8f 100644
--- a/src/mp-equation.h
+++ b/src/mp-equation.h
@@ -19,9 +19,7 @@ typedef enum
     PARSER_ERR_NONE = 0,
     PARSER_ERR_INVALID,
     PARSER_ERR_OVERFLOW,
-    PARSER_ERR_UNKNOWN_VARIABLE,
-    PARSER_ERR_UNKNOWN_FUNCTION,
-    PARSER_ERR_UNKNOWN_CONVERSION,
+    PARSER_ERR_UNKNOWN_SYMBOL,
     PARSER_ERR_MP
 } MPErrorCode;
 
diff --git a/src/test-mp-equation.c b/src/test-mp-equation.c
index dac35ec..1afcc65 100644
--- a/src/test-mp-equation.c
+++ b/src/test-mp-equation.c
@@ -215,18 +215,18 @@ test_equations()
     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("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); /* e is a built-in 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("Aâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("Aâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("A", "", PARSER_ERR_UNKNOWN_SYMBOL); test("Aââ", "10", 0);
+    test("Bâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("Bâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("B", "", PARSER_ERR_UNKNOWN_SYMBOL); test("Bââ", "11", 0);
+    test("Câ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("Câ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("C", "", PARSER_ERR_UNKNOWN_SYMBOL); test("Cââ", "12", 0);
+    test("Dâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("Dâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("D", "", PARSER_ERR_UNKNOWN_SYMBOL); test("Dââ", "13", 0);
+    test("Eâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("Eâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("E", "", PARSER_ERR_UNKNOWN_SYMBOL); test("Eââ", "14", 0);
+    test("Fâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("Fâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("F", "", PARSER_ERR_UNKNOWN_SYMBOL); test("Fââ", "15", 0);
+    test("aâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("aâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("a", "", PARSER_ERR_UNKNOWN_SYMBOL); test("aââ", "10", 0);
+    test("bâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("bâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("b", "", PARSER_ERR_UNKNOWN_SYMBOL); test("bââ", "11", 0);
+    test("câ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("câ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("c", "", PARSER_ERR_UNKNOWN_SYMBOL); test("cââ", "12", 0);
+    test("dâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("dâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("d", "", PARSER_ERR_UNKNOWN_SYMBOL); test("dââ", "13", 0);
+    test("eâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("eâ", "", PARSER_ERR_UNKNOWN_SYMBOL); /* e is a built-in variable */              test("eââ", "14", 0);
+    test("fâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("fâ", "", PARSER_ERR_UNKNOWN_SYMBOL); test("f", "", PARSER_ERR_UNKNOWN_SYMBOL); test("fââ", "15", 0);
 
     test("+1", "1", 0);
     test("â1", "â1", 0);
@@ -286,14 +286,14 @@ test_equations()
 
     test("x", "2", 0);
     test("y", "3", 0);
-    test("z", "", PARSER_ERR_UNKNOWN_VARIABLE);
+    test("z", "", PARSER_ERR_UNKNOWN_SYMBOL);
     test("2y", "6", 0);
     test("y2", "", PARSER_ERR_INVALID);
     test("y 2", "", PARSER_ERR_INVALID);
-    test("2z", "", PARSER_ERR_UNKNOWN_VARIABLE);  
-    test("z2", "", PARSER_ERR_UNKNOWN_VARIABLE);
-    test("z 2", "", PARSER_ERR_UNKNOWN_VARIABLE);
-    test("z(2)", "", PARSER_ERR_UNKNOWN_VARIABLE);
+    test("2z", "", PARSER_ERR_UNKNOWN_SYMBOL);  
+    test("z2", "", PARSER_ERR_UNKNOWN_SYMBOL);
+    test("z 2", "", PARSER_ERR_UNKNOWN_SYMBOL);
+    test("z(2)", "", PARSER_ERR_UNKNOWN_SYMBOL);
     test("yÂ", "9", 0);
     test("2yÂ", "18", 0);
     test("xÃy", "6", 0);



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