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

Parser::lazyLoad()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 9
c 0
b 0
f 0
nc 6
nop 0
dl 0
loc 15
ccs 9
cts 9
cp 1
crap 5
rs 9.6111
1
<?php
2
3
namespace Cerbero\JsonParser\Tokens;
4
5
use Cerbero\JsonParser\Decoders\ConfigurableDecoder;
6
use Cerbero\JsonParser\Exceptions\SyntaxException;
7
use Cerbero\JsonParser\Tokens\CompoundBegin;
8
use Cerbero\JsonParser\Tokens\CompoundEnd;
9
use Cerbero\JsonParser\Tokens\Token;
10
use Cerbero\JsonParser\ValueObjects\Config;
11
use Cerbero\JsonParser\ValueObjects\State;
12
use Generator;
13
use IteratorAggregate;
14
use Traversable;
15
16
/**
17
 * The JSON parser.
18
 *
19
 * @implements IteratorAggregate<string|int, mixed>
20
 */
21
final class Parser implements IteratorAggregate
22
{
23
    /**
24
     * The decoder handling potential errors.
25
     *
26
     * @var ConfigurableDecoder
27
     */
28
    private ConfigurableDecoder $decoder;
29
30
    /**
31
     * Whether the parser is fast-forwarding.
32
     *
33
     * @var bool
34
     */
35
    private bool $isFastForwarding = false;
36
37
    /**
38
     * Instantiate the class.
39
     *
40
     * @param Generator<int, Token> $tokens
41
     * @param Config $config
42
     */
43 342
    public function __construct(private Generator $tokens, private Config $config)
44
    {
45 342
        $this->decoder = new ConfigurableDecoder($config);
46
    }
47
48
    /**
49
     * Retrieve the JSON fragments
50
     *
51
     * @return Traversable<string|int, mixed>
52
     */
53 330
    public function getIterator(): Traversable
54
    {
55 330
        $state = new State($this->config->pointers, fn () => new self($this->lazyLoad(), clone $this->config));
56
57 330
        foreach ($this->tokens as $token) {
58 326
            if ($this->isFastForwarding) {
59 7
                continue;
60 326
            } elseif (!$token->matches($state->expectedToken)) {
61 1
                throw new SyntaxException($token);
62
            }
63
64 326
            $state->mutateByToken($token);
65
66 326
            if (!$token->endsChunk() || $state->tree()->isDeep()) {
67 326
                continue;
68
            }
69
70 324
            if ($state->hasBuffer()) {
71
                /** @var string|int $key */
72 238
                $key = $this->decoder->decode($state->key());
73 238
                $value = $this->decoder->decode($state->value());
74
75 237
                yield $key => $state->callPointer($value, $key);
76
77 237
                $value instanceof self && $value->fastForward();
78
            }
79
80 323
            if ($state->canStopParsing()) {
81 160
                break;
82
            }
83
        }
84
    }
85
86
    /**
87
     * Retrieve the generator to lazy load the current compound
88
     *
89
     * @return Generator<int, Token>
90
     */
91 30
    public function lazyLoad(): Generator
92
    {
93 30
        $depth = 0;
94
95
        do {
96 30
            yield $token = $this->tokens->current();
97
98 30
            if ($token instanceof CompoundBegin) {
99 30
                $depth++;
100 30
            } elseif ($token instanceof CompoundEnd) {
101 30
                $depth--;
102
            }
103
104 30
            $depth > 0 && $this->tokens->next();
105 30
        } while ($depth > 0);
106
    }
107
108
    /**
109
     * Eager load the current compound into an array
110
     *
111
     * @return array<string|int, mixed>
112
     */
113 190
    public function toArray(): array
114
    {
115 190
        $array = [];
116
117 190
        foreach ($this as $key => $value) {
118 136
            $array[$key] = $value instanceof self ? $value->toArray() : $value;
119
        }
120
121 190
        return $array;
122
    }
123
124
    /**
125
     * Fast-forward the parser
126
     *
127
     * @return void
128
     */
129 30
    public function fastForward(): void
130
    {
131 30
        if (!$this->tokens->valid()) {
132 23
            return;
133
        }
134
135 7
        $this->isFastForwarding = true;
136
137 7
        foreach ($this as $value) {
138
            $value instanceof self && $value->fastForward();
139
        }
140
    }
141
}
142