From 16856cd848345a61f14b1e7f14de536f4510c8f1 Mon Sep 17 00:00:00 2001
From: Kraemerd <Dominic_Daniel.Kraemer@Student.Reutlingen-University.de>
Date: Mon, 9 Jan 2023 18:27:35 +0100
Subject: [PATCH] implemented final version of Parser.java and a fitting test
 class ParserTest.java

---
 expressions.txt          |  4 ++
 src/main/Parser.java     | 85 +++++++++++++++++++++++++++++-----------
 src/test/ParserTest.java | 39 +++++++++++++++++-
 3 files changed, 103 insertions(+), 25 deletions(-)

diff --git a/expressions.txt b/expressions.txt
index a5fb886..bcba895 100644
--- a/expressions.txt
+++ b/expressions.txt
@@ -1,2 +1,6 @@
+2*(4^2)
+(4+2)*4
+(4+2*(4-2))
+((3+2)*(12+2))
 1+(x^2.22)-(3*x)
 2*x+0.5*(x^2+4)-1
\ No newline at end of file
diff --git a/src/main/Parser.java b/src/main/Parser.java
index ac39ec2..e29e094 100644
--- a/src/main/Parser.java
+++ b/src/main/Parser.java
@@ -116,11 +116,12 @@ public class Parser {
         return ast;
     }
 
