Lexer::position()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
namespace Cerbero\JsonParser\Tokens;
4
5
use Cerbero\JsonParser\Exceptions\SyntaxException;
6
use Cerbero\JsonParser\Sources\Source;
7
use Cerbero\JsonParser\Tokens\Token;
8
use Cerbero\JsonParser\Tokens\Tokenizer;
9
use Cerbero\JsonParser\Tokens\Tokens;
10
use Cerbero\JsonParser\ValueObjects\Progress;
11
use IteratorAggregate;
12
use Traversable;
13
14
use function strlen;
15
16
/**
17
 * The JSON lexer.
18
 *
19
 * @implements IteratorAggregate<int, Token>
20
 */
21
final class Lexer implements IteratorAggregate
22
{
23
    /**
24
     * The parsing progress.
25
     *
26
     * @var Progress
27
     */
28
    private readonly Progress $progress;
29
30
    /**
31
     * The current position.
32
     *
33
     * @var int
34
     */
35
    private int $position = 0;
36
37
    /**
38
     * Instantiate the class.
39
     *
40
     * @param Source $source
41
     */
42 373
    public function __construct(private readonly Source $source)
43
    {
44 373
        $this->progress = new Progress();
0 ignored issues
show
Bug introduced by
The property progress is declared read-only in Cerbero\JsonParser\Tokens\Lexer.
Loading history...
45
    }
46
47
    /**
48
     * Retrieve the JSON fragments
49
     *
50
     * @return \Generator<int, Token>
51
     */
52 361
    public function getIterator(): Traversable
53
    {
54 361
        $buffer = '';
55 361
        $inString = $isEscaping = false;
56 361
        $tokenizer = Tokenizer::instance();
57
58 361
        foreach ($this->source as $chunk) {
59 358
            for ($i = 0, $size = strlen($chunk); $i < $size; $i++, $this->position++) {
60 358
                $character = $chunk[$i];
61 358
                $inString = ($character == '"') != $inString || $isEscaping;
62 358
                $isEscaping = $character == '\\' && !$isEscaping;
63
64 358
                if ($inString || !isset(Tokens::BOUNDARIES[$character])) {
65 304
                    $buffer == '' && !isset(Tokens::TYPES[$character]) && throw new SyntaxException($character);
66 301
                    $buffer .= $character;
67 301
                    continue;
68
                }
69
70 357
                if ($buffer != '') {
71 301
                    yield $tokenizer->toToken($buffer);
72 297
                    $buffer = '';
73
                }
74
75 357
                if (isset(Tokens::DELIMITERS[$character])) {
76 357
                    yield $tokenizer->toToken($character);
77
                }
78
            }
79
        }
80
    }
81
82
    /**
83
     * Retrieve the current position
84
     *
85
     * @return int
86
     */
87 11
    public function position(): int
88
    {
89 11
        return $this->position;
90
    }
91
92
    /**
93
     * Retrieve the parsing progress
94
     *
95
     * @return Progress
96
     */
97 1
    public function progress(): Progress
98
    {
99 1
        return $this->progress->setCurrent($this->position)->setTotal($this->source->size());
100
    }
101
}
102