From fed70f3ac817ed4943a230cb901fcd65dc6fdd0c Mon Sep 17 00:00:00 2001
From: Maƫl Gassmann <mael.gassmann@students.bfh.ch>
Date: Fri, 11 Jun 2021 20:55:12 +0200
Subject: [~] First totaly functional Calculator, reorganised the files,
 verified and added error messages

---
 .../src/main/java/ch/bfh/CalculatorLexer.java      | 106 ---------------------
 calculator-java/src/main/java/ch/bfh/Main.java     |   7 +-
 calculator-java/src/main/java/ch/bfh/Token.java    |  23 -----
 .../java/ch/bfh/exceptions/LexerException.java     |   5 -
 .../java/ch/bfh/exceptions/ParserException.java    |   5 -
 .../main/java/ch/bfh/lexer/CalculatorLexer.java    | 105 ++++++++++++++++++++
 .../src/main/java/ch/bfh/lexer/LexerException.java |   5 +
 .../src/main/java/ch/bfh/lexer/Token.java          |  23 +++++
 .../main/java/ch/bfh/parser/ExpressionParser.java  |  36 ++++---
 .../src/main/java/ch/bfh/parser/FactorParser.java  |  15 +--
 .../src/main/java/ch/bfh/parser/Parser.java        |   7 +-
 .../main/java/ch/bfh/parser/ParserException.java   |   8 ++
 .../main/java/ch/bfh/parser/StatementParser.java   |  13 ++-
 .../src/main/java/ch/bfh/parser/TermParser.java    |  13 +--
 14 files changed, 186 insertions(+), 185 deletions(-)
 delete mode 100644 calculator-java/src/main/java/ch/bfh/CalculatorLexer.java
 delete mode 100644 calculator-java/src/main/java/ch/bfh/Token.java
 delete mode 100644 calculator-java/src/main/java/ch/bfh/exceptions/LexerException.java
 delete mode 100644 calculator-java/src/main/java/ch/bfh/exceptions/ParserException.java
 create mode 100644 calculator-java/src/main/java/ch/bfh/lexer/CalculatorLexer.java
 create mode 100644 calculator-java/src/main/java/ch/bfh/lexer/LexerException.java
 create mode 100644 calculator-java/src/main/java/ch/bfh/lexer/Token.java
 create mode 100644 calculator-java/src/main/java/ch/bfh/parser/ParserException.java

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

diff --git a/calculator-java/src/main/java/ch/bfh/CalculatorLexer.java b/calculator-java/src/main/java/ch/bfh/CalculatorLexer.java
deleted file mode 100644
index 48d009e..0000000
--- a/calculator-java/src/main/java/ch/bfh/CalculatorLexer.java
+++ /dev/null
@@ -1,106 +0,0 @@
-package ch.bfh;
-import ch.bfh.exceptions.LexerException;
-
-// Lexer for classical arithmetic expressions with identifiers and assignments.
-// Scans a source string char by char.
-
-public class CalculatorLexer {
-
-  private String src;  // source string for lexical analysis
-  private int idx;     // current index in source
-  private int len;     // length of source
-
-  public CalculatorLexer() { }
-
-  public void initLexer(String source) {
-    this.src = source;
-    idx = 0;
-    len = src.length();
-  }
-
-  // Consumes letters only and builds an identifier
-  private String identifier() {
-    StringBuffer s = new StringBuffer();
-  
-    do {
-      s.append(src.charAt(idx));
-      idx++;
-    } while (idx < len && Character.isLetter(src.charAt(idx)));
-    return s.toString();
-  }
-
-  // Consumes digits and convert integer part and decimal part
-  // Convert characters using the formula
-  // "3456.253" = [(((3+0)*10+4)*10+5)*10+6]+[0.1*2+0.01*5+0.001*3]
-  private double number() throws LexerException {
-    double v = 0;        // accumulates the result
-    double factor = 0.1; // factor for decimal part
-
-    do { // integer part
-      v = v * 10 + Character.digit(src.charAt(idx),30);
-      idx++;
-    } while (idx < len && Character.isDigit(src.charAt(idx)));
-    if (idx < len && src.charAt(idx) == '.') { // decimal point
-      idx++; 
-      if (idx < len && Character.isDigit(src.charAt(idx))) { // decimal part
-        while (idx < len && Character.isDigit(src.charAt(idx))) {
-          v = v + (factor * Character.digit(src.charAt(idx),30));
-          factor = factor * 0.1;
-          idx++;
-        }
-      }
-      else throw new LexerException("Illegal number: decimal part missing");
-    }
-    return v;
-  }
-
-  // Skips blanks, tabs, newlines
-  private void skip() {
-    char c;
-    while (idx < len) {
-      c = src.charAt(idx);
-      if (c==' ' || c=='\t' || c=='\n') idx++;
-      else break;
-    }
-  }
-
-  // returns next token 
-  public Token nextToken() throws LexerException {
-    Token tok = new Token();
-
-    skip();
-    if (idx>=len) {
-	tok.str="EOL";
-	tok.type=Token.EOL;
-    }
-    else
-      // is it a positive number?
-      if (Character.isDigit(src.charAt(idx))) {
-        tok.value = number();
-        tok.type  = Token.NUM;
-        tok.str   = Double.toString(tok.value);
-      }
-      else
-        if (Character.isLetter(src.charAt(idx))) {
-          tok.value = 0;
-          tok.type  = Token.ID;
-          tok.str   = identifier();
-          if (tok.str.compareTo("let")==0)  tok.type = Token.LET;
-          if (tok.str.compareTo("exit")==0) tok.type = Token.END;
-        }
-        else {
-          switch (src.charAt(idx)) {
-            case '+': tok.type = Token.ADD; tok.str = "+"; break;
-            case '-': tok.type = Token.SUB; tok.str = "-"; break;
-            case '*': tok.type = Token.MUL; tok.str = "*"; break;
-            case '/': tok.type = Token.DIV; tok.str = "/"; break;
-            case '(': tok.type = Token.PAL; tok.str = "("; break;
-            case ')': tok.type = Token.PAR; tok.str = ")"; break;
-            case '=': tok.type = Token.EQU; tok.str = "="; break;
-            default : throw new LexerException("Illegal Token: '" + src.charAt(idx) + "'");
-          }
-          idx++;
-        }
-    return tok;
-  }
-}
\ No newline at end of file
diff --git a/calculator-java/src/main/java/ch/bfh/Main.java b/calculator-java/src/main/java/ch/bfh/Main.java
index 5dc8a91..ea01185 100644
--- a/calculator-java/src/main/java/ch/bfh/Main.java
+++ b/calculator-java/src/main/java/ch/bfh/Main.java
@@ -1,7 +1,8 @@
 package ch.bfh;
 
