From 16856cd848345a61f14b1e7f14de536f4510c8f1 Mon Sep 17 00:00:00 2001 From: Kraemerd <Dominic_Daniel.Kraemer@Student.Reutlingen-University.de> Date: Mon, 9 Jan 2023 18:27:35 +0100 Subject: [PATCH] implemented final version of Parser.java and a fitting test class ParserTest.java --- expressions.txt | 4 ++ src/main/Parser.java | 85 +++++++++++++++++++++++++++++----------- src/test/ParserTest.java | 39 +++++++++++++++++- 3 files changed, 103 insertions(+), 25 deletions(-) diff --git a/expressions.txt b/expressions.txt index a5fb886..bcba895 100644 --- a/expressions.txt +++ b/expressions.txt @@ -1,2 +1,6 @@ +2*(4^2) +(4+2)*4 +(4+2*(4-2)) +((3+2)*(12+2)) 1+(x^2.22)-(3*x) 2*x+0.5*(x^2+4)-1 \ No newline at end of file diff --git a/src/main/Parser.java b/src/main/Parser.java index ac39ec2..e29e094 100644 --- a/src/main/Parser.java +++ b/src/main/Parser.java @@ -116,11 +116,12 @@ public class Parser { return ast; } - - - - - + //called by parseExpression + //checks if there are multiple tokens left, which indicates that they build up a BinaryOperation + //It splits up the tokens into a left and right expression and with a recursive call of parseExpression + //each part will be determined, until all BinaryOperations consist of finite left and right expressions + //Then a new BinaryOperation will be returned, which saves the left- & right expression and the operator + //between them private BinaryOperation parseBinaryOperation(List<Lexer.Token> ts) throws ParserException { if(ts.isEmpty()) { throw new ParserException("SyntaxError: empty token list"); @@ -128,76 +129,111 @@ public class Parser { if(ts.size()==1) { return null; } - int index = 0; + //Index will be determined in the next if/else statement + //It will indicate the position of the operator that separates the left- and right expression + int index; int tokensInBracket = parseBrackets(ts); boolean capsuled; + //If the result of parseBrackets(ts)+1 is the same as ts.size the current BinaryOperation is capsuled if(tokensInBracket+1==ts.size()) { + //removes the tokens with the capsuling brackets ts.remove(tokensInBracket); ts.remove(0); capsuled = true; - index = 1; + //Checks again for brackets + tokensInBracket = parseBrackets(ts); + //If no bracket is following, the operator needs to be at position 1 of ts + if(tokensInBracket==-1) { + index = 1; + } + else { + //If brackets are following, the operator is at position tokensInBracket+1 + index = tokensInBracket+1; + } } + //If tokensInBracket is -1 this expression is not capsuled and the operator needs to be at position 1 of ts else if (tokensInBracket==-1) { capsuled = false; - parseOperator(ts.get(1).getData()); index = 1; } + //If both other cases were not fulfilled an inner expression is capsuled + //Then this BinaryOperation is not capsuled, no brackets need to be removed and the operator is at + //position tokensInBracket+1 else { capsuled = false; index = tokensInBracket+1; } + //Checks if the operator of this BinaryOperation is permitted + parseOperator(ts.get(index).getData()); List<Lexer.Token> leftList = new LinkedList<>(); + //Splits the token list ts into a left and right expression for(int iterator = 0; iterator<index; iterator++) { leftList.add(ts.remove(0)); } Expression leftExpression = parseExpression(leftList); + //Operator is now at position 0 String operator = ts.remove(0).getData(); Expression rightExpression = parseExpression(ts); return new BinaryOperation(leftExpression, operator, rightExpression, capsuled); } - //checks if a String only contains an allowed operator with parseCharacter(...) - private boolean parseOperator(String operator) throws ParserException { - if(operator.length()>1) { - throw new ParserException("SyntaxError: invalid length for an operator: " + operator); - } - if(!parseCharacter(operator, "+-*/^")) { - throw new ParserException("SyntaxError: invalid operator character: " + operator); - } - return true; - } + //called by parseBinaryOperator + //checks if the first token is a bracket + //If yes: checks if the bracket gets closed correctly and returns the index of the closing bracket + //If no: returns -1 private int parseBrackets(List<Lexer.Token> ts) throws ParserException{ + if(ts.isEmpty()) { + throw new ParserException("SyntaxError: empty token list"); + } if(parseCharacter(ts.get(0).getData(), "(")) { int index; int lBrackets = 0; int rBrackets = 0; boolean found = false; + //Iterates through every token, until the closing bracket got found 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 more ')' got found then '(' the bracket got closed correctly and the loop can be interrupted if(rBrackets>lBrackets) { found = true; break; } } } + //If no matching closing bracket was found a ParserException will be thrown if(!found) { throw new ParserException("SyntaxError: brackets never got closed"); } + //If the closing bracket was placed immediately after the opening bracket a ParserException will be thrown if(index==1) { throw new ParserException("SyntaxError: no value inside brackets"); } + //Returns the index of the closing bracket return index; } return -1; } - + //called by parseBinaryOperation + //checks if a String only contains an allowed operator with parseCharacter + //if not: throws a ParserException + private void parseOperator(String operator) throws ParserException { + if(operator.isEmpty()) { + throw new ParserException("RuntimeError: empty String instead of an operator (+, -, *, /, ^"); + } + if(operator.length()>1) { + throw new ParserException("SyntaxError: invalid length for an operator: " + operator); + } + if(!parseCharacter(operator, "+-*/^")) { + throw new ParserException("SyntaxError: invalid operator character: " + operator); + } + } //called by parseExpression //Checks if a token is a variable: @@ -280,7 +316,10 @@ public class Parser { //If afterDot is false, the first digit is not allowed to be 0 if(!afterDot) { parseDigitWithoutZero(data.substring(0,1)); - parseDigit(data.substring(1)); + + if (data.length()>1) { + parseDigit(data.substring(1)); + } } //Checks if all digits of the number are permitted else { @@ -293,10 +332,10 @@ public class Parser { //checks if a String only contains numbers(including zero) with parseCharacter //if not, a ParserException is thrown private void parseDigit(String data) throws ParserException { - if(data.isEmpty()) { - throw new ParserException("RuntimeError: empty String instead of a digit(0-9)"); - } for(int index=0; index<data.length(); index++) { + if(data.isEmpty()) { + throw new ParserException("RuntimeError: empty String instead of a digit(0-9)"); + } String character = Character.toString(data.charAt(index)); if(!parseCharacter(character, "0123456789")) { throw new ParserException("SyntaxError: unexpected character: " + character); diff --git a/src/test/ParserTest.java b/src/test/ParserTest.java index 090bb60..2d70d2e 100644 --- a/src/test/ParserTest.java +++ b/src/test/ParserTest.java @@ -7,8 +7,6 @@ import org.junit.Test; import java.util.LinkedList; import java.util.List; -import static org.junit.jupiter.api.Assertions.assertEquals; - public class ParserTest { @Test @@ -62,4 +60,41 @@ public class ParserTest { e.printStackTrace(); } } + + @Test + void testParseBinaryOperation() { + List<Lexer.Token> tokens = new LinkedList<>(); + tokens.add(new Lexer.Token(Lexer.TokenType.SPECIAL,"(")); + tokens.add(new Lexer.Token(Lexer.TokenType.NUMBER,"5")); + tokens.add(new Lexer.Token(Lexer.TokenType.SPECIAL,"*")); + tokens.add(new Lexer.Token(Lexer.TokenType.NUMBER,"2")); + tokens.add(new Lexer.Token(Lexer.TokenType.SPECIAL,")")); + tokens.add(new Lexer.Token(Lexer.TokenType.NUMBER,"*")); + tokens.add(new Lexer.Token(Lexer.TokenType.NUMBER,"3")); + var parser = new Parser(); + try { + var ast = parser.parse(tokens); + } + catch(Exception e) { + e.printStackTrace(); + } + } + + @Test + void testParseWrongBinaryOperation() { + List<Lexer.Token> tokens = new LinkedList<>(); + tokens.add(new Lexer.Token(Lexer.TokenType.SPECIAL,"(")); + tokens.add(new Lexer.Token(Lexer.TokenType.NUMBER,"5")); + tokens.add(new Lexer.Token(Lexer.TokenType.SPECIAL,"*")); + tokens.add(new Lexer.Token(Lexer.TokenType.NUMBER,"2")); + tokens.add(new Lexer.Token(Lexer.TokenType.SPECIAL,")")); + tokens.add(new Lexer.Token(Lexer.TokenType.NUMBER,"3")); + var parser = new Parser(); + try { + var ast = parser.parse(tokens); + } + catch(Exception e) { + e.printStackTrace(); + } + } } -- GitLab