Passed
Push — master ( 8427a2...6e446b )
by Alexander
01:16
created

Parser::exponent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 0
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php
2
3
4
namespace App;
5
6
7
use App\Exception\ParseError;
8
use App\Node\BinaryOperator;
9
use App\Node\FunctionCall;
10
use App\Node\Integer;
11
use App\Node\Real;
12
use App\Node\UnaryOperator;
13
use App\Node\Visitable;
14
15
class Parser
16
{
17
    /**
18
     * @var Lexer
19
     */
20
    private $lexer;
21
22
    /**
23
     * @var Token
24
     */
25
    private $currentToken;
26
27
    /**
28
     * Parser constructor.
29
     * @param Lexer $lexer
30
     * @throws Exception\LexerException
31
     * @throws Exception\UnknownIdentifier
32
     */
33
    public function __construct(
34
        Lexer $lexer
35
    )
36
    {
37
        $this->lexer        = $lexer;
38
        $this->currentToken = $this->lexer->getNextToken();
39
    }
40
41
    /**
42
     * @param string $tokenType
43
     * @throws Exception\LexerException
44
     * @throws ParseError
45
     * @throws Exception\UnknownIdentifier
46
     */
47
    private function eat(string $tokenType): void
48
    {
49
        if ($this->currentToken->type === $tokenType) {
50
            $this->currentToken = $this->lexer->getNextToken();
51
        } else {
52
            throw new ParseError('Wrong token type');
53
        }
54
    }
55
56
    /**
57
     * @return FunctionCall
58
     * @throws Exception\LexerException
59
     * @throws Exception\UnknownIdentifier
60
     * @throws ParseError
61
     */
62
    private function parseFunction(): FunctionCall
63
    {
64
        $token = $this->currentToken;
65
        $this->eat(Token::FUNCTION_CALL);
66
        $this->eat(Token::LPAREN);
67
        $args = [];
68
69
        while (true) {
70
            $args[] = $this->expr();
71
            if ($this->currentToken->type === Token::COMMA) {
72
                $this->eat(Token::COMMA);
73
            } else {
74
                break;
75
            }
76
        }
77
78
        $this->eat(Token::RPAREN);
79
80
        return new FunctionCall($token, $args);
81
    }
82
83
    /**
84
     * @return Visitable
85
     * @throws Exception\LexerException
86
     * @throws ParseError
87
     * @throws Exception\UnknownIdentifier
88
     */
89
    private function factor(): Visitable
90
    {
91
        $token = $this->currentToken;
92
93
        if ($token->type === Token::PLUS) {
94
            $this->eat(Token::PLUS);
95
            return new UnaryOperator($token, $this->factor());
96
        }
97
        if ($token->type === Token::MINUS) {
98
            $this->eat(Token::MINUS);
99
            return new UnaryOperator($token, $this->factor());
100
        }
101
102
        if ($token->type === Token::INT) {
103
            $this->eat(Token::INT);
104
            return new Integer((int)$token->value);
105
        }
106
107
        if ($token->type === Token::REAL) {
108
            $this->eat(Token::REAL);
109
            return new Real((float)$token->value);
110
        }
111
112
        if ($token->type === Token::FUNCTION_CALL) {
113
            return $this->parseFunction();
114
        }
115
116
        if ($token->type === Token::LPAREN) {
117
            $this->eat(Token::LPAREN);
118
            $node = $this->expr();
119
            $this->eat(Token::RPAREN);
120
            return $node;
121
        }
122
123
        throw new ParseError('Unknown factor type');
124
    }
125
126
    /**
127
     * @throws Exception\LexerException
128
     * @throws Exception\UnknownIdentifier
129
     * @throws ParseError
130
     */
131
    private function exponent(): Visitable
132
    {
133
        $node = $this->factor();
134
135
        while ($this->currentToken->type === Token::POWER) {
136
            $operator = $this->currentToken;
137
            $this->eat(Token::POWER);
138
            $node = new BinaryOperator($node, $operator, $this->exponent());
139
        }
140
141
        return $node;
142
    }
143
144
    /**
145
     * @return Visitable
146
     * @throws Exception\LexerException
147
     * @throws ParseError
148
     * @throws Exception\UnknownIdentifier
149
     */
150
    private function term(): Visitable
151
    {
152
        $node = $this->exponent();
153
154
        while (in_array($this->currentToken->type, [Token::MUL, Token::REALDIV, Token::POWER], true)) {
155
            /** @var Token $operator */
156
            $operator = $this->currentToken;
157
            /** @psalm-suppress PossiblyNullArgument */
158
            $this->eat($this->currentToken->type);
159
            $node = new BinaryOperator($node, $operator, $this->exponent());
160
        }
161
162
        return $node;
163
    }
164
165
    /**
166
     * @return Visitable
167
     * @throws Exception\LexerException
168
     * @throws ParseError
169
     * @throws Exception\UnknownIdentifier
170
     */
171
    private function expr(): Visitable
172
    {
173
        $node = $this->term();
174
175
        while (in_array($this->currentToken->type, [Token::PLUS, Token::MINUS], true)) {
176
            /** @var Token $operator */
177
            $operator = $this->currentToken;
178
            /** @psalm-suppress PossiblyNullArgument */
179
            $this->eat($this->currentToken->type);
180
            $node = new BinaryOperator($node, $operator, $this->term());
181
        }
182
183
        return $node;
184
    }
185
186
    /**
187
     * @return Visitable
188
     * @throws Exception\LexerException
189
     * @throws ParseError
190
     * @throws Exception\UnknownIdentifier
191
     */
192
    public function parse(): Visitable
193
    {
194
        return $this->expr();
195
    }
196
}