+import ch.bfh.lexer.LexerException;
+import ch.bfh.parser.ParserException;
 import ch.bfh.parser.StatementParser;
-
 import java.util.Scanner;
 
 public class Main {
@@ -16,8 +17,8 @@ public class Main {
 
             try {
                 sp.parseStatement(expression);
-                System.out.println(sp.getValue());
-            } catch (Exception e) {
+                System.out.println("Result: " + sp.getValue());
+            } catch (LexerException | ParserException e) {
                 System.out.println(e.getMessage());
             }
         }
diff --git a/calculator-java/src/main/java/ch/bfh/Token.java b/calculator-java/src/main/java/ch/bfh/Token.java
deleted file mode 100644
index 93d4d02..0000000
--- a/calculator-java/src/main/java/ch/bfh/Token.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package ch.bfh;
-
-// Various Tokens for arithmetic expressions based on integers
-// with identifiers and assignments
-
-public class Token {
-  public int type;                   // token type
-  public double value;               // numerical value for NUM
-  public String str;                 // token string
-
-  public static final int EOL=0;     // End Of Line
-  public static final int PAL=1;     // Left Parenthesis
-  public static final int PAR=2;     // Right Parenthesis
-  public static final int ADD=3;     // operators
-  public static final int SUB=4;
-  public static final int MUL=5;
-  public static final int DIV=6;
-  public static final int NUM=7;     // number
-  public static final int EQU=8;     // equal 
-  public static final int LET=9;     // let
-  public static final int ID=10;     // identifier
-  public static final int END=11;    // exit
-}
diff --git a/calculator-java/src/main/java/ch/bfh/exceptions/LexerException.java b/calculator-java/src/main/java/ch/bfh/exceptions/LexerException.java
deleted file mode 100644
index 082beee..0000000
--- a/calculator-java/src/main/java/ch/bfh/exceptions/LexerException.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package ch.bfh.exceptions;
-
-public class LexerException extends RuntimeException {
-    public LexerException(String s) { super(s); }
-}
\ No newline at end of file
diff --git a/calculator-java/src/main/java/ch/bfh/exceptions/ParserException.java b/calculator-java/src/main/java/ch/bfh/exceptions/ParserException.java
deleted file mode 100644
index ca6d037..0000000
--- a/calculator-java/src/main/java/ch/bfh/exceptions/ParserException.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package ch.bfh.exceptions;
-
-public class ParserException extends RuntimeException {
-    public ParserException(String s) { super(s); }
-}
\ No newline at end of file
diff --git a/calculator-java/src/main/java/ch/bfh/lexer/CalculatorLexer.java b/calculator-java/src/main/java/ch/bfh/lexer/CalculatorLexer.java
new file mode 100644
index 0000000..0ba9535
--- /dev/null
+++ b/calculator-java/src/main/java/ch/bfh/lexer/CalculatorLexer.java
@@ -0,0 +1,105 @@
+package ch.bfh.lexer;
+
+// Lexer for classical arithmetic expressions with identifiers and assignments.
+// Scans a source string char by char.
+
+public class CalculatorLexer {
+
+  private String src;  // source string for lexical analysis
+  private int idx;     // current index in source
+  private int len;     // length of source
+
+  public CalculatorLexer() { }
+
+  public void initLexer(String source) {
+    this.src = source;
+    idx = 0;
+    len = src.length();
+  }
+
+  // Consumes letters only and builds an identifier
+  private String identifier() {
+    StringBuffer s = new StringBuffer();
+  
+    do {
+      s.append(src.charAt(idx));
+      idx++;
+    } while (idx < len && Character.isLetter(src.charAt(idx)));
+    return s.toString();
+  }
+
+  // Consumes digits and convert integer part and decimal part
+  // Convert characters using the formula
+  // "3456.253" = [(((3+0)*10+4)*10+5)*10+6]+[0.1*2+0.01*5+0.001*3]
+  private double number() throws LexerException {
+    double v = 0;        // accumulates the result
+    double factor = 0.1; // factor for decimal part
+
+    do { // integer part
+      v = v * 10 + Character.digit(src.charAt(idx),30);
+      idx++;
+    } while (idx < len && Character.isDigit(src.charAt(idx)));
+    if (idx < len && src.charAt(idx) == '.') { // decimal point
+      idx++; 
+      if (idx < len && Character.isDigit(src.charAt(idx))) { // decimal part
+        while (idx < len && Character.isDigit(src.charAt(idx))) {
+          v = v + (factor * Character.digit(src.charAt(idx),30));
+          factor = factor * 0.1;
+          idx++;
+        }
+      }
+      else throw new LexerException("Illegal number: decimal part missing");
+    }
+    return v;
+  }
+
+  // Skips blanks, tabs, newlines
+  private void skip() {
+    char c;
+    while (idx < len) {
+      c = src.charAt(idx);
+      if (c==' ' || c=='\t' || c=='\n') idx++;
+      else break;
+    }
+  }
+
+  // returns next token 
+  public Token nextToken() throws LexerException {
+    Token tok = new Token();
+
+    skip();
+    if (idx>=len) {
+	tok.str="EOL";
+	tok.type=Token.EOL;
+    }
+    else
+      // is it a positive number?
+      if (Character.isDigit(src.charAt(idx))) {
+        tok.value = number();
+        tok.type  = Token.NUM;
+        tok.str   = Double.toString(tok.value);
+      }
+      else
+        if (Character.isLetter(src.charAt(idx))) {
+          tok.value = 0;
+          tok.type  = Token.ID;
+          tok.str   = identifier();
+          if (tok.str.compareTo("let")==0)  tok.type = Token.LET;
+          if (tok.str.compareTo("exit")==0) tok.type = Token.END;
+        }
+        else {
+          switch (src.charAt(idx)) {
+            case '+': tok.type = Token.ADD; tok.str = "+"; break;
+            case '-': tok.type = Token.SUB; tok.str = "-"; break;
+            case '*': tok.type = Token.MUL; tok.str = "*"; break;
+            case '/': tok.type = Token.DIV; tok.str = "/"; break;
+            case '(': tok.type = Token.PAL; tok.str = "("; break;
+            case ')': tok.type = Token.PAR; tok.str = ")"; break;
+            case '=': tok.type = Token.EQU; tok.str = "="; break;
+            default : throw new LexerException("Illegal Token: '" + src.charAt(idx) + "'");
+          }
+          idx++;
+        }
+    return tok;
+  }
+}
\ No newline at end of file
diff --git a/calculator-java/src/main/java/ch/bfh/lexer/LexerException.java b/calculator-java/src/main/java/ch/bfh/lexer/LexerException.java
new file mode 100644
index 0000000..c5f8671
--- /dev/null
+++ b/calculator-java/src/main/java/ch/bfh/lexer/LexerException.java
@@ -0,0 +1,5 @@
+package ch.bfh.lexer;
+
+public class LexerException extends RuntimeException {
+    public LexerException(String s) { super(s); }
+}
\ No newline at end of file
diff --git a/calculator-java/src/main/java/ch/bfh/lexer/Token.java b/calculator-java/src/main/java/ch/bfh/lexer/Token.java
new file mode 100644
index 0000000..b021075
--- /dev/null
+++ b/calculator-java/src/main/java/ch/bfh/lexer/Token.java
@@ -0,0 +1,23 @@
+package ch.bfh.lexer;
+
+// Various Tokens for arithmetic expressions based on integers
+// with identifiers and assignments
+
+public class Token {
+  public int type;                   // token type
+  public double value;               // numerical value for NUM
+  public String str;                 // token string
+
+  public static final int EOL=0;//     // End Of Line
+  public static final int PAL=1;//     // Left Parenthesis
+  public static final int PAR=2;//     // Right Parenthesis
+  public static final int ADD=3;//     // operators
+  public static final int SUB=4;//
+  public static final int MUL=5;
+  public static final int DIV=6;
+  public static final int NUM=7;//     // number
+  public static final int EQU=8;     // equal 
+  public static final int LET=9;     // let
+  public static final int ID=10;//     // identifier
+  public static final int END=11;    // exit
+}
\ No newline at end of file
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 3ec0194..e68983b 100644
--- a/calculator-java/src/main/java/ch/bfh/parser/ExpressionParser.java
+++ b/calculator-java/src/main/java/ch/bfh/parser/ExpressionParser.java
@@ -1,22 +1,15 @@
 package ch.bfh.parser;
 
-import ch.bfh.CalculatorLexer;
-import ch.bfh.Token;
-import ch.bfh.exceptions.ParserException;
-
+import ch.bfh.lexer.CalculatorLexer;
+import ch.bfh.lexer.Token;
 import java.util.ArrayList;
 
-public class ExpressionParser extends Parser {
+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();
-    }
-
     protected ExpressionParser(CalculatorLexer cl, Token lastToken, boolean isParenthesised) {
         this.cl = cl;
         this.lastToken = lastToken;
@@ -31,8 +24,8 @@ public class ExpressionParser extends Parser {
             token = lastToken;
         else
             token = cl.nextToken();
-        Parser l = null;
-        Parser r = null; // left and right expressions
+        TermParser l = null;
+        TermParser r = null; // left and right Terms
         Token op = null; // operation of the current l and right expressions
 
         loop:
@@ -48,9 +41,9 @@ public class ExpressionParser extends Parser {
                         op = token;
                         this.ops.add(op);
                     }else if (l == null)
-                        throw new ParserException("Factor cannot start with '+'.");
+                        throw new ParserException("Redundant use of '+' is forbidden.");
                     else
-                        throw new ParserException("Invalid use of '+' was found in the Expression.");
+                        throw new ParserException(token, "repetition of operator -> a term was expected.");
                     break;
                 case Token.SUB:
                     if (l != null && op == null) { // if the '-' sign is between two term (op)
@@ -82,14 +75,19 @@ public class ExpressionParser extends Parser {
                         break loop;
                     } else
                         throw new ParserException("No matching opening parenthesis were found.");
-                default:
-                    //TODO - Replace default by each individual case
-                    throw new ParserException("Malformed Expression.");
+                case Token.MUL:
+                case Token.DIV:
+                    throw new ParserException(token,"a term was expected.");
+                case Token.EQU:
+                case Token.LET:
+                    throw new ParserException("The inputted token '"+token.str+"' can only be placed in a variable declaration context (let var = Expression).");
+                case Token.END:
+                    throw new ParserException("The keyword 'exit' can only be placed at the beginning of an expression.");
             }
             token = cl.nextToken();
         }
         if (op != null && r == null)
-            throw new ParserException("Missing expression after last operation '"+op.str+"'");
+            throw new ParserException("Missing term after the last operator '"+op.str+"'.");
     }
 
     @Override
@@ -111,4 +109,4 @@ public class ExpressionParser extends Parser {
         }
         return result;
     }
