Passed
Push — master ( 633b25...e5131a )
by Alexander
01:44 queued 10s
created

Parser::factor()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 48
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 31
c 1
b 0
f 0
nc 9
nop 0
dl 0
loc 48
rs 8.0555
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 Visitable
58
     * @throws Exception\LexerException
59
     * @throws ParseError
60
     * @throws Exception\UnknownIdentifier
61
     */
62
    public function factor(): Visitable
63
    {
64
        $token = $this->currentToken;
65
66
        if ($token->type === Token::PLUS) {
67
            $this->eat(Token::PLUS);
68
            return new UnaryOperator($token, $this->factor());
69
        }
70
        if ($token->type === Token::MINUS) {
71
            $this->eat(Token::MINUS);
72
            return new UnaryOperator($token, $this->factor());
73
        }
74
75
        if ($token->type === Token::INT) {
76
            $this->eat(Token::INT);
77
            return new Integer((int)$token->value);
78
        }
79
80
        if ($token->type === Token::REAL) {
81
            $this->eat(Token::REAL);
82
            return new Real((float)$token->value);
83
        }
84
85
        if ($token->type === Token::FUNCTION_CALL) {
86
            $this->eat(Token::FUNCTION_CALL);
87
            $this->eat(Token::LPAREN);
88
            $args = [];
89
            while (true) {
90
                $args[] = $this->expr();
91
                if ($this->currentToken->type === Token::COMMA) {
92
                    $this->eat(Token::COMMA);
93
                } else {
94
                    break;
95
                }
96
            }
97
            $this->eat(Token::RPAREN);
98
99
            return new FunctionCall($token, $args);
100
        }
101
102
        if ($token->type === Token::LPAREN) {
103
            $this->eat(Token::LPAREN);
104
            $node = $this->expr();
105
            $this->eat(Token::RPAREN);
106
            return $node;
107
        }
108
109
        throw new ParseError('Unknown factor type');
110
    }
111
112
    /**
113
     * @return Visitable
114
     * @throws Exception\LexerException
115
     * @throws ParseError
116
     * @throws Exception\UnknownIdentifier
117
     */
118
    public function term(): Visitable
119
    {
120
        $node = $this->factor();
121
122
        while (in_array($this->currentToken->type, [Token::MUL, Token::REALDIV, Token::POWER], true)) {
123
            /** @var Token $operator */
124
            $operator = $this->currentToken;
125
            /** @psalm-suppress PossiblyNullArgument */
126
            $this->eat($this->currentToken->type);
127
            $node = new BinaryOperator($node, $operator, $this->factor());
128
        }
129
130
        return $node;
131
    }
132
133
    /**
134
     * @return Visitable
135
     * @throws Exception\LexerException
136
     * @throws ParseError
137
     * @throws Exception\UnknownIdentifier
138
     */
139
    private function expr(): Visitable
140
    {
141
        $node = $this->term();
142
143
        while (in_array($this->currentToken->type, [Token::PLUS, Token::MINUS], true)) {
144
            /** @var Token $operator */
145
            $operator = $this->currentToken;
146
            /** @psalm-suppress PossiblyNullArgument */
147
            $this->eat($this->currentToken->type);
148
            $node = new BinaryOperator($node, $operator, $this->term());
149
        }
150
151
        return $node;
152
    }
153
154
    /**
155
     * @return Visitable
156
     * @throws Exception\LexerException
157
     * @throws ParseError
158
     * @throws Exception\UnknownIdentifier
159
     */
160
    public function parse(): Visitable
161
    {
162
        return $this->expr();
163
    }
164
}