From 126a6c85b60ad07fa29ab48315f0f195fb45d854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Gassmann?= Date: Fri, 11 Jun 2021 01:39:49 +0200 Subject: [+] First version of the java calculator --- .../main/java/ch/bfh/parser/ExpressionParser.java | 270 +++++++++++++++++++++ .../src/main/java/ch/bfh/parser/Parser.java | 16 ++ 2 files changed, 286 insertions(+) create mode 100644 calculator-java/src/main/java/ch/bfh/parser/ExpressionParser.java create mode 100644 calculator-java/src/main/java/ch/bfh/parser/Parser.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 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 parsers = new ArrayList<>(); + protected ArrayList 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; + } +} + -- cgit v1.2.3