[gnome-calculator] GCalc: adding Parameter recognition capabilities



commit a322a7a824b86fea1bf84abbc43a4fd44f971038
Author: Daniel Espinosa <esodan gmail com>
Date:   Wed Oct 16 18:53:45 2019 -0500

    GCalc: adding Parameter recognition capabilities

 gcalc/gcalc-expression.vala | 15 +++++++++
 gcalc/gcalc-gparameter.vala | 46 +++++++++++++++++++++++++++
 gcalc/gcalc-gparser.vala    | 37 ++++++++++++++++++----
 gcalc/gcalc-gvariable.vala  |  8 ++---
 gcalc/gcalc-parameter.vala  | 31 ++++++++++++++++++
 gcalc/gcalc-variable.vala   |  6 ++++
 gcalc/meson.build           |  2 ++
 tests/gcalc-parsing.vala    | 76 +++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 211 insertions(+), 10 deletions(-)
---
diff --git a/gcalc/gcalc-expression.vala b/gcalc/gcalc-expression.vala
index 6b83c5d5..b83315db 100644
--- a/gcalc/gcalc-expression.vala
+++ b/gcalc/gcalc-expression.vala
@@ -18,10 +18,25 @@
  * Authors:
  *      Daniel Espinosa <esodan gmail com>
  */
+/**
+ * A part of a math equation
+ */
 public interface GCalc.Expression : Object {
+  /**
+   * Parent of the expression
+   */
   public abstract weak Expression parent { get; set; }
+  /**
+   * Child expressions
+   */
   public abstract ExpressionContainer expressions { get; }
+  /**
+   * Creates a string representation of the expression
+   */
   public abstract string to_string ();
+  /**
+   * Solves the expression and returning a {@link Result}
+   */
   public abstract Result solve ();
 }
 
diff --git a/gcalc/gcalc-gparameter.vala b/gcalc/gcalc-gparameter.vala
new file mode 100644
index 00000000..6192fbac
--- /dev/null
+++ b/gcalc/gcalc-gparameter.vala
@@ -0,0 +1,46 @@
+/* gcalc-gparameter.vala
+ *
+ * Copyright (C) 2019  Daniel Espinosa <esodan gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *      Daniel Espinosa <esodan gmail com>
+ */
+public class GCalc.GParameter : GCalc.GVariable, Parameter {
+
+  public GParameter (string name) {
+    base (name);
+  }
+
+  internal void set_value (GLib.Value val) throws GLib.Error {
+    Constant c = new GConstant.integer (0);
+    if (val.holds (GLib.Type.INT)) {
+      c = new GConstant.integer ((int) val);
+    } else if (val.holds (GLib.Type.DOUBLE)) {
+      c = new GConstant.@double ((double) val);
+    } else if (val.holds (GLib.Type.FLOAT)) {
+      c = new GConstant.@double ((double) ((float) val));
+    } else if (val.type ().is_a (typeof (GCalc.Constant))) {
+      c = (GCalc.Constant) ((Object) val);
+    }
+    @value = c;
+  }
+
+  internal GLib.Value get_value () {
+    var v = GLib.Value (typeof (GCalc.Constant));
+    v = @value;
+    return v;
+  }
+}
diff --git a/gcalc/gcalc-gparser.vala b/gcalc/gcalc-gparser.vala
index 35467f34..9c95d685 100644
--- a/gcalc/gcalc-gparser.vala
+++ b/gcalc/gcalc-gparser.vala
@@ -18,11 +18,15 @@
  * Authors:
  *      Daniel Espinosa <esodan gmail com>
  */
