diff --git a/src/Parser.php b/src/Parser.php index 828f6a8..80a9305 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -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()) { diff --git a/src/Pointers/Pointer.php b/src/Pointers/Pointer.php index 23ecf9e..3e76e2c 100644 --- a/src/Pointers/Pointer.php +++ b/src/Pointers/Pointer.php @@ -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. * diff --git a/src/Pointers/Pointers.php b/src/Pointers/Pointers.php index f489c0f..babce37 100644 --- a/src/Pointers/Pointers.php +++ b/src/Pointers/Pointers.php @@ -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; + } } /** diff --git a/src/State.php b/src/State.php index 85058a6..979c02f 100644 --- a/src/State.php +++ b/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; - } } diff --git a/src/Tokens/Comma.php b/src/Tokens/Comma.php index 4d96769..f172f6e 100644 --- a/src/Tokens/Comma.php +++ b/src/Tokens/Comma.php @@ -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; } } } diff --git a/src/Tokens/CompoundBegin.php b/src/Tokens/CompoundBegin.php index 684ab46..ffd3f12 100644 --- a/src/Tokens/CompoundBegin.php +++ b/src/Tokens/CompoundBegin.php @@ -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; } } } diff --git a/src/Tokens/CompoundEnd.php b/src/Tokens/CompoundEnd.php index 05ae632..261bed3 100644 --- a/src/Tokens/CompoundEnd.php +++ b/src/Tokens/CompoundEnd.php @@ -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; } } diff --git a/src/Tokens/ScalarString.php b/src/Tokens/ScalarString.php index 302abe6..dee0ebe 100644 --- a/src/Tokens/ScalarString.php +++ b/src/Tokens/ScalarString.php @@ -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; } } diff --git a/src/Tokens/Token.php b/src/Tokens/Token.php index a8be193..be51747 100644 --- a/src/Tokens/Token.php +++ b/src/Tokens/Token.php @@ -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; } /** diff --git a/src/Tree.php b/src/Tree.php index 735e026..5b59362 100644 --- a/src/Tree.php +++ b/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 *