Passed
Push — master ( e5131a...cc97fa )
by Alexander
01:17
created

Parser::parseFunction()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 12
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 19
rs 9.8666
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
     * @return Visitable
128
     * @throws Exception\LexerException
129
     * @throws ParseError
130
     * @throws Exception\UnknownIdentifier
131
     */
132
    private function term(): Visitable
133
    {
134
        $node = $this->factor();
135
136
        while (in_array($this->currentToken->type, [Token::MUL, Token::REALDIV, Token::POWER], true)) {
137
            /** @var Token $operator */
138
            $operator = $this->currentToken;
139
            /** @psalm-suppress PossiblyNullArgument */
140
            $this->eat($this->currentToken->type);
141
            $node = new BinaryOperator($node, $operator, $this->factor());
142
        }
143
144
        return $node;
145
    }
146
147
    /**
148
     * @return Visitable
149
     * @throws Exception\LexerException
150
     * @throws ParseError
151
     * @throws Exception\UnknownIdentifier
152
     */
153
    private function expr(): Visitable
154
    {
155
        $node = $this->term();
156
157
        while (in_array($this->currentToken->type, [Token::PLUS, Token::MINUS], true)) {
158
            /** @var Token $operator */
159
            $operator = $this->currentToken;
160
            /** @psalm-suppress PossiblyNullArgument */
161
            $this->eat($this->currentToken->type);
162
            $node = new BinaryOperator($node, $operator, $this->term());
163
        }
164
165
        return $node;
166
    }
167
168
    /**
169
     * @return Visitable
170
     * @throws Exception\LexerException
171
     * @throws ParseError
172
     * @throws Exception\UnknownIdentifier
173
     */
174
    public function parse(): Visitable
175
    {
176
        return $this->expr();
177
    }
178
}