aboutsummaryrefslogtreecommitdiff
path: root/calculator-java/src/main/java/ch/bfh/parser/ExpressionParser.java
blob: 91effbde90da76a8ec2a56a72147c1901b9189f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package ch.bfh.parser;

import ch.bfh.lexer.CalculatorLexer;
import ch.bfh.lexer.Token;
import java.util.ArrayList;

class ExpressionParser extends Parser {

    protected ArrayList<Parser> parsers = new ArrayList<>();
    protected ArrayList<Token> ops = new ArrayList<>();
    private boolean isParenthesised = false;

    protected ExpressionParser(CalculatorLexer cl, Token lastToken, boolean isParenthesised) {
        this.cl = cl;
        this.lastToken = lastToken;
        this.isParenthesised = isParenthesised; //If an expression starts with the lastToken set as '(' it could mean that the Expression first term will be a parenthesised expression
        parse();                                //But it could also mean that this Expression is the parenthesised one, that is why this variable is required.
    }

    @Override
    protected void parse() {
        Token token;
        if(lastToken != null && !isParenthesised)
            token = lastToken;
        else
            token = cl.nextToken();
        TermParser l = null;
        TermParser r = null; // left and right Terms
        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("Redundant use of '+' is forbidden.");
                    else
                        throw new ParserException(token, "repetition of operator -> a term was expected.");
                    break;
                case Token.SUB:
                    if (l != null && op == null) { // if the '-' sign is between two term (op)
                        op = token;
                        this.ops.add(op);
                        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.ID:
                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 != null && lastToken.type == Token.PAL) { // if equal to '(' it means it was created by a Factor
                        if(parsers.size() == 0)
                            throw new ParserException("Empty parenthesis were found.");
                        lastToken = token;
                        break loop;
                    } else
                        throw new ParserException("No matching opening parenthesis were found.");
                case Token.MUL:
                case Token.DIV:
                    throw new ParserException(token,"a term was expected.");
                case Token.EQU:
                case Token.LET:
                    throw new ParserException("The inputted token '"+token.str+"' can only be placed in a variable declaration context (let var = Expression).");
                case Token.END:
                    throw new ParserException("The keyword 'exit' can only be placed at the beginning of an expression.");
            }
            token = cl.nextToken();
        }
        if (op != null && r == null)
            throw new ParserException("Missing term after the last operator '"+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;
    }
}