[gcalctool] Support conversions in display



commit d44a18eeba84b483e37ffc61a982f293a0a42a5b
Author: Robert Ancell <robert ancell gmail com>
Date:   Tue Oct 27 13:00:54 2009 +1100

    Support conversions in display

 ChangeLog                |    4 +
 src/currency.h           |    5 ++
 src/display.c            |  176 ++++++++++++++++++++++++++++++++++++++++++++++
 src/mp-equation-lexer.l  |    2 +
 src/mp-equation-parser.y |   17 ++++-
 src/mp-equation.c        |    2 +
 src/mp-equation.h        |    4 +
 7 files changed, 209 insertions(+), 1 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index f753fce..0d8218b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -9,6 +9,10 @@ gcalctool change history.
 
 2009-10-27 Robert Ancell <robert ancell gmail com>
 
+    * Support conversions in display (e.g. "1 AUD in USD" or "6.2 inches in meters")
+
+2009-10-27 Robert Ancell <robert ancell gmail com>
+
     * README:
       NEWS:
       configure.in:
diff --git a/src/currency.h b/src/currency.h
index c08c216..2ac8488 100644
--- a/src/currency.h
+++ b/src/currency.h
@@ -1,3 +1,6 @@
+#ifndef CURRENCY_H
+#define CURRENCY_H
+
 #include <glib/gi18n.h>
 
 #include "mp.h"
