Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
Parser.java 9.49 KiB
package main;

import Exceptions.ParserException;

import java.util.LinkedList;
import java.util.List;

public class Parser {

    public abstract class Expression {
        public abstract <T> T accept(Visitor<T> visitor);
    }

    public class BinaryOperation extends Expression {
        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;
        }
        public <T> T accept(Visitor<T> visitor) {
            return visitor.visit(this);
        }

        public <T> T accept(Visitor<T> vistor, String operator) {
            return vistor.visit(this);
        }
    }

    public class Variable extends Expression {
        String variableName;
        public Variable(String i, Boolean capsuled) {
            this.variableName = i;
        }
        public <T> T accept(Visitor<T> visitor) {
            return visitor.visit(this);
        }
    }

    private abstract class Value extends Expression {

    }

    public class Number extends Value {
        public String digits;
        public Number(String i) {
            this.digits = i;
        }
        public <T> T accept(Visitor<T> visitor) {
            return visitor.visit(this);
        }
    }

    public class Decimal extends Value {
        public Number beforeDot;
        public Number afterDot;

        public Decimal(Number i1, Number i2) {
            this.beforeDot = i1;
            this.afterDot = i2;
        }
        public <T> T accept(Visitor<T> visitor) {
            return visitor.visit(this);
        }
    }
    //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("SyntaxError: empty token list");
        }
        Expression ast = parseBinaryOperation(ts);
            if(ast==null) {
                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 {
        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;
    }

    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");
        }
        if(ts.size()==1) {
            return null;
        }
        int index = 0;
        int tokensInBracket = parseBrackets(ts);
        boolean capsuled;
        if(tokensInBracket+1==ts.size()) {
            ts.remove(tokensInBracket);
            ts.remove(0);
            capsuled = true;
            index = 1;
        }
        else if (tokensInBracket==-1) {
            capsuled = false;
            parseOperator(ts.get(1).getData());
            index = 1;
        }
        else {
            capsuled = false;
            index = tokensInBracket+1;
        }
        List<Lexer.Token> leftList = new LinkedList<>();
        for(int iterator = 0; iterator<index; iterator++) {
            leftList.add(ts.remove(0));
        }
        Expression leftExpression = parseExpression(leftList);
        String operator = ts.remove(0).getData();
        Expression rightExpression = parseExpression(ts);
        return new BinaryOperation(leftExpression, operator, rightExpression, capsuled);
    }

    private Variable parseVariable(List<Lexer.Token> ts) throws ParserException {
        if (ts.isEmpty()) {
            throw new ParserException("SyntaxError: empty token list");
        }
        if (ts.get(0).getType() != Lexer.TokenType.VARIABLE) {
            return null;
        }

        String data = ts.remove(0).getData();
        if (data.isEmpty()) {
            throw new ParserException("SyntaxError: empty token");
        }

        if(!parseCharacter(data,"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")) {
            throw new ParserException("Invalid variable character: " + data);
        }
        return new Variable(data, false);
    }

    //called by method parseException(...)
    //parses a token/list of tokens into a value
    private Value parseValue(List<Lexer.Token> ts) throws ParserException {
        if (ts.isEmpty()) {
            throw new ParserException("SyntaxError: empty token list");
        }
        if (ts.get(0).getType() != Lexer.TokenType.NUMBER) {
            return null;
        }
        String data = ts.remove(0).getData();
        if (data.isEmpty()) {
            throw new ParserException("SyntaxError: empty token");
        }

        if(data.contains(".")) {
            return parseDecimal(data);
        }
        return parseNumber(data, false);
    }

    //called by method parseValue(...)
    //parses a decimal of a list of tokens & a string
    private Decimal parseDecimal(String data) throws ParserException {
        if(data.isEmpty()) {
            throw new ParserException("SyntaxError: empty 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, boolean afterDot) throws ParserException{
        if (data.isEmpty()) {
            throw new ParserException("RuntimeException: empty token");
        }

        if (data.startsWith("0") && data.length() == 1) {
            return new Number(data);
        }
        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=0; index<data.length(); index++) {
            String character = Character.toString(data.charAt(index));
            if(!parseCharacter(character, "0123456789")) {
                throw new ParserException("SyntaxError: unexpected character: " + character);
            }
        }
    }

    //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=0; index<data.length(); index++) {
            String character = Character.toString(data.charAt(index));
            if(!parseCharacter(character, "123456789")) {
                throw new ParserException("SyntaxError: unexpected character: " + character);
            }
        }
    }

    //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 {
        if(data.isEmpty()) {
            throw new ParserException("RuntimeError: empty String");
        }
        return (allowedCharacters.contains(data));
    }
}