-}
+}
\ No newline at end of file
diff --git a/calculator-java/src/main/java/ch/bfh/parser/FactorParser.java b/calculator-java/src/main/java/ch/bfh/parser/FactorParser.java
index 8bb5ef7..fd54cf4 100644
--- a/calculator-java/src/main/java/ch/bfh/parser/FactorParser.java
+++ b/calculator-java/src/main/java/ch/bfh/parser/FactorParser.java
@@ -1,8 +1,7 @@
 package ch.bfh.parser;
 
-import ch.bfh.CalculatorLexer;
-import ch.bfh.Token;
-import ch.bfh.exceptions.ParserException;
+import ch.bfh.lexer.CalculatorLexer;
+import ch.bfh.lexer.Token;
 
 class FactorParser extends Parser {
 
@@ -72,9 +71,11 @@ class FactorParser extends Parser {
                         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.");
+                case Token.EQU:
+                case Token.LET:
+                    throw new ParserException("The inputted token '"+token.str+"' can only be placed in a variable declaration context (let var = Expression).");
+                case Token.END:
+                    throw new ParserException("The keyword 'exit' can only be placed at the beginning of an expression.");
             }
             token = cl.nextToken();
         }
@@ -91,4 +92,4 @@ class FactorParser extends Parser {
             return value * (-1);
         return value;
     }
