Tokenizer::getNextToken()   A
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5.0909

Importance

Changes 0
Metric Value
cc 5
eloc 12
nc 5
nop 0
dl 0
loc 23
ccs 11
cts 13
cp 0.8462
crap 5.0909
rs 9.5555
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace Diezz\YamlToObjectMapper\Resolver\Parser;
4
5
class Tokenizer
6
{
7
    public const T_BEGIN_OF_EXPRESSION = 1;
8
    public const T_END_OF_EXPRESSION = 2;
9
    public const T_SEMICOLON = 3;
10
    public const T_SINGLE_QUOTE = 4;
11
    public const T_DOUBLE_QUOTE = 5;
12
    public const T_STRING_LITERAL = 6;
13
    public const T_QUOTED_STRING_LITERAL = 7;
14
    public const T_NUMERIC_LITERAL = 8;
15
    public const T_DASH = 9;
16
    public const T_UNDERSCORE = 10;
17
    public const T_ARRAY_BEGIN = 11;
18
    public const T_ARRAY_END = 12;
19
    public const T_COMMA = 13;
20
    public const T_SPACE = 14;
21
    public const T_DOT = 15;
22
23
24
    private string $string;
25
    private int $cursor;
26
27 17
    public function __construct(string $string)
28
    {
29 17
        $this->string = $string;
30 17
        $this->cursor = 0;
31 17
    }
32
33
    /**
34
     * @throws SyntaxException
35
     */
36 17
    public function getNextToken(): ?Token
37
    {
38 17
        if (!$this->hasMoreToken()) {
39 13
            return null;
40
        }
41
42 17
        $string = substr($this->string, $this->cursor);
43
44 17
        foreach ($this->getSpec() as $specItem) {
45 17
            $tokenType = $specItem->getTokenType();
46 17
            $tokenValue = $this->match($specItem->getPattern(), $string);
47 17
            if ($tokenValue === null) {
48 17
                continue;
49
            }
50
51 17
            if ($tokenType === null) {
52
                return $this->getNextToken();
53
            }
54
55 17
            return Token::of($tokenType, $tokenValue);
56
        }
57
58
        throw new SyntaxException("Unexpected token ${string[0]}");
59
    }
60
61
    public function isEOF(): bool
62
    {
63
        return $this->cursor === strlen($this->string);
64
    }
65
66 17
    public function isScalar(): bool
67
    {
68 17
        return preg_match("/\\$\{.*\}/", $this->string) === 0;
69
    }
70
71 4
    public function getString(): string
72
    {
73 4
        return $this->string;
74
    }
75
76
    /**
77
     * Match a token for a regular expression
78
     */
79 17
    private function match(string $pattern, string $string): ?string
80
    {
81 17
        preg_match($pattern, $string, $matches);
82 17
        if (empty($matches)) {
83 17
            return null;
84
        }
85
86 17
        $result = $matches[0];
87 17
        $this->cursor += strlen($result);
88
89 17
        return $result;
90
    }
91
92 17
    private function hasMoreToken(): bool
93
    {
94 17
        return $this->cursor < strlen($this->string);
95
    }
96
97
    /**
98
     * @return SpecItem[]
99
     */
100 17
    private function getSpec(): array
101
    {
102
        return [
103
            //Skip spaces
104 17
            SpecItem::of("/^\s+/", self::T_SPACE),
105
106
            //Special chars
107 17
            SpecItem::of("/^\\$\{/", self::T_BEGIN_OF_EXPRESSION),
108 17
            SpecItem::of("/^\}/", self::T_END_OF_EXPRESSION),
109 17
            SpecItem::of("/^\:/", self::T_SEMICOLON),
110 17
            SpecItem::of("/^-/", self::T_DASH),
111 17
            SpecItem::of("/^\[/", self::T_ARRAY_BEGIN),
112 17
            SpecItem::of("/^\]/", self::T_ARRAY_END),
113 17
            SpecItem::of("/^\,/", self::T_COMMA),
114 17
            SpecItem::of("/^\./", self::T_DOT),
115
116
            // Literals
117 17
            SpecItem::of("/^\"[^\"]*\"/", self::T_QUOTED_STRING_LITERAL),
118 17
            SpecItem::of("/^'[^']*'/", self::T_QUOTED_STRING_LITERAL),
119 17
            SpecItem::of("/^\w+/", self::T_STRING_LITERAL),
120 17
            SpecItem::of("/^\d+/", self::T_NUMERIC_LITERAL),
121
        ];
122
    }
123
124
}
125