diff options
author | Maël Gassmann <mael.gassmann@students.bfh.ch> | 2021-06-11 01:39:49 +0200 |
---|---|---|
committer | Maël Gassmann <mael.gassmann@students.bfh.ch> | 2021-06-11 01:39:49 +0200 |
commit | 126a6c85b60ad07fa29ab48315f0f195fb45d854 (patch) | |
tree | 1c0245fc232c701da8577a5451d703a0d5808822 | |
parent | ee369b50b14ffe662e05355459f57c076680cd58 (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.java | 23 | ||||
-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.java | 5 | ||||
-rw-r--r-- | calculator-java/src/main/java/ch/bfh/parser/ExpressionParser.java | 270 | ||||
-rw-r--r-- | calculator-java/src/main/java/ch/bfh/parser/Parser.java | 16 |
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; + } +} + |