-}
+}
\ No newline at end of file
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 333b942..6a03fe1 100644
--- a/calculator-java/src/main/java/ch/bfh/parser/Parser.java
+++ b/calculator-java/src/main/java/ch/bfh/parser/Parser.java
@@ -1,14 +1,13 @@
 package ch.bfh.parser;
 
-import ch.bfh.CalculatorLexer;
-import ch.bfh.Token;
-
+import ch.bfh.lexer.CalculatorLexer;
+import ch.bfh.lexer.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 static Map<String, ExpressionParser> variables = new HashMap<>(); //Persisted Expressions aka variables
 
     protected CalculatorLexer cl;
     protected Token lastToken;
diff --git a/calculator-java/src/main/java/ch/bfh/parser/ParserException.java b/calculator-java/src/main/java/ch/bfh/parser/ParserException.java
new file mode 100644
index 0000000..a85e3b2
--- /dev/null
+++ b/calculator-java/src/main/java/ch/bfh/parser/ParserException.java
@@ -0,0 +1,8 @@
+package ch.bfh.parser;
+
+import ch.bfh.lexer.Token;
+
+public class ParserException extends RuntimeException {
+    public ParserException(String s) { super(s); }
+    public ParserException(Token token, String s) { super("Last read token: '"+token.str+"', "+s); }
+}
\ 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
index c85398b..482ef34 100644
--- a/calculator-java/src/main/java/ch/bfh/parser/StatementParser.java
+++ b/calculator-java/src/main/java/ch/bfh/parser/StatementParser.java
@@ -1,8 +1,7 @@
 package ch.bfh.parser;
 
