From 98ddff97ec1d092b18ef2d176e83bd92f9671e03 Mon Sep 17 00:00:00 2001
From: Maƫl Gassmann <mael.gassmann@students.bfh.ch>
Date: Fri, 11 Jun 2021 17:28:55 +0200
Subject: [+] Added StatementParser, [~] Restructured the Parsers

---
 .../main/java/ch/bfh/parser/ExpressionParser.java  | 180 ++-------------------
 .../src/main/java/ch/bfh/parser/FactorParser.java  |  94 +++++++++++
 .../src/main/java/ch/bfh/parser/Parser.java        |   9 +-
 .../main/java/ch/bfh/parser/StatementParser.java   |  70 ++++++++
 .../src/main/java/ch/bfh/parser/TermParser.java    |  93 +++++++++++
 5 files changed, 274 insertions(+), 172 deletions(-)
 create mode 100644 calculator-java/src/main/java/ch/bfh/parser/FactorParser.java
 create mode 100644 calculator-java/src/main/java/ch/bfh/parser/StatementParser.java
 create mode 100644 calculator-java/src/main/java/ch/bfh/parser/TermParser.java

(limited to 'calculator-java/src/main/java/ch/bfh/parser')

diff --git a/calculator-java/src/main/java/ch/bfh/parser/ExpressionParser.java b/calculator-java/src/main/java/ch/bfh/parser/ExpressionParser.java
index f96353e..3ec0194 100644
--- a/calculator-java/src/main/java/ch/bfh/parser/ExpressionParser.java
+++ b/calculator-java/src/main/java/ch/bfh/parser/ExpressionParser.java
@@ -10,21 +10,27 @@ public class ExpressionParser extends Parser {
 
     protected ArrayList<Parser> parsers = new ArrayList<>();
     protected ArrayList<Token> ops = new ArrayList<>();
+    private boolean isParenthesised = false;
 
     public ExpressionParser(CalculatorLexer cl) {
         this.cl = cl;
         parse();
     }
 
-    private ExpressionParser(CalculatorLexer cl, Token lastToken) {
+    protected ExpressionParser(CalculatorLexer cl, Token lastToken, boolean isParenthesised) {
         this.cl = cl;
         this.lastToken = lastToken;
-        parse();
+        this.isParenthesised = isParenthesised; //If an expression starts with the lastToken set as '(' it could mean that the Expression first term will be a parenthesised expression
+        parse();                                //But it could also mean that this Expression is the parenthesised one, that is why this variable is required.
     }
 
     @Override
     protected void parse() {
-        Token token = cl.nextToken();
+        Token token;
+        if(lastToken != null && !isParenthesised)
+            token = lastToken;
+        else
+            token = cl.nextToken();
         Parser l = null;
         Parser r = null; // left and right expressions
         Token op = null; // operation of the current l and right expressions
@@ -58,6 +64,7 @@ public class ExpressionParser extends Parser {
                         this.ops.add(op);
                     } // Then the '-' is the start of a new expression and not an operation
                 case Token.PAL:
+                case Token.ID:
                 case Token.NUM:
                     if (l == null) {
                         l = new TermParser(cl, token);
@@ -104,171 +111,4 @@ public class ExpressionParser extends Parser {
         }
         return result;
     }
-
-    private class TermParser extends ExpressionParser {
-
-        public TermParser(CalculatorLexer cl, Token lastToken) {
-            super(cl, lastToken);
-        }
-
-        @Override
-        protected void parse() {
-            Token token = lastToken;
-            lastToken = null;
-            Parser l = null;
-            Parser r = null; // left and right expressions
-            Token op = null; // operation of the current l and right expressions
-
-            loop:
-            while (token != null && token.type != Token.EOL) {
-                switch (token.type) {
-                    case Token.ADD:
-                        if (op != null) {
-                            lastToken = token;
-                            break loop; // going here on case 'n/+...' or 'n*+...' since '+' would be invalid
-                        }
-                    case Token.SUB:
-                    case Token.NUM:
-                    case Token.PAL:
-                        if (l == null) {
-                            l = new FactorParser(cl, token);
-                            this.parsers.add(l);
-                            token = l.lastToken;
-                            continue loop;
-                        } else if (op != null) {
-                            r = new FactorParser(cl, token);
-                            this.parsers.add(r);
-                            token = r.lastToken;
-                            lastToken = token; // in case we finished
-                            continue loop;
-                        } else {
-                            lastToken = token;
-                            break loop; // going here on case 'n+...' or 'n-...' since '+' and '-' are part of the parent expression
-                        }
-                    case Token.MUL:
-                    case Token.DIV:
-                        if (l != null && op == null) { // if the '*' or '/' signs are between two Factors (op)
-                            op = token;
-                            this.ops.add(op);
-                        } else if (l != null && r != null) { // if we already found a 'l op r' we roll
-                            l = r;
-                            r = null;
-                            op = token;
-                            this.ops.add(op);
-                        } else
-                            throw new ParserException("Two '" + token.str + "' in a row were found.");
-                        break;
-                    case Token.PAR:  // Going as high as possible --> possibly the end of an expression created by a Factor :)
-                        lastToken = token;
-                        break loop;
-                    default:
-                        //TODO - Replace default by each individual case
-                        throw new ParserException("Malformed Expression.");
-                }
-                token = cl.nextToken();
-            }
-            if (op != null && r == null)
-                throw new ParserException("Missing expression after last operation '"+op.str+"'");
-        }
-
-        @Override
-        public double getValue(){
-            double result = 0.0;
-            if (!this.parsers.isEmpty()) {
-                result = this.parsers.get(0).getValue();
-
-                for (int i = 1; i < this.parsers.size(); i++) {
-                    switch (this.ops.get(i-1).type){
-                        case Token.MUL:
-                            result *= this.parsers.get(i).getValue();
-                            break;
-                        case Token.DIV:
-                            result /= this.parsers.get(i).getValue();
-                            break;
-                    }
-                }
-            }
-            return result;
-        }
-
-        private class FactorParser extends Parser {
-
-            ExpressionParser expression; // only used when parenthesis are found
-            boolean isNegative = false;
-            boolean somethingWasParsed = false;
-
-            public FactorParser(CalculatorLexer cl, Token lastToken) {
-                this.cl = cl;
-                this.lastToken = lastToken;
-                parse();
-            }
-
-            @Override
-            protected void parse() {
-                Token token = lastToken;
-                lastToken = null;
-                loop:
-                while (token != null && token.type != Token.EOL) {
-                    switch (token.type) {
-                        case Token.MUL:
-                        case Token.DIV:
-                        case Token.ADD:
-                            if (somethingWasParsed) { // finished parsing the Factor
-                                lastToken = token; // sending the token to the parent
-                                break loop;
-                            } else
-                                throw new ParserException("Empty expression before '" + token.str + "'.");
-                        case Token.SUB:
-                            if (!somethingWasParsed)
-                                if (!isNegative)
-                                    isNegative = true;
-                                else
-                                    throw new ParserException("A factor cannot contain two '-'.");
-                            else { // Meaning we could have parsed '-4' or '4' and '-' is the next digit which is op for the overall expression
-                                lastToken = token;
-                                break loop;
-                            }
-                            break;
-                        case Token.NUM:
-                            if (!somethingWasParsed) {
-                                value = token.value;
-                                somethingWasParsed = true;
-                            } else
-                                throw new ParserException("Missing operator between the two Factors.");
-                            break;
-                        case Token.PAL:
-                            if (!somethingWasParsed) {
-                                expression = new ExpressionParser(cl, token);
-                                if (expression.lastToken.type != Token.PAR)
-                                    throw new ParserException("No matching closing parenthesis were found.");
-                                somethingWasParsed = true;
-                            } else
-                                throw new ParserException("Missing operator between the two Factors.");
-                            break;
-                        case Token.PAR:  // Going as high as possible --> possibly the end of an expression created by a Factor :)
-                            if (somethingWasParsed)
-                                lastToken = token;
-                            else throw new ParserException("Missing expression before closing the parenthesis.");
-                            break loop;
-                        default:
-                            //TODO - Replace default by each individual case
-                            throw new ParserException("Malformed Expression.");
-                    }
-                    token = cl.nextToken();
-                }
-            }
-
-            @Override
-            public double getValue(){
-                if (expression != null)
-                    if (isNegative)
-                        return expression.getValue()*(-1);
-                    else
-                        return expression.getValue();
-                if (isNegative)
-                    return value*(-1);
-                return value;
-            }
-        }
-    }
 }
diff --git a/calculator-java/src/main/java/ch/bfh/parser/FactorParser.java b/calculator-java/src/main/java/ch/bfh/parser/FactorParser.java
new file mode 100644
index 0000000..8bb5ef7
--- /dev/null
+++ b/calculator-java/src/main/java/ch/bfh/parser/FactorParser.java
@@ -0,0 +1,94 @@
+package ch.bfh.parser;
+
+import ch.bfh.CalculatorLexer;
+import ch.bfh.Token;
+import ch.bfh.exceptions.ParserException;
+
+class FactorParser extends Parser {
+
+    ExpressionParser expression; // only used when parenthesis are found
+    boolean isNegative = false;
+    boolean somethingWasParsed = false;
+
+    public FactorParser(CalculatorLexer cl, Token lastToken) {
+        this.cl = cl;
+        this.lastToken = lastToken;
+        parse();
+    }
+
+    @Override
+    protected void parse() {
+        Token token = lastToken;
+        lastToken = null;
+        loop:
+        while (token != null && token.type != Token.EOL) {
+            switch (token.type) {
+                case Token.MUL:
+                case Token.DIV:
+                case Token.ADD:
+                    if (somethingWasParsed) { // finished parsing the Factor
+                        lastToken = token; // sending the token to the parent
+                        break loop;
+                    } else
+                        throw new ParserException("Empty expression before '" + token.str + "'.");
+                case Token.SUB:
+                    if (!somethingWasParsed)
+                        if (!isNegative)
+                            isNegative = true;
+                        else
+                            throw new ParserException("A factor cannot contain two '-'.");
+                    else { // Meaning we could have parsed '-4' or '4' and '-' is the next digit which is op for the overall expression
+                        lastToken = token;
+                        break loop;
+                    }
+                    break;
+                case Token.NUM:
+                    if (!somethingWasParsed) {
+                        value = token.value;
+                        somethingWasParsed = true;
+                    } else
+                        throw new ParserException("Missing operator between the two Factors.");
+                    break;
+                case Token.ID:
+                    if (!somethingWasParsed) {
+                        expression = variables.get(token.str);
+                        if (expression == null)
+                            throw new ParserException("'"+token.str+"' is not yet defined.");
+                        somethingWasParsed = true;
+                    } else
+                        throw new ParserException("Missing operator between the two Factors.");
+                    break;
+                case Token.PAL:
+                    if (!somethingWasParsed) {
+                        expression = new ExpressionParser(cl, token, true);
+                        if (expression.lastToken.type != Token.PAR)
+                            throw new ParserException("No matching closing parenthesis were found.");
+                        somethingWasParsed = true;
+                    } else
+                        throw new ParserException("Missing operator between the two Factors.");
+                    break;
+                case Token.PAR:  // Going as high as possible --> possibly the end of an expression created by a Factor :)
+                    if (somethingWasParsed)
+                        lastToken = token;
+                    else throw new ParserException("Missing expression before closing the parenthesis.");
+                    break loop;
+                default:
+                    //TODO - Replace default by each individual case
+                    throw new ParserException("Malformed Expression.");
+            }
+            token = cl.nextToken();
+        }
+    }
+
+    @Override
+    public double getValue() {
+        if (expression != null)
+            if (isNegative)
+                return expression.getValue() * (-1);
+            else
+                return expression.getValue();
+        if (isNegative)
+            return value * (-1);
+        return value;
+    }
+}
diff --git a/calculator-java/src/main/java/ch/bfh/parser/Parser.java b/calculator-java/src/main/java/ch/bfh/parser/Parser.java
index 9e048a6..333b942 100644
--- a/calculator-java/src/main/java/ch/bfh/parser/Parser.java
+++ b/calculator-java/src/main/java/ch/bfh/parser/Parser.java
@@ -3,12 +3,17 @@ package ch.bfh.parser;
 import ch.bfh.CalculatorLexer;
 import ch.bfh.Token;
 
+import java.util.HashMap;
+import java.util.Map;
+
 abstract class Parser{
+
+    protected static Map<String, ExpressionParser> variables = new HashMap<>(); //'Persisted' Expressions when 'let' token was specified
+
     protected CalculatorLexer cl;
     protected Token lastToken;
     protected double value;
 
     protected abstract void parse();
     public abstract double getValue();
-}
-
+}
\ No newline at end of file
diff --git a/calculator-java/src/main/java/ch/bfh/parser/StatementParser.java b/calculator-java/src/main/java/ch/bfh/parser/StatementParser.java
new file mode 100644
index 0000000..c85398b
--- /dev/null
+++ b/calculator-java/src/main/java/ch/bfh/parser/StatementParser.java
@@ -0,0 +1,70 @@
+package ch.bfh.parser;
+
+import ch.bfh.CalculatorLexer;
+import ch.bfh.Token;
+import ch.bfh.exceptions.ParserException;
+
+public class StatementParser extends Parser{
+
+    String input;
+    ExpressionParser parsedExpression;
+
+    public void parseStatement(String input){
+        this.input = input;
+        cl = new CalculatorLexer();
+        cl.initLexer(input);
+        parsedExpression = null;
+        parse();
+    }
+
+    @Override
+    protected void parse() {
+        Token token = cl.nextToken();
+        String variableName = null;
+        lastToken = null;
+        loop:
+        while (token != null && token.type != Token.EOL) {
+            switch (token.type) {
+                case Token.LET:
+                    if (lastToken != null)
+                        throw new ParserException("The keyword 'let' cannot be placed anywhere else than at the beginning of an expression.");
+                    break;
+                case Token.EQU:
+                    if (lastToken == null || lastToken.type != Token.LET || variableName == null)
+                        throw new ParserException("The inputted token '=' can only be placed in a variable declaration context (let variable = Expression).");
+                    break;
+                case Token.END:
+                    if (lastToken != null)
+                        throw new ParserException("The keyword 'exit' cannot be placed anywhere else than at the beginning of an expression.");
+                    else
+                        System.exit(0);
+                case Token.ID:
+                    if (lastToken != null && lastToken.type == Token.LET && variableName == null) { // we are defining a new variable
+                        variableName = token.str;
+                        token = cl.nextToken();
+                        continue loop; // lastToken value will thus still be 'let' so that we could then parse the '=' token that is supposed to come next correctly.
+                    }else if (variableName != null && lastToken != null && lastToken.type != Token.EQU)
+                        throw new ParserException("Two consecutive variables ('"+variableName+"' and '"+token.str+"') were found in the declaration context.");
+                    // the expression started with a variable -> no definition -> meaning it is an expression
+                default:
+                    parsedExpression = new ExpressionParser(cl, token, false);
+                    if (lastToken != null && lastToken.type == Token.EQU) // this still need to be put in the variables list
+                        variables.put(variableName, parsedExpression);
+                    break;
+            }
+            lastToken = token;
+            token = cl.nextToken();
+        }
+        if (lastToken == null)
+            return;
+        if (lastToken.type == Token.LET || lastToken.type == Token.EQU)
+            throw new ParserException("Incomplete variable declaration");
+    }
+
+    @Override
+    public double getValue() {
+        if (parsedExpression == null)
+            return 0.0;
+        return parsedExpression.getValue();
+    }
+}
\ No newline at end of file
diff --git a/calculator-java/src/main/java/ch/bfh/parser/TermParser.java b/calculator-java/src/main/java/ch/bfh/parser/TermParser.java
new file mode 100644
index 0000000..9a97659
--- /dev/null
+++ b/calculator-java/src/main/java/ch/bfh/parser/TermParser.java
@@ -0,0 +1,93 @@
+package ch.bfh.parser;
+
+import ch.bfh.CalculatorLexer;
+import ch.bfh.Token;
+import ch.bfh.exceptions.ParserException;
+
+class TermParser extends ExpressionParser {
+
+    public TermParser(CalculatorLexer cl, Token lastToken) {
+        super(cl, lastToken, false);
+    }
+
+    @Override
+    protected void parse() {
+        Token token = lastToken;
+        lastToken = null;
+        Parser l = null;
+        Parser r = null; // left and right expressions
+        Token op = null; // operation of the current l and right expressions
+
+        loop:
+        while (token != null && token.type != Token.EOL) {
+            switch (token.type) {
+                case Token.ADD:
+                    if (op != null) {
+                        lastToken = token;
+                        break loop; // going here on case 'n/+...' or 'n*+...' since '+' would be invalid
+                    }
+                case Token.SUB:
+                case Token.NUM:
+                case Token.ID:
+                case Token.PAL:
+                    if (l == null) {
+                        l = new FactorParser(cl, token);
+                        this.parsers.add(l);
+                        token = l.lastToken;
+                        continue loop;
+                    } else if (op != null) {
+                        r = new FactorParser(cl, token);
+                        this.parsers.add(r);
+                        token = r.lastToken;
+                        lastToken = token; // in case we finished
+                        continue loop;
+                    } else {
+                        lastToken = token;
+                        break loop; // going here on case 'n+...' or 'n-...' since '+' and '-' are part of the parent expression
+                    }
+                case Token.MUL:
+                case Token.DIV:
+                    if (l != null && op == null) { // if the '*' or '/' signs are between two Factors (op)
+                        op = token;
+                        this.ops.add(op);
+                    } else if (l != null && r != null) { // if we already found a 'l op r' we roll
+                        l = r;
+                        r = null;
+                        op = token;
+                        this.ops.add(op);
+                    } else
+                        throw new ParserException("Two '" + token.str + "' in a row were found.");
+                    break;
+                case Token.PAR:  // Going as high as possible --> possibly the end of an expression created by a Factor :)
+                    lastToken = token;
+                    break loop;
+                default:
+                    //TODO - Replace default by each individual case
+                    throw new ParserException("Malformed Expression.");
+            }
+            token = cl.nextToken();
+        }
+        if (op != null && r == null)
+            throw new ParserException("Missing expression after last operation '" + op.str + "'");
+    }
+
+    @Override
+    public double getValue() {
+        double result = 0.0;
+        if (!this.parsers.isEmpty()) {
+            result = this.parsers.get(0).getValue();
+
+            for (int i = 1; i < this.parsers.size(); i++) {
+                switch (this.ops.get(i - 1).type) {
+                    case Token.MUL:
+                        result *= this.parsers.get(i).getValue();
+                        break;
+                    case Token.DIV:
+                        result /= this.parsers.get(i).getValue();
+                        break;
+                }
+            }
+        }
+        return result;
+    }
+}
\ No newline at end of file
-- 
cgit v1.2.3