mirror of
https://github.com/cerbero90/json-parser.git
synced 2025-01-17 04:58:15 +01:00
Centralize state mutation logic
This commit is contained in:
parent
a758cf570b
commit
ab8c10c05d
@ -60,16 +60,16 @@ class Parser implements IteratorAggregate
|
||||
$this->state->setPointers(...$this->config->pointers);
|
||||
|
||||
foreach ($this->lexer as $token) {
|
||||
$token->mutateState($this->state);
|
||||
$this->state->mutateByToken($token);
|
||||
|
||||
if (!$token->endsChunk() || $this->state->treeIsDeep()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->state->hasBuffer() && $this->state->inObject()) {
|
||||
yield $this->decoder->decode($this->state->key()) => $this->decoder->decode($this->state->pullBuffer());
|
||||
yield $this->decoder->decode($this->state->key()) => $this->decoder->decode($this->state->value());
|
||||
} elseif ($this->state->hasBuffer() && !$this->state->inObject()) {
|
||||
yield $this->decoder->decode($this->state->pullBuffer());
|
||||
yield $this->decoder->decode($this->state->value());
|
||||
}
|
||||
|
||||
if ($this->state->canStopParsing()) {
|
||||
|
@ -27,6 +27,13 @@ class Pointer implements ArrayAccess, Stringable
|
||||
*/
|
||||
protected int $depth;
|
||||
|
||||
/**
|
||||
* Whether the pointer was found.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public bool $wasFound = false;
|
||||
|
||||
/**
|
||||
* Instantiate the class.
|
||||
*
|
||||
|
@ -74,7 +74,10 @@ class Pointers implements Countable
|
||||
*/
|
||||
public function markAsFound(Pointer $pointer): void
|
||||
{
|
||||
$this->found[(string) $pointer] = true;
|
||||
if (!$pointer->wasFound) {
|
||||
$pointer->wasFound = true;
|
||||
$this->found[(string) $pointer] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
196
src/State.php
196
src/State.php
@ -52,7 +52,7 @@ class State
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $expectsKey = false;
|
||||
public bool $expectsKey = false;
|
||||
|
||||
/**
|
||||
* Instantiate the class.
|
||||
@ -73,16 +73,6 @@ class State
|
||||
return $this->tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the tree should be tracked
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldTrackTree(): bool
|
||||
{
|
||||
return $this->pointer == '' || $this->tree->depth() < $this->pointer->depth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the tree is deep
|
||||
*
|
||||
@ -105,51 +95,6 @@ class State
|
||||
return $this->tree->currentKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the given object key
|
||||
*
|
||||
* @param string $key
|
||||
* @return void
|
||||
*/
|
||||
public function traverseKey(string $key): void
|
||||
{
|
||||
$this->tree->traverseKey($key);
|
||||
|
||||
$this->treeChanged = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse a JSON array
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function traverseArray(): void
|
||||
{
|
||||
$this->tree->traverseArray($this->pointer);
|
||||
|
||||
$this->treeChanged = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the tree changed
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function treeChanged(): bool
|
||||
{
|
||||
return $this->treeChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the JSON tree as not changed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function treeDidNotChange(): void
|
||||
{
|
||||
$this->treeChanged = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the current position is within an object
|
||||
*
|
||||
@ -157,7 +102,10 @@ class State
|
||||
*/
|
||||
public function inObject(): bool
|
||||
{
|
||||
return $this->tree->inObject();
|
||||
$tree = $this->tree->original();
|
||||
$depth = $this->tree->depth();
|
||||
|
||||
return is_string($tree[$depth] ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -170,41 +118,9 @@ class State
|
||||
{
|
||||
$this->pointers = new Pointers(...$pointers);
|
||||
|
||||
$this->matchPointer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JSON pointer matching the tree
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function matchPointer(): void
|
||||
{
|
||||
$this->pointer = $this->pointers->matchTree($this->tree);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new matching JSON pointer when the tree changes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rematchPointer(): void
|
||||
{
|
||||
if ($this->treeChanged && $this->pointers->count() > 1) {
|
||||
$this->matchPointer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the JSON pointer matching the tree
|
||||
*
|
||||
* @return Pointer
|
||||
*/
|
||||
public function pointer(): Pointer
|
||||
{
|
||||
return $this->pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the parser can stop parsing
|
||||
*
|
||||
@ -215,12 +131,60 @@ class State
|
||||
return $this->pointers->wereFound() && !$this->pointer->includesTree($this->tree);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutate state depending on the given token
|
||||
*
|
||||
* @param Token $token
|
||||
* @return void
|
||||
*/
|
||||
public function mutateByToken(Token $token): void
|
||||
{
|
||||
$this->treeChanged = false;
|
||||
$shouldTrackTree = $this->pointer == '' || $this->tree->depth() < $this->pointer->depth();
|
||||
|
||||
if ($shouldTrackTree && $token->isValue() && !$this->inObject()) {
|
||||
$this->tree->traverseArray($this->pointer);
|
||||
$this->treeChanged = true;
|
||||
}
|
||||
|
||||
if ($shouldTrackTree && $token->isString() && $this->expectsKey) {
|
||||
$this->tree->traverseKey($token);
|
||||
$this->treeChanged = true;
|
||||
}
|
||||
|
||||
$this->bufferToken($token);
|
||||
|
||||
if ($this->treeChanged && $this->pointers->count() > 1) {
|
||||
$this->pointer = $this->pointers->matchTree($this->tree);
|
||||
}
|
||||
|
||||
$token->mutateState($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer the given token
|
||||
*
|
||||
* @param Token $token
|
||||
* @return void
|
||||
*/
|
||||
protected function bufferToken(Token $token): void
|
||||
{
|
||||
$shouldBuffer = $this->tree->depth() >= 0
|
||||
&& $this->pointerMatchesTree()
|
||||
&& ($this->treeIsDeep() || ($token->isValue() && !$this->expectsKey));
|
||||
|
||||
if ($shouldBuffer) {
|
||||
$this->buffer .= $token;
|
||||
$this->pointers->markAsFound($this->pointer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the tree matches the JSON pointer
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function pointerMatchesTree(): bool
|
||||
protected function pointerMatchesTree(): bool
|
||||
{
|
||||
return $this->pointer == ''
|
||||
|| in_array($this->pointer->referenceTokens(), [$this->tree->original(), $this->tree->wildcarded()]);
|
||||
@ -237,29 +201,11 @@ class State
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer the given token
|
||||
*
|
||||
* @param Token $token
|
||||
* @return void
|
||||
*/
|
||||
public function bufferToken(Token $token): void
|
||||
{
|
||||
$shouldBuffer = $this->tree->depth() >= 0
|
||||
&& $this->pointerMatchesTree()
|
||||
&& ($this->treeIsDeep() || ($token->isValue() && !$this->expectsKey()));
|
||||
|
||||
if ($shouldBuffer) {
|
||||
$this->buffer .= $token;
|
||||
$this->pointers->markAsFound($this->pointer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve and reset the buffer
|
||||
* Retrieve the value from the buffer and reset it
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function pullBuffer(): string
|
||||
public function value(): string
|
||||
{
|
||||
$buffer = $this->buffer;
|
||||
|
||||
@ -267,34 +213,4 @@ class State
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether an object key is expected
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function expectsKey(): bool
|
||||
{
|
||||
return $this->expectsKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expect an object key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function expectKey(): void
|
||||
{
|
||||
$this->expectsKey = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not expect any object key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function doNotExpectKey(): void
|
||||
{
|
||||
$this->expectsKey = false;
|
||||
}
|
||||
}
|
||||
|
@ -28,10 +28,8 @@ class Comma extends Token
|
||||
*/
|
||||
public function mutateState(State $state): void
|
||||
{
|
||||
parent::mutateState($state);
|
||||
|
||||
if ($state->inObject()) {
|
||||
$state->expectKey();
|
||||
$state->expectsKey = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,12 +28,10 @@ class CompoundBegin extends Token
|
||||
*/
|
||||
public function mutateState(State $state): void
|
||||
{
|
||||
parent::mutateState($state);
|
||||
|
||||
$state->tree()->deepen();
|
||||
|
||||
if ($this->value == '{') {
|
||||
$state->expectKey();
|
||||
$state->expectsKey = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,12 +28,10 @@ class CompoundEnd extends Token
|
||||
*/
|
||||
public function mutateState(State $state): void
|
||||
{
|
||||
parent::mutateState($state);
|
||||
|
||||
$state->tree()->emerge();
|
||||
|
||||
if ($this->value == '}') {
|
||||
$state->doNotExpectKey();
|
||||
$state->expectsKey = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,10 +35,8 @@ class ScalarString extends Token
|
||||
*/
|
||||
public function mutateState(State $state): void
|
||||
{
|
||||
parent::mutateState($state);
|
||||
|
||||
if ($this->isKey = $state->expectsKey()) {
|
||||
$state->doNotExpectKey();
|
||||
if ($this->isKey = $state->expectsKey) {
|
||||
$state->expectsKey = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,19 +76,7 @@ abstract class Token implements Stringable
|
||||
*/
|
||||
public function mutateState(State $state): void
|
||||
{
|
||||
$state->treeDidNotChange();
|
||||
|
||||
if ($this->isValue() && !$state->inObject() && $state->shouldTrackTree()) {
|
||||
$state->traverseArray();
|
||||
}
|
||||
|
||||
if ($state->expectsKey() && $state->shouldTrackTree()) {
|
||||
$state->traverseKey($this);
|
||||
}
|
||||
|
||||
$state->bufferToken($this);
|
||||
|
||||
$state->rematchPointer();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
12
src/Tree.php
12
src/Tree.php
@ -83,18 +83,6 @@ class Tree implements IteratorAggregate
|
||||
$this->depth--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the tree is traversing an object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function inObject(): bool
|
||||
{
|
||||
$key = $this->original[$this->depth] ?? null;
|
||||
|
||||
return is_string($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the given object key
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user