-import ch.bfh.CalculatorLexer;
-import ch.bfh.Token;
-import ch.bfh.exceptions.ParserException;
+import ch.bfh.lexer.CalculatorLexer;
+import ch.bfh.lexer.Token;
 
 public class StatementParser extends Parser{
 
@@ -27,15 +26,15 @@ public class StatementParser extends Parser{
             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.");
+                        throw new ParserException("The keyword 'let' can only be placed 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).");
+                        throw new ParserException("The token '=' can only be placed in a variable declaration context (let var = 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.");
+                        throw new ParserException("The keyword 'exit' can only be placed at the beginning of an expression.");
                     else
                         System.exit(0);
                 case Token.ID:
@@ -58,7 +57,7 @@ public class StatementParser extends Parser{
         if (lastToken == null)
             return;
         if (lastToken.type == Token.LET || lastToken.type == Token.EQU)
-            throw new ParserException("Incomplete variable declaration");
+            throw new ParserException("Incomplete variable declaration. Expected: let var = Expression.");
     }
 
     @Override
diff --git a/calculator-java/src/main/java/ch/bfh/parser/TermParser.java b/calculator-java/src/main/java/ch/bfh/parser/TermParser.java
index 9a97659..9e49fa8 100644
--- a/calculator-java/src/main/java/ch/bfh/parser/TermParser.java
+++ b/calculator-java/src/main/java/ch/bfh/parser/TermParser.java
@@ -1,8 +1,7 @@
 package ch.bfh.parser;
 
-import ch.bfh.CalculatorLexer;
-import ch.bfh.Token;
-import ch.bfh.exceptions.ParserException;
+import ch.bfh.lexer.CalculatorLexer;
+import ch.bfh.lexer.Token;
 
 class TermParser extends ExpressionParser {
 
@@ -61,9 +60,11 @@ class TermParser extends ExpressionParser {
                 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.");
+                case Token.EQU:
+                case Token.LET:
+                    throw new ParserException("The inputted token '"+token.str+"' can only be placed in a variable declaration context (let var = Expression).");
+                case Token.END:
+                    throw new ParserException("The keyword 'exit' can only be placed at the beginning of an expression.");
             }
             token = cl.nextToken();
         }
-- 
cgit v1.2.3