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; } } } }