-
-
-
-
-
+    //called by parseExpression
+    //checks if there are multiple tokens left, which indicates that they build up a BinaryOperation
+    //It splits up the tokens into a left and right expression and with a recursive call of parseExpression
+    //each part will be determined, until all BinaryOperations consist of finite left and right expressions
+    //Then a new BinaryOperation will be returned, which saves the left- & right expression and the operator
+    //between them
     private BinaryOperation parseBinaryOperation(List<Lexer.Token> ts) throws ParserException {
         if(ts.isEmpty()) {
             throw new ParserException("SyntaxError: empty token list");
@@ -128,76 +129,111 @@ public class Parser {
         if(ts.size()==1) {
             return null;
         }
-        int index = 0;
+        //Index will be determined in the next if/else statement
+        //It will indicate the position of the operator that separates the left- and right expression
+        int index;
         int tokensInBracket = parseBrackets(ts);
         boolean capsuled;
+        //If the result of parseBrackets(ts)+1 is the same as ts.size the current BinaryOperation is capsuled
         if(tokensInBracket+1==ts.size()) {
+            //removes the tokens with the capsuling brackets
             ts.remove(tokensInBracket);
             ts.remove(0);
             capsuled = true;
-            index = 1;
+            //Checks again for brackets
+            tokensInBracket = parseBrackets(ts);
+            //If no bracket is following, the operator needs to be at position 1 of ts
+            if(tokensInBracket==-1) {
+                index = 1;
+            }
+            else {
+                //If brackets are following, the operator is at position tokensInBracket+1
+                index = tokensInBracket+1;
+            }
         }
+        //If tokensInBracket is -1 this expression is not capsuled and the operator needs to be at position 1 of ts
         else if (tokensInBracket==-1) {
             capsuled = false;
-            parseOperator(ts.get(1).getData());
             index = 1;
         }
+        //If both other cases were not fulfilled an inner expression is capsuled
+        //Then this BinaryOperation is not capsuled, no brackets need to be removed and the operator is at
+        //position tokensInBracket+1
         else {
             capsuled = false;
             index = tokensInBracket+1;
         }
+        //Checks if the operator of this BinaryOperation is permitted
+        parseOperator(ts.get(index).getData());
         List<Lexer.Token> leftList = new LinkedList<>();
+        //Splits the token list ts into a left and right expression
         for(int iterator = 0; iterator<index; iterator++) {
             leftList.add(ts.remove(0));
         }
         Expression leftExpression = parseExpression(leftList);
+        //Operator is now at position 0
         String operator = ts.remove(0).getData();
         Expression rightExpression = parseExpression(ts);
         return new BinaryOperation(leftExpression, operator, rightExpression, capsuled);
     }
 
-    //checks if a String only contains an allowed operator with parseCharacter(...)
-    private boolean parseOperator(String operator) throws ParserException {
-        if(operator.length()>1) {
-            throw new ParserException("SyntaxError: invalid length for an operator: " + operator);
-        }
-        if(!parseCharacter(operator, "+-*/^")) {
-            throw new ParserException("SyntaxError: invalid operator character: " + operator);
-        }
-        return true;
-    }
 
+    //called by parseBinaryOperator
+    //checks if the first token is a bracket
+    //If yes: checks if the bracket gets closed correctly and returns the index of the closing bracket
+    //If no: returns -1
     private int parseBrackets(List<Lexer.Token> ts) throws ParserException{
+        if(ts.isEmpty()) {
+            throw new ParserException("SyntaxError: empty token list");
+        }
         if(parseCharacter(ts.get(0).getData(), "(")) {
             int index;
             int lBrackets = 0;
             int rBrackets = 0;
             boolean found = false;
+            //Iterates through every token, until the closing bracket got found
             for(index=1; index<ts.size(); index++) {
                 if(parseCharacter(ts.get(index).getData(), "(")) {
                     lBrackets += 1;
                 }
                 else if(parseCharacter(ts.get(index).getData(), ")")) {
                     rBrackets += 1;
+                    //If more ')' got found then '(' the bracket got closed correctly and the loop can be interrupted
                     if(rBrackets>lBrackets) {
                         found = true;
                         break;
                     }
                 }
             }
+            //If no matching closing bracket was found a ParserException will be thrown
             if(!found) {
                 throw new ParserException("SyntaxError: brackets never got closed");
             }
+            //If the closing bracket was placed immediately after the opening bracket a ParserException will be thrown
             if(index==1) {
                 throw new ParserException("SyntaxError: no value inside brackets");
             }
+            //Returns the index of the closing bracket
             return index;
         }
         return -1;
     }
 
 
-
+    //called by parseBinaryOperation
+    //checks if a String only contains an allowed operator with parseCharacter
+    //if not: throws a ParserException
+    private void parseOperator(String operator) throws ParserException {
+        if(operator.isEmpty()) {
+            throw new ParserException("RuntimeError: empty String instead of an operator (+, -, *, /, ^");
+        }
+        if(operator.length()>1) {
+            throw new ParserException("SyntaxError: invalid length for an operator: " + operator);
+        }
+        if(!parseCharacter(operator, "+-*/^")) {
+            throw new ParserException("SyntaxError: invalid operator character: " + operator);
+        }
+    }
 
     //called by parseExpression
     //Checks if a token is a variable:
@@ -280,7 +316,10 @@ public class Parser {
         //If afterDot is false, the first digit is not allowed to be 0
         if(!afterDot) {
             parseDigitWithoutZero(data.substring(0,1));
-            parseDigit(data.substring(1));
+
+            if (data.length()>1) {
+                parseDigit(data.substring(1));
+            }
         }
         //Checks if all digits of the number are permitted
         else {
@@ -293,10 +332,10 @@ public class Parser {
     //checks if a String only contains numbers(including zero) with parseCharacter
     //if not, a ParserException is thrown
     private void parseDigit(String data) throws ParserException {
-        if(data.isEmpty()) {
-            throw new ParserException("RuntimeError: empty String instead of a digit(0-9)");
-        }
         for(int index=0; index<data.length(); index++) {
+            if(data.isEmpty()) {
+                throw new ParserException("RuntimeError: empty String instead of a digit(0-9)");
+            }
             String character = Character.toString(data.charAt(index));
             if(!parseCharacter(character, "0123456789")) {
                 throw new ParserException("SyntaxError: unexpected character: " + character);
diff --git a/src/test/ParserTest.java b/src/test/ParserTest.java
index 090bb60..2d70d2e 100644
--- a/src/test/ParserTest.java
+++ b/src/test/ParserTest.java
@@ -7,8 +7,6 @@ import org.junit.Test;
 import java.util.LinkedList;
 import java.util.List;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
 public class ParserTest {
 
     @Test
@@ -62,4 +60,41 @@ public class ParserTest {
             e.printStackTrace();
         }
     }
+
+    @Test
+    void testParseBinaryOperation() {
+        List<Lexer.Token> tokens = new LinkedList<>();
+        tokens.add(new Lexer.Token(Lexer.TokenType.SPECIAL,"("));
+        tokens.add(new Lexer.Token(Lexer.TokenType.NUMBER,"5"));
+        tokens.add(new Lexer.Token(Lexer.TokenType.SPECIAL,"*"));
+        tokens.add(new Lexer.Token(Lexer.TokenType.NUMBER,"2"));
+        tokens.add(new Lexer.Token(Lexer.TokenType.SPECIAL,")"));
+        tokens.add(new Lexer.Token(Lexer.TokenType.NUMBER,"*"));
+        tokens.add(new Lexer.Token(Lexer.TokenType.NUMBER,"3"));
+        var parser = new Parser();
+        try {
+            var ast = parser.parse(tokens);
+        }
+        catch(Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    void testParseWrongBinaryOperation() {
+        List<Lexer.Token> tokens = new LinkedList<>();
+        tokens.add(new Lexer.Token(Lexer.TokenType.SPECIAL,"("));
+        tokens.add(new Lexer.Token(Lexer.TokenType.NUMBER,"5"));
+        tokens.add(new Lexer.Token(Lexer.TokenType.SPECIAL,"*"));
+        tokens.add(new Lexer.Token(Lexer.TokenType.NUMBER,"2"));
+        tokens.add(new Lexer.Token(Lexer.TokenType.SPECIAL,")"));
+        tokens.add(new Lexer.Token(Lexer.TokenType.NUMBER,"3"));
+        var parser = new Parser();
+        try {
+            var ast = parser.parse(tokens);
+        }
+        catch(Exception e) {
+            e.printStackTrace();
+        }
+    }
 }
-- 
GitLab