-
+/**
+ * Takes a string an create a tree of {@link Expression} objects representing
+ * a math equation.
+ */
 public class GCalc.GParser : Object {
   Expression current = null;
   Expression current_parent = null;
   Expression top_parent = null;
+  bool enable_parameter = false;
   Gee.ArrayList<TokenType> expected = new Gee.ArrayList<TokenType> ();
   GLib.Scanner scanner;
 
@@ -40,7 +44,11 @@ public class GCalc.GParser : Object {
     scanner.config.scan_hex_dollar = false;
     scanner.config.numbers_2_int = false;
   }
-
+  /**
+   * Creates a {@link MathEquation} and adds it to given
+   * {@link MathEquationManager} including all Variables and
+   * parameters found in the parser expression.
+   */
   public void parse (string str, MathEquationManager eqman) throws GLib.Error {
     TokenType token = TokenType.NONE;
     GMathEquation eq = new GMathEquation ();
@@ -48,6 +56,7 @@ public class GCalc.GParser : Object {
     current = null;
     current_parent = null;
     top_parent = null;
+    enable_parameter = false;
     while (token != TokenType.EOF) {
       token = read_token ();
       if (token == TokenType.EOF) {
@@ -90,6 +99,10 @@ public class GCalc.GParser : Object {
             throw new ParserError.INVALID_TOKEN_ERROR ("Found an unexpected function definition expression");
           } else {
             var v = new GVariable (n) as Expression;
+            if (enable_parameter) {
+              v = new GParameter (n) as Expression;
+              enable_parameter = false;
+            }
             var sv = eqman.find_variable (n) as Variable;
             if (sv == null) {
               sv = eq.variables.find_named (n) as Variable;
@@ -313,7 +326,10 @@ public class GCalc.GParser : Object {
         case TokenType.INTERR:
         // Hash
         case TokenType.HASH:
-          throw new ParserError.INVALID_TOKEN_ERROR ("Found an unexpected expression");
+          throw new ParserError.INVALID_TOKEN_ERROR ("Found an unexpected expression: '%s'", token.to_string 
());
+        case TokenType.CURRENCY_SYMBOL:
+          enable_parameter = true;
+          break;
       }
     }
     eqman.equations.add (eq);
@@ -395,7 +411,10 @@ public class GCalc.GParser : Object {
       expected.clear ();
     }
   }
-  public TokenType read_token () {
+  /**
+   * Reads a token at a given position
+   */
+  protected TokenType read_token () {
     GLib.TokenType t = scanner.get_next_token ();
     switch (t) {
     case GLib.TokenType.IDENTIFIER:
@@ -438,12 +457,17 @@ public class GCalc.GParser : Object {
           return TokenType.OPEN_BRACKET;
         case ']':
           return TokenType.CLOSE_BRACKET;
+        case '$':
+          return TokenType.CURRENCY_SYMBOL;
       }
       break;
     }
     return TokenType.NONE;
   }
-  public string token_to_string () {
+  /**
+   * Creates a string representation of the current {@link TokenType}
+   */
+  protected string token_to_string () {
     GLib.TokenType t = scanner.cur_token ();
     switch (t) {
     case GLib.TokenType.IDENTIFIER:
@@ -504,7 +528,8 @@ public class GCalc.GParser : Object {
     DOT,
     ELLIPSIS,
     INTERR,
-    HASH
+    HASH,
+    CURRENCY_SYMBOL
   }
 }
 
diff --git a/gcalc/gcalc-gvariable.vala b/gcalc/gcalc-gvariable.vala
index bd9b3a3d..72c202f4 100644
--- a/gcalc/gcalc-gvariable.vala
+++ b/gcalc/gcalc-gvariable.vala
@@ -21,8 +21,8 @@
 public class GCalc.GVariable : GExpression, Variable, Hashable {
 
   public string name { get; construct set; }
-  public Constant value { get; set; }
-  public Variable bind { get; set; }
+  internal Constant value { get; set; }
+  internal Variable bind { get; set; }
 
   construct {
     _value = new GConstant.@double (0.0);
@@ -31,11 +31,11 @@ public class GCalc.GVariable : GExpression, Variable, Hashable {
     this.name = name;
   }
   // Expression
-  public override string to_string () {
+  internal override string to_string () {
     return name;
   }
   // Hashable
-  public uint hash () {
+  internal uint hash () {
     return name.hash ();
   }
 }
diff --git a/gcalc/gcalc-parameter.vala b/gcalc/gcalc-parameter.vala
new file mode 100644
index 00000000..80c26c3d
--- /dev/null
+++ b/gcalc/gcalc-parameter.vala
@@ -0,0 +1,31 @@
+/* gcalc-expresion.vala
+ *
+ * Copyright (C) 2019  Daniel Espinosa <esodan gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *      Daniel Espinosa <esodan gmail com>
+ */
+/**
+ * A parameter is a {@link Variable} holding a value, that is not
+ * expected to be resolved as part of an {@link Expression} evaluation,
+ * but by asigning its value.
+ *
+ * Currently the value will be converted to a {@link Constant} if possible.
+ */
+public interface GCalc.Parameter : Object, Variable {
+  public abstract void set_value (GLib.Value val) throws GLib.Error;
+  public abstract GLib.Value get_value ();
+}
diff --git a/gcalc/gcalc-variable.vala b/gcalc/gcalc-variable.vala
index 7a70bb10..721136b8 100644
--- a/gcalc/gcalc-variable.vala
+++ b/gcalc/gcalc-variable.vala
@@ -18,6 +18,9 @@
  * Authors:
  *      Daniel Espinosa <esodan gmail com>
  */
+/**
+ * A variable that can be evaluated from an {@link Expression}
+ */
 public interface GCalc.Variable : Object, Expression {
   public abstract string name { get; construct set; }
   public abstract Constant @value { get; set; }
@@ -28,6 +31,9 @@ public interface GCalc.Variable : Object, Expression {
     if (bind != null) {
       return bind.evaluate ();
     }
+    if (this is Parameter) {
+      return @value;
+    }
     if (parent == null) {
       throw new VariableError.INVALID_PARENT ("Can't access to Variable's expression definition. Invalid 
parent. Expected Assign operator");
     }
diff --git a/gcalc/meson.build b/gcalc/meson.build
index 0992a95a..a4cdfbb4 100644
--- a/gcalc/meson.build
+++ b/gcalc/meson.build
@@ -90,6 +90,7 @@ sources = files([
        'gcalc-gmath-equation-manager.vala',
        'gcalc-gminus.vala',
        'gcalc-gmultiply.vala',
+       'gcalc-gparameter.vala',
        'gcalc-gparser.vala',
        'gcalc-gplus.vala',
        'gcalc-gpolynomial.vala',
@@ -106,6 +107,7 @@ sources = files([
        'gcalc-minus.vala',
        'gcalc-multiply.vala',
        'gcalc-operator.vala',
+       'gcalc-parameter.vala',
        'gcalc-plus.vala',
        'gcalc-polynomial.vala',
        'gcalc-pow.vala',
diff --git a/tests/gcalc-parsing.vala b/tests/gcalc-parsing.vala
index e71e8380..843a0017 100644
--- a/tests/gcalc-parsing.vala
+++ b/tests/gcalc-parsing.vala
@@ -1071,6 +1071,82 @@ class Tests {
         warning ("Error: %s", error.message);
       }
     });
+    Test.add_func ("/gcalc/parser/parameter/equations",
+    ()=>{
+      try {
+        var parser = new GParser ();
+        var eqman = new GMathEquationManager ();
+        parser.parse ("x=$param1", eqman);
+        parser.parse ("x", eqman);
+        assert (eqman.equations.get_n_items () == 2);
+        var eq = eqman.equations.get_item (0) as MathEquation;
+        assert (eq != null);
+        assert (eq.expressions.get_n_items () == 1);
+        var a = eq.expressions.get_item (0) as Assign;
+        assert (a != null);
+        assert (a.expressions.get_n_items () == 2);
+        var v = a.expressions.get_item (0) as Variable;
+        assert (v != null);
+        assert (v.name == "x");
+        var e = a.expressions.get_item (1) as Polynomial;
+        assert (e != null);
+        assert (e.expressions.get_n_items () == 1);
+        var t = e.expressions.get_item (0) as Term;
+        assert (t != null);
+        var ev = t.expressions.get_item (0) as Variable;
+        assert (ev != null);
+        var p = ev as GCalc.Parameter;
+        assert (p != null);
+        assert (p.name == "param1");
+        var eq2 = eqman.equations.get_item (1) as MathEquation;
+        assert (eq2 != null);
+        message (eq2.to_string ());
+        assert (eq2.expressions.get_n_items () == 1);
+        var e2 = eq2.expressions.get_item (0) as Polynomial;
+        assert (e2 != null);
+        assert (e2.expressions.get_n_items () == 1);
+        var t2 = e2.expressions.get_item (0) as Term;
+        assert (t2 != null);
+        var v2 = t2.expressions.get_item (0) as Variable;
+        assert (v2 != null);
+      } catch (GLib.Error error) {
+        warning ("Error: %s", error.message);
+      }
+    });
+    Test.add_func ("/gcalc/parser/parameter/equations/evaluate",
+    ()=>{
+      try {
+        var parser = new GParser ();
+        var eqman = new GMathEquationManager ();
+        parser.parse ("x=$param1", eqman);
+        parser.parse ("x", eqman);
+        assert (eqman.equations.get_n_items () == 2);
+        var eq = eqman.equations.get_item (0) as MathEquation;
+        assert (eq != null);
+        var r = eq.solve ();
+        assert (r.expression != null);
+        assert (r.expression is Constant);
+        var cr = r.expression as Constant;
+        assert (cr != null);
+        assert (cr.real () == 0.0);
+        var p = eq.variables.find_named ("param1") as GCalc.Parameter;
+        assert (p != null);
+        p.set_value (10.0);
+        r = eq.solve ();
+        assert (r.expression != null);
+        assert (r.expression is Constant);
+        cr = r.expression as Constant;
+        assert (cr != null);
+        assert (cr.real () == 10.0);
+        var eq2 = eqman.equations.get_item (1) as MathEquation;
+        assert (eq2 != null);
+        var cr2 = r.expression as Constant;
+        assert (cr2 != null);
+        assert (cr2.real () == 10.0);
+      } catch (GLib.Error error) {
+        warning ("Error: %s", error.message);
+      }
+    });
     return Test.run ();
   }
 }


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