diff --git a/expressions.txt b/expressions.txt index 2b40ceaf50da3b0bfc6d612b6c9576e312e575a9..8fd2860c05d82f1195d3ce9a1e1827eb4a9a1419 100644 --- a/expressions.txt +++ b/expressions.txt @@ -1 +1 @@ -40.2+3*x \ No newline at end of file +(5*2-(5^2)+3*(201.01-15)) \ No newline at end of file diff --git a/src/Application.java b/src/Application.java index f8ac1ee4a8efcb6326fb7f33612862193a4dc8fb..a4c744fcf4d30465f3709796e7ba4eae0af8acde 100644 --- a/src/Application.java +++ b/src/Application.java @@ -12,10 +12,10 @@ public class Application { List<String> expressions = new LinkedList<>(); //Reads the file "expressions.txt" and adds its values to a list BufferedReader reader; - String datapath = "expressions.txt"; + String dataPath = "expressions.txt"; try { System.out.println("Reading the file...\n"); - reader = new BufferedReader(new FileReader(datapath)); + reader = new BufferedReader(new FileReader(dataPath)); String line = reader.readLine(); while(line != null) { expressions.add(line); @@ -39,8 +39,8 @@ public class Application { } Parser parser = new Parser(); try { - parser.parse(tokens); - System.out.println(); + Parser.Expression exp = parser.parse(tokens); + System.out.println(exp.toString()); } catch (ParserException e) { System.out.println(e.getMessage()); diff --git a/src/Lexer.java b/src/Lexer.java index a65c71b0985fbd51f4fc9a793228e2bd2baf31a9..c3806955e133a727f09a745550f50d27c1aaa1a7 100644 --- a/src/Lexer.java +++ b/src/Lexer.java @@ -43,13 +43,16 @@ public class Lexer { //checks if a character is a digit, then creates a token with one or multiple numbers //with TokenType=NUMBER if(Character.isDigit(current)) { - int endIndex = index+1; + int endIndex = index; if(endIndex<input.length()) { while(Character.isDigit(input.charAt(endIndex)) && endIndex<input.length()) { endIndex += 1; if(endIndex==input.length()) { break; } + if(input.charAt(endIndex) == '.') { + endIndex+=1; + } } } Token token = new Token(TokenType.NUMBER, input.substring(index,endIndex)); diff --git a/src/Parser.java b/src/Parser.java index fcc6fa333dc8dce123c0c474f149f5d35d9e0ee2..e8f6331d43ef4b14d1ccaeb6ea16547206150b75 100644 --- a/src/Parser.java +++ b/src/Parser.java @@ -1,29 +1,44 @@ import java.util.LinkedList; import java.util.List; -import java.util.Optional; public class Parser { - private abstract class Expression { - + public static class Expression { + @Override + public String toString() { + return ""; + } } private class BinaryOperation extends Expression { Expression leftExpression; String operator; Expression rightExpression; - public BinaryOperation(Expression leftExpression, String operator, Expression rightExpression) { + Boolean capsuled; + public BinaryOperation(Expression leftExpression, String operator, Expression rightExpression, Boolean capsuled) { this.leftExpression = leftExpression; this.operator = operator; this.rightExpression = rightExpression; + this.capsuled = capsuled; + } + @Override + public String toString() { + if(capsuled) { + return "("+ leftExpression.toString() + operator + rightExpression.toString() + ")"; + } + return leftExpression.toString() + operator + rightExpression.toString(); } } private class Variable extends Expression { String variableName; - public Variable(String i) { + public Variable(String i, Boolean capsuled) { this.variableName = i; } + @Override + public String toString() { + return variableName; + } } private abstract class Value extends Expression { @@ -35,6 +50,10 @@ public class Parser { public Number(String i) { this.digits = i; } + @Override + public String toString() { + return digits; + } } private class Decimal extends Value { @@ -45,6 +64,10 @@ public class Parser { this.beforeDot = i1; this.afterDot = i2; } + @Override + public String toString() { + return beforeDot.toString() + "." + afterDot.toString(); + } } //starting method of the parser @@ -58,7 +81,7 @@ public class Parser { Expression ast = parseExpression(ts); if(!ts.isEmpty()) { - throw new ParserException("RuntimeError: " + ts.size() + " token(s) left"); + throw new ParserException("SyntaxError: " + ts.size() + " token(s) left"); } return ast; } @@ -69,22 +92,21 @@ public class Parser { if(ts.isEmpty()) { throw new ParserException("SyntaxError: empty token list"); } - Expression ast; - ast = parseBinaryOperation(ts); - if(ast==null) { - ast = parseVariable(ts); + Expression ast = parseBinaryOperation(ts); if(ast==null) { - ast = parseValue(ts); - } - if(ast==null) { - throw new ParserException("SyntaxError: invalid character"); + ast = parseVariable(ts); + if(ast==null) { + ast = parseValue(ts); + } + if(ast==null) { + throw new ParserException("SyntaxError: invalid character"); + } } - } return ast; } //checks if a String only contains an allowed operator with parseCharacter(...) - private Boolean parseOperator(String operator) throws ParserException { + private boolean parseOperator(String operator) throws ParserException { if(operator.length()>1) { throw new ParserException("SyntaxError: invalid length for an operator: " + operator); } @@ -94,6 +116,35 @@ public class Parser { return true; } + private int parseBrackets(List<Lexer.Token> ts) throws ParserException{ + if(parseCharacter(ts.get(0).getData(), "(")) { + int index; + int lBrackets = 0; + int rBrackets = 0; + boolean found = false; + for(index=1; index<ts.size(); index++) { + if(parseCharacter(ts.get(index).getData(), "(")) { + lBrackets += 1; + } + else if(parseCharacter(ts.get(index).getData(), ")")) { + rBrackets += 1; + if(rBrackets>lBrackets) { + found = true; + break; + } + } + } + if(!found) { + throw new ParserException("SyntaxError: brackets never got closed"); + } + if(index==1) { + throw new ParserException("SyntaxError: no value inside brackets"); + } + return index; + } + return -1; + } + private BinaryOperation parseBinaryOperation(List<Lexer.Token> ts) throws ParserException { if(ts.isEmpty()) { throw new ParserException("SyntaxError: empty token list"); @@ -101,52 +152,32 @@ public class Parser { if(ts.size()==1) { return null; } - int index; - for(index=0; index<ts.size(); index++) { - if(ts.get(index).getType()== Lexer.TokenType.SPECIAL) { - if(!parseCharacter(ts.get(index).getData(), ".")) { - break; - } - } + int index = 0; + int tokensInBracket = parseBrackets(ts); + boolean capsuled; + if(tokensInBracket+1==ts.size()) { + ts.remove(tokensInBracket); + ts.remove(0); + capsuled = true; + index = 1; } - if(index==ts.size()) { - return null; + else if (tokensInBracket==-1) { + capsuled = false; + parseOperator(ts.get(1).getData()); + index = 1; + } + else { + capsuled = false; + index = tokensInBracket+1; } - //0 - //1 - //2 - //3 - //4 * => index=4 - - //5 - //6 - //7 List<Lexer.Token> leftList = new LinkedList<>(); - for(int i=0; i<index; i++) { + for(int iterator = 0; iterator<index; iterator++) { leftList.add(ts.remove(0)); } Expression leftExpression = parseExpression(leftList); String operator = ts.remove(0).getData(); - parseOperator(operator); - Expression rightExpression = parseExpression(ts); - - /* - if(parseCharacter(ts.get(1).getData(), ".")) { - return null; - } - BinaryOperation operation; - if(ts.get(1).getType()!= Lexer.TokenType.SPECIAL) { - throw new ParserException("SyntaxError: unexpected operator: " + ts.get(1).getData()); - } - List<Lexer.Token> leftList = new LinkedList<>(); - leftList.add(ts.remove(0)); - Expression leftExpression = parseExpression(leftList); - String operator = ts.remove(0).getData(); - parseOperator(operator); Expression rightExpression = parseExpression(ts); - - */ - return new BinaryOperation(leftExpression, operator, rightExpression); + return new BinaryOperation(leftExpression, operator, rightExpression, capsuled); } private Variable parseVariable(List<Lexer.Token> ts) throws ParserException { @@ -165,7 +196,7 @@ public class Parser { if(!parseCharacter(data,"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")) { throw new ParserException("Invalid variable character: " + data); } - return new Variable(data); + return new Variable(data, false); } //called by method parseException(...) @@ -177,47 +208,34 @@ public class Parser { if (ts.get(0).getType() != Lexer.TokenType.NUMBER) { return null; } - String data = ts.get(0).getData(); + String data = ts.remove(0).getData(); if (data.isEmpty()) { throw new ParserException("SyntaxError: empty token"); } - Value value = null; - if (ts.size()>1) { - //if the next token is of TokenType.SPECIAL, check if it's a comma - //if it is, create a decimal - if(ts.get(1).getType() == Lexer.TokenType.SPECIAL) { - if(parseComma(ts)) { - value = parseDecimal(ts); - } - } + if(data.contains(".")) { + return parseDecimal(data); } - if(value==null) { - value = parseNumber(ts.remove(0).getData()); - } - return value; + return parseNumber(data, false); } //called by method parseValue(...) //parses a decimal of a list of tokens & a string - private Decimal parseDecimal(List<Lexer.Token> ts) throws ParserException { - String data = ts.remove(0).getData(); + private Decimal parseDecimal(String data) throws ParserException { if(data.isEmpty()) { throw new ParserException("SyntaxError: empty data"); } - if(ts.size()==0) { - throw new ParserException("SyntaxError: no tokens left to create Decimal"); - } - Number beforeDot = (Number) parseNumber(data); - ts.remove(0); - data = ts.remove(0).getData(); - Number afterDot = (Number) parseNumber(data); + int dot = data.indexOf('.'); + String number1 = data.substring(0,dot); + String number2 = data.substring(dot+1); + Number beforeDot = (Number) parseNumber(number1, true); + Number afterDot = (Number) parseNumber(number2, false); return new Decimal(beforeDot, afterDot); } //called by method parseValue(...) //parses a String into a number - private Number parseNumber(String data) throws ParserException{ + private Number parseNumber(String data, boolean afterDot) throws ParserException{ if (data.isEmpty()) { throw new ParserException("RuntimeException: empty token"); } @@ -225,16 +243,20 @@ public class Parser { if (data.startsWith("0") && data.length() == 1) { return new Number(data); } - - parseDigitWithoutZero(data.substring(0,1)); - parseDigit(data.substring(1)); + if(afterDot) { + parseDigitWithoutZero(data.substring(0,1)); + parseDigit(data.substring(1)); + } + else { + parseDigit(data); + } return new Number(data); } //called by method parseNumber(...) //checks if a String only contains numbers(including zero) with parseCharacter(...) private void parseDigit(String data) throws ParserException { - for(int index=1; index<data.length(); index++) { + for(int index=0; index<data.length(); index++) { String character = Character.toString(data.charAt(index)); if(!parseCharacter(character, "0123456789")) { throw new ParserException("SyntaxError: unexpected character: " + character); @@ -245,7 +267,7 @@ public class Parser { //called by method parseNumber(...) //checks if a String only contains numbers(excluding zero) with parseCharacter(...) private void parseDigitWithoutZero(String data) throws ParserException { - for(int index=1; index<data.length(); index++) { + for(int index=0; index<data.length(); index++) { String character = Character.toString(data.charAt(index)); if(!parseCharacter(character, "123456789")) { throw new ParserException("SyntaxError: unexpected character: " + character); @@ -253,30 +275,9 @@ public class Parser { } } - private Boolean parseComma(List<Lexer.Token> ts) throws ParserException { - String data = ts.get(1).getData(); - if(parseCharacter(data, ".")) { - if(ts.get(2).getType()!= Lexer.TokenType.NUMBER) { - throw new ParserException("SyntaxError: no number after comma"); - } - return true; - } - return false; - } - - private Boolean parseBracket(List<Lexer.Token> ts) throws ParserException { - String data = ts.get(0).getData(); - if(parseCharacter(data, "(")) { - return true; - } - else { - return false; - } - } - //called by methods parseOperator(...), parseDigit(...), parseDigitWithoutZero(...) //checks if a certain string can be found in a string of allowed character - private Boolean parseCharacter(String data, String allowedCharacters) throws ParserException { + private boolean parseCharacter(String data, String allowedCharacters) throws ParserException { if(data.isEmpty()) { throw new ParserException("RuntimeError: empty String"); }