diff --git a/src/Parser.java b/src/Parser.java new file mode 100644 index 0000000000000000000000000000000000000000..358b1879fbcad3a41fb10d98d012282b86728377 --- /dev/null +++ b/src/Parser.java @@ -0,0 +1,167 @@ +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +public class Parser { + + private abstract interface Expression { + + } + + private abstract class Value implements Expression { + + } + + private class Number extends Value { + public String original; + public Number(String i) { + this.original = i; + } + } + + private class Decimal extends Value { + public Number beforeDot; + public char dot; + public Number afterDot; + + public Decimal(Number i1, Number i2) { + this.beforeDot = i1; + dot = '.'; + this.afterDot = i2; + } + } + + //starting method of the parser + //parses a list of tokens + public Expression parse(List<Lexer.Token> list) throws ParserException { + if(list.isEmpty()) { + throw new ParserException("empty token list"); + } + + List<Lexer.Token> ts = new LinkedList<>(list); + Expression ast = parseExpression(ts); + + if(!ts.isEmpty()) { + throw new ParserException("SyntaxError: " + ts.size() + " Token(s) left"); + } + + return ast; + } + + //called by method parse(...) + //parses a list of tokens into an expression + private Expression parseExpression(List<Lexer.Token> ts) throws ParserException { + if(ts.isEmpty()) { + throw new ParserException("empty token list"); + } + + return parseValue(ts).orElseGet(() -> null); + } + + //checks if a String only contains an allowed operator with parseCharacter(...) + private void parseOperator(String operator) throws ParserException { + if(operator.length()>1) { + throw new ParserException("RuntimeException: invalid length for an operator: " + operator); + } + parseCharacter(operator, "+-*/^"); + } + + //called by methods parseOperator(...), parseDigit(...), parseDigitWithoutZero(...) + //checks if a certain string can be found in a string of allowed character + //if yes: return true + //if not: a ParserException is thrown + private Boolean parseCharacter(String data, String allowedCharacters) throws ParserException { + if(!allowedCharacters.contains(data)) { + throw new ParserException("SyntaxError: Invalid character: " + data); + } + if(data.isEmpty()) { + throw new ParserException("RuntimeException: empty String"); + } + return true; + } + + //called by method parseException(...) + //parses a list of tokens into a value + private Optional<Expression> parseValue(List<Lexer.Token> ts) throws ParserException { + if (ts.isEmpty()) { + throw new ParserException("SyntaxError: expected a number"); + } + if (ts.get(0).getType() != Lexer.TokenType.NUMBER) { + return Optional.empty(); + } + + String data = ts.remove(0).getData(); + if (data.isEmpty()) { + throw new ParserException("RuntimeException: empty token"); + } + + if (ts.size()>0) { + //if the next token is of TokenType.SPECIAL, check if it's a comma + //if it is, create a decimal + if(ts.get(0).getType() == Lexer.TokenType.SPECIAL) { + if(ts.size()>1) { + if(parseComma(ts.get(0).getData())) { + return Optional.of(parseDecimal(ts, data)); + } + } + } + } + //if the next token wasn't a comma, create a number + return Optional.of(parseNumber(data)); + } + + //called by method parseValue(...) + //parses a decimal of a list of tokens & a string + private Expression parseDecimal(List<Lexer.Token> ts, String data) throws ParserException { + if(ts.size()<1) { + throw new ParserException("SyntaxError: "); + } + if(ts.get(0).getType() != Lexer.TokenType.SPECIAL) { + throw new ParserException(""); + } + if(ts.get(1).getType() != Lexer.TokenType.NUMBER) { + throw new ParserException(""); + } + Number beforeDot = (Number) parseNumber(data); + ts.remove(0); + data = ts.remove(0).getData(); + Number afterDot = (Number) parseNumber(data); + return new Decimal(beforeDot, afterDot); + } + + //called by method parseValue(...) + //parses a String into a number + private Expression parseNumber(String data) throws ParserException{ + if (data.isEmpty()) { + throw new ParserException("RuntimeException: empty token"); + } + + if (data.startsWith("0") && data.length() == 1) { + return new Number(data); + } + + parseDigitWithoutZero(data); + 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++) { + parseCharacter(Character.toString(data.charAt(index)), "0123456789"); + } + } + + //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++) { + parseCharacter(Character.toString(data.charAt(index)),"123456789"); + } + } + + private Boolean parseComma(String data) throws ParserException { + return true; + } +} diff --git a/src/ParserException.java b/src/ParserException.java new file mode 100644 index 0000000000000000000000000000000000000000..ef36000d208d794a262a186f4f844606f35ab1d0 --- /dev/null +++ b/src/ParserException.java @@ -0,0 +1,5 @@ +public class ParserException extends Exception{ + public ParserException(String message) { + super(message); + } +}