Centralize state mutation logic

This commit is contained in:
Andrea Marco Sartori 2022-12-03 20:27:45 +10:00
parent a758cf570b
commit ab8c10c05d
10 changed files with 76 additions and 182 deletions

View File

@ -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()) {

View File

@ -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.
*

View File

@ -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;
}
}
/**

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
/**

View File

@ -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
*