Issues (2)

src/AbstractLexer.php (1 issue)

1
<?php
2
/**
3
 * This file is part of NACL.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @copyright 2019 Nuglif (2018) Inc.
9
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
10
 * @author    Pierrick Charron <[email protected]>
11
 * @author    Charle Demers <[email protected]>
12
 */
13
14
declare(strict_types=1);
15
16
namespace Nuglif\Nacl;
17
18
abstract class AbstractLexer
19
{
20
    protected const EOF           = -1;
21
    protected const STATE_INITIAL = 0;
22
23
    private array $regexes   = [];
24
    private array $tokenMaps = [];
25
26
    private int $state = self::STATE_INITIAL;
27
    private array $stack = [];
28
29
    protected int $line = 0;
30
    protected string $content = '';
31
    protected int $count = 0;
32
    protected string $filename = '';
33
34
    abstract protected function getRules(): array;
35
36 591
    public function __construct()
37
    {
38 591
        foreach ($this->getRules() as $state => $patterns) {
39 591
            $eofCallback = false;
40
41 591
            if (isset($patterns[self::EOF])) {
42 591
                $eofCallback = $patterns[self::EOF];
43 591
                unset($patterns[self::EOF]);
44
            }
45
46 591
            $this->regexes[$state]   = $this->computeRegex(array_keys($patterns));
47 591
            $this->tokenMaps[$state] = array_values($patterns);
48
49 591
            $this->tokenMaps[$state][-1] = $eofCallback;
50
        }
51
    }
52
53
    /**
54
     * @psalm-suppress InvalidReturnType
55
     */
56 590
    public function yylex(): Token
57
    {
58
        do {
59 590
            if (isset($this->content[$this->count])) {
60 589
                if (!preg_match($this->regexes[$this->state], $this->content, $matches, 0, $this->count)) {
61
                    $this->error(sprintf('Unexpected character "%s"', $this->content[$this->count]));
62
                }
63 589
                for ($i = 1; '' === $matches[$i]; ++$i) {
64
                }
65 589
                $this->count += strlen($matches[0]);
66 589
                $this->line  += substr_count($matches[0], "\n");
67
            } else {
68 585
                $i       = 0;
69 585
                $matches = [ '' ];
70
            }
71
72 590
            if ($this->tokenMaps[$this->state][$i - 1]) {
73 590
                $callback = $this->tokenMaps[$this->state][$i - 1];
74 590
                if ($token = $callback($matches[$i])) {
75 589
                    return new Token($token, $matches[$i]);
76
                }
77
            }
78 586
        } while ($i);
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return Nuglif\Nacl\Token. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
79
    }
80
81 3
    protected function error(string $errorMessage): void
82
    {
83 3
        throw new LexingException($errorMessage, $this->filename, $this->line);
84
    }
85
86 565
    protected function begin(int $state): void
87
    {
88 565
        $this->state = $state;
89
    }
90
91 591
    private function computeRegex(array $patterns): string
92
    {
93 591
        return '#\G(' . implode(')|\G(', $patterns) . ')#A';
94
    }
95
96 590
    public function push(string $content, string $filename): void
97
    {
98 590
        if ('' !== $this->content) {
99 10
            $this->stack[] = [
100 10
                $this->line,
101 10
                $this->content,
102 10
                $this->count,
103 10
                $this->filename,
104 10
            ];
105
        }
106
107 590
        $this->line     = 1;
108 590
        $this->content  = $content;
109 590
        $this->count    = 0;
110 590
        $this->filename = $filename;
111
    }
112
113 582
    public function pop(): bool
114
    {
115 582
        if (empty($this->stack)) {
116 582
            return false;
117
        }
118
119 10
        [
120 10
            $this->line,
121 10
            $this->content,
122 10
            $this->count,
123 10
            $this->filename
124 10
        ] = array_pop($this->stack);
125
126 10
        return true;
127
    }
128
129 13
    public function getLine(): int
130
    {
131 13
        return $this->line;
132
    }
133
134 24
    public function getFilename(): string
135
    {
136 24
        return $this->filename;
137
    }
138
}
139