Passed
Branch feature/first-release (4f172a)
by Andrea Marco
01:55
created

Lexer::position()   A

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 Progress $progress;
29
30
    /**
31
     * The current position.
32
     *
33
     * @var int
34
     */
35
    private int $position = 1;
36
37
    /**
38
     * Instantiate the class.
39
     *
40
     * @param Source $source
41
     */
42 342
    public function __construct(private Source $source)
43
    {
44 342
        $this->progress = new Progress();
45
    }
46
47
    /**
48
     * Retrieve the JSON fragments
49
     *
50
     * @return \Generator<int, Token>
51
     */
52 330
    public function getIterator(): Traversable
53
    {
54 330
        $buffer = '';
55 330
        $inString = $isEscaping = false;
56
57 330
        foreach ($this->source as $chunk) {
58 327
            for ($i = 0, $size = strlen($chunk); $i < $size; $i++, $this->position++) {
59 327
                $character = $chunk[$i];
60 327
                $inString = ($character == '"') != $inString || $isEscaping;
61 327
                $isEscaping = $character == '\\' && !$isEscaping;
62 327
                $shouldBuffer = $inString || !isset(Tokens::BOUNDARIES[$character]);
63
64 327
                if ($shouldBuffer && $buffer == '' && !isset(Tokens::TYPES[$character])) {
65 10
                    throw new SyntaxException($character);
66
                }
67
68 326
                if ($shouldBuffer) {
69 274
                    $buffer .= $character;
70 274
                    continue;
71
                }
72
73 326
                if ($buffer != '') {
74 274
                    yield Tokenizer::instance()->toToken($buffer);
75 270
                    $buffer = '';
76
                }
77
78 326
                if (isset(Tokens::DELIMITERS[$character])) {
79 326
                    yield Tokenizer::instance()->toToken($character);
80
                }
81
            }
82
        }
83
    }
84
85
    /**
86
     * Retrieve the current position
87
     *
88
     * @return int
89
     */
90 11
    public function position(): int
91
    {
92 11
        return $this->position;
93
    }
94
95
    /**
96
     * Retrieve the parsing progress
97
     *
98
     * @return Progress
99
     */
100
    public function progress(): Progress
101
    {
102
        return $this->progress->setCurrent($this->position)->setTotal($this->source->size());
103
    }
104
}
105