aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaël Gassmann <mael.gassmann@students.bfh.ch>2021-06-11 01:39:49 +0200
committerMaël Gassmann <mael.gassmann@students.bfh.ch>2021-06-11 01:39:49 +0200
commit126a6c85b60ad07fa29ab48315f0f195fb45d854 (patch)
tree1c0245fc232c701da8577a5451d703a0d5808822
parentee369b50b14ffe662e05355459f57c076680cd58 (diff)
[+] First version of the java calculator
-rw-r--r--calculator-java/src/main/java/ch/bfh/CalculatorLexer.java (renamed from calculator-java/src/main/java/CalculatorLexer.java)7
-rw-r--r--calculator-java/src/main/java/ch/bfh/Main.java23
-rw-r--r--calculator-java/src/main/java/ch/bfh/Token.java (renamed from calculator-java/src/main/java/Token.java)4
-rw-r--r--calculator-java/src/main/java/ch/bfh/exceptions/LexerException.java (renamed from calculator-java/src/main/java/exceptions/LexerException.java)2
-rw-r--r--calculator-java/src/main/java/ch/bfh/exceptions/ParserException.java5
-rw-r--r--calculator-java/src/main/java/ch/bfh/parser/ExpressionParser.java270
-rw-r--r--calculator-java/src/main/java/ch/bfh/parser/Parser.java16
7 files changed, 322 insertions, 5 deletions
diff --git a/calculator-java/src/main/java/CalculatorLexer.java b/calculator-java/src/main/java/ch/bfh/CalculatorLexer.java
index 61b906c..ba10944 100644
--- a/calculator-java/src/main/java/CalculatorLexer.java
+++ b/calculator-java/src/main/java/ch/bfh/CalculatorLexer.java
@@ -1,7 +1,8 @@
-// Lexer for classical arithmetic expressions with identifiers and assignements.
-// Scans a source string char by char.
+package ch.bfh;
+import ch.bfh.exceptions.LexerException;
-import exceptions.LexerException;
+// Lexer for classical arithmetic expressions with identifiers and assignments.
+// Scans a source string char by char.
public class CalculatorLexer {
diff --git a/calculator-java/src/main/java/ch/bfh/Main.java b/calculator-java/src/main/java/ch/bfh/Main.java
new file mode 100644
index 0000000..b0dedf3
--- /dev/null
+++ b/calculator-java/src/main/java/ch/bfh/Main.java
@@ -0,0 +1,23 @@
+package ch.bfh;
+
+import ch.bfh.parser.ExpressionParser;
+
+import java.util.Arrays;
+
+public class Main {
+
+ public static void main(String[] args){
+ CalculatorLexer cl = new CalculatorLexer();
+
+ //TODO - command line reader instead of arg
+
+ // Combines the arguments & init the lexer
+ cl.initLexer(
+ Arrays.stream(args).reduce("", (expression, arg) -> expression + arg)
+ );
+
+ ExpressionParser ep = new ExpressionParser(cl);
+
+ System.out.println(ep.getValue());
+ }
+}
diff --git a/calculator-java/src/main/java/Token.java b/calculator-java/src/main/java/ch/bfh/Token.java
index 4fe8b23..93d4d02 100644
--- a/calculator-java/src/main/java/Token.java
+++ b/calculator-java/src/main/java/ch/bfh/Token.java
@@ -1,4 +1,6 @@
-// Various Tokens for arithmetic expressions based on integers
+package ch.bfh;
+
+// Various Tokens for arithmetic expressions based on integers
// with identifiers and assignments
public class Token {
diff --git a/calculator-java/src/main/java/exceptions/LexerException.java b/calculator-java/src/main/java/ch/bfh/exceptions/LexerException.java
index b7b1f24..082beee 100644
--- a/calculator-java/src/main/java/exceptions/LexerException.java
+++ b/calculator-java/src/main/java/ch/bfh/exceptions/LexerException.java
@@ -1,4 +1,4 @@
-package exceptions;
+package ch.bfh.exceptions;
public class LexerException extends RuntimeException {
public LexerException(String s) { super(s); }
diff --git a/calculator-java/src/main/java/ch/bfh/exceptions/ParserException.java b/calculator-java/src/main/java/ch/bfh/exceptions/ParserException.java
new file mode 100644
index 0000000..ca6d037
--- /dev/null
+++ b/calculator-java/src/main/java/ch/bfh/exceptions/ParserException.java
@@ -0,0 +1,5 @@
+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/parser/ExpressionParser.java b/calculator-java/src/main/java/ch/bfh/parser/ExpressionParser.java
new file mode 100644
index 0000000..229e700
--- /dev/null
+++ b/calculator-java/src/main/java/ch/bfh/parser/ExpressionParser.java
@@ -0,0 +1,270 @@
+package ch.bfh.parser;
+
+import ch.bfh.CalculatorLexer;
+import ch.bfh.Token;
+import ch.bfh.exceptions.ParserException;
+
+import java.util.ArrayList;
+
+public class ExpressionParser extends Parser {
+
+ protected ArrayList<Parser> parsers = new ArrayList<>();
+ protected ArrayList<Token> ops = new ArrayList<>();
+
+ public ExpressionParser(CalculatorLexer cl) {
+ this.cl = cl;
+ parse();
+ }
+
+ private ExpressionParser(CalculatorLexer cl, Token lastToken) {
+ this.cl = cl;
+ this.lastToken = lastToken;
+ parse();
+ }
+
+ @Override
+ protected void parse() {
+ Token token = cl.nextToken();
+ 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: // '+' is not authorised if not op
+ if (l != null && op == null) { // if the '+' sign is between two term (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 if (l == null)
+ throw new ParserException("Factor cannot start with '+'.");
+ else
+ throw new ParserException("Invalid use of '+' was found in the Expression.");
+ break;
+ case Token.SUB:
+ if (l != null && op == null) { // if the '-' sign is between two term (op)
+ op = token;
+ break;
+ } 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);
+ } // Then the '-' is the start of a new expression and not an operation
+ case Token.PAL:
+ case Token.NUM:
+ if (l == null) {
+ l = new TermParser(cl, token);
+ this.parsers.add(l);
+ token = l.lastToken;
+ } else {
+ r = new TermParser(cl, token);
+ this.parsers.add(r);
+ token = r.lastToken;
+ }
+ continue loop;
+ case Token.PAR:
+ if (lastToken.type == Token.PAL) { // if equal to '(' it means it was created by a Factor
+ lastToken = token;
+ 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.");
+ }
+ 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.ADD:
+ result += this.parsers.get(i).getValue();
+ break;
+ case Token.SUB:
+ result -= this.parsers.get(i).getValue();
+ break;
+ }
+ }
+ }
+ 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)
+ 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
new file mode 100644
index 0000000..755313a
--- /dev/null
+++ b/calculator-java/src/main/java/ch/bfh/parser/Parser.java
@@ -0,0 +1,16 @@
+package ch.bfh.parser;
+
+import ch.bfh.CalculatorLexer;
+import ch.bfh.Token;
+
+abstract class Parser{
+ protected CalculatorLexer cl;
+ protected Token lastToken;
+ protected double value;
+
+ protected abstract void parse();
+ public double getValue(){
+ return value;
+ }
+}
+