@@ -72,3 +75,5 @@ void currency_convert(const MPNumber *from_amount,
 
 /* Frees up all allocated resources */
 void currency_free_resources();
+
+#endif /* CURRENCY_H */
diff --git a/src/display.c b/src/display.c
index 6038bc6..08f9240 100644
--- a/src/display.c
+++ b/src/display.c
@@ -31,6 +31,7 @@
 #include "ui.h"
 #include "mp-equation.h"
 #include "register.h"
+#include "currency.h"
 
 static GCDisplayState *
 get_state(GCDisplay *display)
@@ -753,6 +754,175 @@ set_variable(const char *name, const MPNumber *x, void *data)
 
 
 static int
+do_convert(const char *units[][2], const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z)
+{
+    int x_index, z_index;
+    MPNumber x_factor, z_factor;
+    
+    for (x_index = 0; units[x_index][0] != NULL && strcmp(units[x_index][0], x_units) != 0; x_index++);
+    if (units[x_index][0] == NULL)
+        return 0;
+    for (z_index = 0; units[z_index][0] != NULL && strcmp(units[z_index][0], z_units) != 0; z_index++);
+    if (units[z_index][0] == NULL)
+        return 0;
+
+    mp_set_from_string(units[x_index][1], &x_factor);
+    mp_set_from_string(units[z_index][1], &z_factor);
+    mp_multiply(x, &x_factor, z);
+    mp_divide(z, &z_factor, z);
+
+    return 1;
+}
+
+
+static int
+convert(const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z, void *data)
+{
+    const char *length_units[][2] = {
+        {"parsec",     "30857000000000000"},
+        {"parsecs",    "30857000000000000"},
+        {"pc",         "30857000000000000"},
+        {"lightyear",   "9460730472580800"},
+        {"lightyears",  "9460730472580800"},
+        {"ly",          "9460730472580800"},
+        {"au",              "149597870691"},
+        {"nm",                   "1852000"},
+        {"mile",                    "1609.344"},
+        {"miles",                   "1609.344"},
+        {"m",                       "1609.344"},
+        {"kilometer",               "1000"},
+        {"kilometers",              "1000"},
+        {"km",                      "1000"},
+        {"kms",                     "1000"},
+        {"cable",                    "219.456"},
+        {"cables",                   "219.456"},
+        {"cb",                       "219.456"},
+        {"fathom",                     "1.8288"},
+        {"fathoms",                    "1.8288"},
+        {"ftm",                        "1.8288"},
+        {"meter",                      "1"},
+        {"meters",                     "1"},
+        {"m",                          "1"},
+        {"yard",                       "0.9144"},
+        {"yd",                         "0.9144"},
+        {"foot",                       "0.3048"},
+        {"feet",                       "0.3048"},
+        {"ft",                         "0.3048"},
+        {"inch",                       "0.0254"},
+        {"inches",                     "0.0254"},
+        {"centimeter",                 "0.01"},
+        {"centimeters",                "0.01"},
+        {"cm",                         "0.01"},
+        {"cms",                        "0.01"},
+        {"millimeter",                 "0.001"},
+        {"millimeters",                "0.001"},
+        {"mm",                         "0.001"},
+        {"micrometer",                 "0.000001"},
+        {"micrometers",                "0.000001"},
+        {"um",                         "0.000001"},
+        {"nanometer",                  "0.000000001"},
+        {"nanometers",                 "0.000000001"},
+        {NULL, NULL}
+    };
+
+    const char *area_units[][2] = {
+        {"hectare",         "10000"},
+        {"hectares",        "10000"},
+        {"acre",             "4046.8564224"},
+        {"acres",            "4046.8564224"},
+        {"m²",                  "1"},
+        {"cm²",                 "0.001"},
+        {NULL, NULL}
+    };
+
+    const char *volume_units[][2] = {
+        {"cm³",              "1000"},
+        {"gallon",              "3.785412"},
+        {"gallons",             "3.785412"},
+        {"gal",                 "3.785412"},
+        {"litre",               "1"},
+        {"litres",              "1"},
+        {"liter",               "1"},
+        {"liters",              "1"},
+        {"L",                   "1"},
+        {"quart",               "0.9463529"},
+        {"quarts",              "0.9463529"},
+        {"qt",                  "0.9463529"},
+        {"pint",                "0.4731765"},
+        {"pints",               "0.4731765"},
+        {"pt",                  "0.4731765"},
+        {"millilitre",          "0.001"},
+        {"millilitres",         "0.001"},
+        {"milliliter",          "0.001"},
+        {"milliliters",         "0.001"},
+        {"mL",                  "0.001"},
+        {"m³",                  "0.001"},
+        {NULL, NULL}
+    };
+
+    const char *weight_units[][2] = {
+        {"tonne",            "1000"},
+        {"tonnes",           "1000"},
+        {"kilograms",           "1"},
+        {"kilogramme",          "1"},
+        {"kilogrammes",         "1"},
+        {"kg",                  "1"},
+        {"kgs",                 "1"},
+        {"pound",               "0.45359237"},
+        {"pounds",              "0.45359237"},
+        {"lb",                  "0.45359237"},
+        {"ounce",               "0.002834952"},
+        {"ounces",              "0.002834952"},
+        {"oz",                  "0.002834952"},
+        {"gram",                "0.001"},
+        {"grams",               "0.001"},
+        {"gramme",              "0.001"},
+        {"grammes",             "0.001"},
+        {"g",                   "0.001"},
+        {NULL, NULL}
+    };
+    
+    const char *time_units[][2] = {
+        {"year",         "31557600"},
+        {"years",        "31557600"},
+        {"day",             "86400"},
+        {"days",            "86400"},
+        {"hour",             "3600"},
+        {"hours",            "3600"},
+        {"minute",             "60"},
+        {"minutes",            "60"},
+        {"second",              "1"},
+        {"seconds",             "1"},
+        {"s",                   "1"},
+        {"millisecond",         "0.001"},
+        {"milliseconds",        "0.001"},
+        {"ms",                  "0.001"},
+        {"microsecond",         "0.000001"},
+        {"microseconds",        "0.000001"},
+        {"us",                  "0.000001"},
+        {NULL, NULL}
+    };
+    
+    /* See if currency */
+    currency_load_rates();
+    if (currency_get_index(x_units) >= 0 && currency_get_index(z_units) >= 0)
+    {
+        currency_convert(x, currency_get_index(x_units), currency_get_index(z_units), z);
+        return 1;
+    }
+    
+    if (do_convert(length_units, x, x_units, z_units, z) ||
+        do_convert(area_units, x, x_units, z_units, z) ||
+        do_convert(volume_units, x, x_units, z_units, z) ||
+        do_convert(weight_units, x, x_units, z_units, z) ||
+        do_convert(time_units, x, x_units, z_units, z))
+        return 1;
+
+    return 0;
+}
+
+
+static int
 parse(GCDisplay *display, const char *text, MPNumber *z, char **error_token)
 {
     MPEquationOptions options;
@@ -762,6 +932,7 @@ parse(GCDisplay *display, const char *text, MPNumber *z, char **error_token)
     options.angle_units = display->angle_unit;
     options.get_variable = get_variable;
     options.set_variable = set_variable;
+    options.convert = convert;
     options.callback_data = display;
 
     return mp_equation_parse(text, &options, z, error_token);
@@ -1038,6 +1209,11 @@ display_do_function(GCDisplay *display, int function, gpointer arg, int cursor_s
                         free(error_token);
                         break;
 
+                    case PARSER_ERR_UNKNOWN_CONVERSION:
+                        /* Translators: Error displayed to user when an conversion with unknown units is attempted */
+                        message = g_strdup_printf(_("Unknown conversion"));
+                        break;
+
                     case PARSER_ERR_MP:
                         message = mp_get_error();
                         break;
diff --git a/src/mp-equation-lexer.l b/src/mp-equation-lexer.l
index fc39e89..c3f7318 100644
--- a/src/mp-equation-lexer.l
+++ b/src/mp-equation-lexer.l
@@ -70,6 +70,7 @@ AND  "â?§"|[aA][nN][dD]
 OR   "â?¨"|[oO][rR]
 XOR  "â??"|[xX][oO][rR]
 NOT  "¬"|"~"|[nN][oO][tT]
+IN   [iI][nN]
 
 %%
 
@@ -85,6 +86,7 @@ NOT  "¬"|"~"|[nN][oO][tT]
 {AND}       {return tAND;}
 {OR}        {return tOR;}
 {XOR}       {return tXOR;}
+{IN}        {return tIN;}
 {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; }
diff --git a/src/mp-equation-parser.y b/src/mp-equation-parser.y
index 43b502f..d398e19 100644
--- a/src/mp-equation-parser.y
+++ b/src/mp-equation-parser.y
@@ -70,6 +70,16 @@ static void do_not(yyscan_t yyscanner, const MPNumber *x, MPNumber *z)
     mp_not(x, _mp_equation_get_extra(yyscanner)->options->wordlen, z);
 }
 
+static void do_conversion(yyscan_t yyscanner, const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z)
+{
+    void *data = _mp_equation_get_extra(yyscanner)->options->callback_data;
+
+    if (_mp_equation_get_extra(yyscanner)->options->convert == NULL
+        || !_mp_equation_get_extra(yyscanner)->options->convert(x, x_units, z_units, z, data)) {
+        set_error(yyscanner, PARSER_ERR_UNKNOWN_CONVERSION);
+    }
+}
+
 %}
 
 %pure-parser
@@ -96,7 +106,8 @@ static void do_not(yyscan_t yyscanner, const MPNumber *x, MPNumber *z)
 %left BOOLEAN_OPERATOR
 %left PERCENTAGE
 %left UNARY_MINUS
-%right '^' tINVERSE '!'
+%right '^' tINVERSE '!' '|'
+%left tIN
 
 %type <int_t> exp variable
 %start statement
@@ -107,8 +118,12 @@ statement:
   exp { set_result(yyscanner, &$1); }
 | exp '=' { set_result(yyscanner, &$1); }
 | tVARIABLE '=' exp {set_variable(yyscanner, $1, &$3); set_result(yyscanner, &$3); }
+| tNUMBER tVARIABLE tIN tVARIABLE { MPNumber t; do_conversion(yyscanner, &$1, $2, $4, &t); set_result(yyscanner, &t); free($2); free($4); }
+| tVARIABLE tIN tVARIABLE { MPNumber x, t; mp_set_from_integer(1, &x); do_conversion(yyscanner, &x, $1, $3, &t); set_result(yyscanner, &t); free($1); free($3); }
 ;
 
+/* |x| gets confused and thinks = |x|(...||) */
+
 exp:
   '(' exp ')' {mp_set_from_mp(&$2, &$$);}
 | '|' exp '|' {mp_abs(&$2, &$$);}
diff --git a/src/mp-equation.c b/src/mp-equation.c
index 1edfec4..03567d5 100644
--- a/src/mp-equation.c
+++ b/src/mp-equation.c
@@ -267,6 +267,8 @@ mp_error_code_to_string(MPErrorCode error_code)
         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_MP:
         return "PARSER_ERR_MP";
     default:
diff --git a/src/mp-equation.h b/src/mp-equation.h
index 666353d..4f1ad2e 100644
--- a/src/mp-equation.h
+++ b/src/mp-equation.h
@@ -29,6 +29,7 @@ typedef enum
     PARSER_ERR_OVERFLOW,
     PARSER_ERR_UNKNOWN_VARIABLE,
     PARSER_ERR_UNKNOWN_FUNCTION,
+    PARSER_ERR_UNKNOWN_CONVERSION,
     PARSER_ERR_MP
 } MPErrorCode;
 
@@ -54,6 +55,9 @@ typedef struct {
 
     /* Function to solve functions */
     int (*get_function)(const char *name, const MPNumber *x, MPNumber *z, void *data);
+
+    /* Function to convert units */
+    int (*convert)(const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z, void *data);
 } MPEquationOptions;
 
 MPErrorCode mp_equation_parse(const char *expression, MPEquationOptions *options, MPNumber *result, char **error_token);



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