[gnome-calculator/60-split-out-a-backend-library] gcalc: added parenthesis (group) term support



commit 9ce1cc5e629deb2924c50d605de32a95d30818d0
Author: Daniel Espinosa Ortiz <esodan gmail com>
Date:   Fri Jan 4 12:08:37 2019 -0600

    gcalc: added parenthesis (group) term support

 gcalc/gcalc-expression-container.vala | 17 +++++++++++
 gcalc/gcalc-expression.vala           |  1 +
 gcalc/gcalc-gexpression.vala          |  5 ++++
 gcalc/gcalc-ggroup.vala               | 54 +++++++++++++++++++++++++++++++++++
 gcalc/gcalc-group.vala                | 30 +++++++++++++++++++
 gcalc/gcalc-parser.vala               | 52 +++++++++++++++++++++++++++++----
 gcalc/meson.build                     |  2 ++
 tests/gcalc-parsing.vala              | 54 +++++++++++++++++++++++++++++++++++
 8 files changed, 209 insertions(+), 6 deletions(-)
---
diff --git a/gcalc/gcalc-expression-container.vala b/gcalc/gcalc-expression-container.vala
index fb0623d5..a948639e 100644
--- a/gcalc/gcalc-expression-container.vala
+++ b/gcalc/gcalc-expression-container.vala
@@ -19,6 +19,23 @@
  *      Daniel Espinosa <esodan gmail com>
  */
 public class GCalc.ExpressionContainer : Gee.ArrayList<Expression>, GLib.ListModel {
+  public weak Expression parent { get; set; }
+  public new void add (Expression exp) {
+    (this as Gee.ArrayList<Expression>).add (exp);
+    exp.parent = parent;
+  }
+  public new Expression remove_at (int index) {
+    var r = (this as Gee.ArrayList<Expression>).remove_at (index);
+    if (r != null) {
+      r.parent = null;
+    }
+    return r;
+  }
+  public new Expression remove (Expression exp) {
+    var i = (this as Gee.ArrayList<Expression>).index_of (exp);
+    return remove_at (i);
+  }
+  // GLib.ListModel
   public Object? get_item (uint position) {
     return (this as Gee.ArrayList<Expression>).@get ((int) position) as Object;
   }
diff --git a/gcalc/gcalc-expression.vala b/gcalc/gcalc-expression.vala
index 4e86b3fb..6b83c5d5 100644
--- a/gcalc/gcalc-expression.vala
+++ b/gcalc/gcalc-expression.vala
@@ -19,6 +19,7 @@
  *      Daniel Espinosa <esodan gmail com>
  */
 public interface GCalc.Expression : Object {
+  public abstract weak Expression parent { get; set; }
   public abstract ExpressionContainer expressions { get; }
   public abstract string to_string ();
   public abstract Result solve ();
diff --git a/gcalc/gcalc-gexpression.vala b/gcalc/gcalc-gexpression.vala
index aac356d4..8238e9bf 100644
--- a/gcalc/gcalc-gexpression.vala
+++ b/gcalc/gcalc-gexpression.vala
@@ -20,6 +20,11 @@
  */
 public class GCalc.GExpression : Object, Expression {
   ExpressionContainer exps = new ExpressionContainer ();
+  construct {
+    exps.parent = this;
+  }
+  // Expression
+  public weak Expression parent { get; set; }
   public ExpressionContainer expressions { get { return exps; } }
   public new virtual string to_string () {
     return "";
diff --git a/gcalc/gcalc-ggroup.vala b/gcalc/gcalc-ggroup.vala
new file mode 100644
index 00000000..b9e7b074
--- /dev/null
+++ b/gcalc/gcalc-ggroup.vala
@@ -0,0 +1,54 @@
+/* gcalc-ggroup.vala
+ *
+ * Copyright (C) 2018  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.GGroup : GExpression, Group {
+  public Group.Level level { get; set; }
+  public bool closed { get; set; }
+  public override string to_string () {
+    string s = "";
+    switch (level) {
+      case ONE:
+        s = "(";
+        break;
+      case TWO:
+        s = "[";
+        break;
+      case THREE:
+        s = "{";
+        break;
+    }
+    foreach (Expression e in expressions) {
+      s += e.to_string ();
+    }
+    switch (level) {
+      case ONE:
+        s += ")";
+        break;
+      case TWO:
+        s += "]";
+        break;
+      case THREE:
+        s += "}";
+        break;
+    }
+    return s;
+  }
+}
+
diff --git a/gcalc/gcalc-group.vala b/gcalc/gcalc-group.vala
new file mode 100644
index 00000000..c56c98b8
--- /dev/null
+++ b/gcalc/gcalc-group.vala
@@ -0,0 +1,30 @@
+/* gcalc-group.vala
+ *
+ * Copyright (C) 2018  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 interface GCalc.Group : Object, Expression {
+  public abstract Level level { get; set; }
+  public abstract bool closed { get; set; }
+  public enum Level {
+    ONE,
+    TWO,
+    THREE
+  }
+}
+
diff --git a/gcalc/gcalc-parser.vala b/gcalc/gcalc-parser.vala
index f64575c8..8d23e6a0 100644
--- a/gcalc/gcalc-parser.vala
+++ b/gcalc/gcalc-parser.vala
@@ -178,6 +178,12 @@ public class GCalc.Parser : Object {
             current_parent.expressions.add (cexp);
             expected.clear ();
             current = cexp;
+          } else if (current is Term && current_parent is Polynomial && top_parent is Group) {
+            current.expressions.add (cexp);
+            top_parent = current_parent;
+            current_parent = current;
+            current = cexp;
+            expected.clear ();
           }
           break;
         case Vala.TokenType.PERCENT:
@@ -221,7 +227,21 @@ public class GCalc.Parser : Object {
           }
           break;
         case Vala.TokenType.OPEN_PARENS:
-          if (current is Function) {
+          if (current == null) {
+            var exp = new GPolynomial ();
+            eq.expressions.add (exp);
+            var t = new GTerm ();
+            exp.expressions.add (t);
+            var g = new GGroup ();
+            t.expressions.add (g);
+            var exp2 = new GPolynomial ();
+            var t2 = new GTerm ();
+            exp2.expressions.add (t2);
+            g.expressions.add (exp2);
+            current = t2;
+            current_parent = exp2;
+            top_parent = g;
+          } else if (current is Function) {
             var fexp = new GPolynomial ();
             current = fexp;
             expected.clear ();
@@ -232,6 +252,30 @@ public class GCalc.Parser : Object {
           }
           break;
         case Vala.TokenType.CLOSE_PARENS:
+          if (current == null) {
+            throw new ParserError.INVALID_TOKEN_ERROR ("Found an unexpected expression while closing 
parenthesis");
+          }
+          bool foundp = false;
+          var par = current;
+          while (par != null) {
+            if (par is Group) {
+              foundp = true;
+              ((Group) par).closed = true;
+              break;
+            }
+            par = par.parent;
+          }
+          if (foundp) {
+            current = par.parent; // Term
+            current_parent = current.parent;
+            top_parent = current_parent.parent;
+          }
+          break;
+        // braces
+        case Vala.TokenType.CLOSE_BRACE:
+        case Vala.TokenType.CLOSE_BRACKET:
+        case Vala.TokenType.OPEN_BRACE:
+        case Vala.TokenType.OPEN_BRACKET:
           break;
         case Vala.TokenType.STRING_LITERAL:
           message ("Found string literal");
@@ -266,13 +310,9 @@ public class GCalc.Parser : Object {
         case Vala.TokenType.OP_SHIFT_LEFT:
         // Carret?
         case Vala.TokenType.CARRET:
-        // braces
-        case Vala.TokenType.CLOSE_BRACE:
-        case Vala.TokenType.CLOSE_BRACKET:
+        // templates and regex
         case Vala.TokenType.CLOSE_REGEX_LITERAL:
         case Vala.TokenType.CLOSE_TEMPLATE:
-        case Vala.TokenType.OPEN_BRACE:
-        case Vala.TokenType.OPEN_BRACKET:
         case Vala.TokenType.OPEN_REGEX_LITERAL:
         case Vala.TokenType.OPEN_TEMPLATE:
         //
diff --git a/gcalc/meson.build b/gcalc/meson.build
index 2b0936c2..42101824 100644
--- a/gcalc/meson.build
+++ b/gcalc/meson.build
@@ -60,6 +60,8 @@ sources = files([
        'gcalc-gplus.vala',
        'gcalc-gpolynomial.vala',
        'gcalc-gresult.vala',
+       'gcalc-group.vala',
+       'gcalc-ggroup.vala',
        'gcalc-gsolver.vala',
        'gcalc-gterm.vala',
        'gcalc-gvariable.vala',
diff --git a/tests/gcalc-parsing.vala b/tests/gcalc-parsing.vala
index 515a05ec..6ab031e1 100644
--- a/tests/gcalc-parsing.vala
+++ b/tests/gcalc-parsing.vala
@@ -509,6 +509,60 @@ class Tests {
       Constant c = new GConstant.@double (-1.0) as Constant;
       assert ("-1" in c.to_string ());
     });
+    Test.add_func ("/gcalc/parser/term/parenthesis",
+    ()=>{
+      try {
+        var parser = new Parser ();
+        var eqman = new GMathEquationManager ();
+        parser.parse ("(1)", eqman);
+        assert (eqman.equations.get_n_items () == 1);
+        var eq = eqman.equations.get_item (0) as MathEquation;
+        assert (eq != null);
+        assert (eq.expressions.get_n_items () == 1);
+        var p = eq.expressions.get_item (0) as Polynomial;
+        assert (p != null);
+        assert (p.expressions.get_n_items () == 1);
+        var t = p.expressions.get_item (0) as Term;
+        assert (t != null);
+        assert (t.expressions.get_n_items () == 1);
+        var g = t.expressions.get_item (0) as Group;
+        assert (g != null);
+        assert (g.closed);
+        assert (g.expressions.get_n_items () == 1);
+        var p1 = g.expressions.get_item (0) as Polynomial;
+        assert (p1 != null);
+        assert (p1.expressions.get_n_items () == 1);
+        var t1 = p1.expressions.get_item (0) as Term;
+        assert (t1 != null);
+        assert (t1.expressions.get_n_items () == 1);
+        var c = t1.expressions.get_item (0) as Constant;
+        assert (c != null);
+      } catch (GLib.Error error) {
+        warning ("Error: %s", error.message);
+      }
+    });
+    Test.add_func ("/gcalc/parser/term/parenthesis/errors",
+    ()=>{
+      var parser = new Parser ();
+      var eqman1 = new GMathEquationManager ();
+      try {
+        parser.parse ("(", eqman1);
+      } catch (GLib.Error error) {
+        message ("Correctly catched grouping error: %s", error.message);
+      }
+      var eqman2 = new GMathEquationManager ();
+      try {
+        parser.parse ("1)", eqman2);
+      } catch (GLib.Error error) {
+        message ("Correctly catched grouping error: %s", error.message);
+      }
+      var eqman3 = new GMathEquationManager ();
+      try {
+        parser.parse ("(1))", eqman3);
+      } catch (GLib.Error error) {
+        message ("Correctly catched grouping error: %s", error.message);
+      }
+    });
     return Test.run ();
   }
 }


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