Optimize time consumption

This commit is contained in:
Andrea Marco Sartori 2023-01-23 20:13:01 +10:00
parent b74045e9e3
commit 18ea75da4b
6 changed files with 54 additions and 100 deletions

View File

@ -6,7 +6,6 @@ use Cerbero\JsonParser\Sources\Source;
use Cerbero\JsonParser\Tokens\Token;
use Cerbero\JsonParser\Tokens\Tokenizer;
use Cerbero\JsonParser\Tokens\Tokens;
use Generator;
use IteratorAggregate;
use Traversable;
@ -25,25 +24,11 @@ final class Lexer implements IteratorAggregate
private Progress $progress;
/**
* The buffer to yield.
* The current position.
*
* @var string
* @var int
*/
private string $buffer = '';
/**
* Whether the current character is escaped.
*
* @var bool
*/
private bool $isEscaping = false;
/**
* Whether the current character belongs to a string.
*
* @var bool
*/
private bool $inString = false;
private int $position = 0;
/**
* Instantiate the class.
@ -62,52 +47,33 @@ final class Lexer implements IteratorAggregate
*/
public function getIterator(): Traversable
{
$buffer = '';
$inString = $isEscaping = false;
foreach ($this->source as $chunk) {
for ($i = 0, $size = strlen($chunk); $i < $size; $i++, $this->progress->advance()) {
for ($i = 0, $size = strlen($chunk); $i < $size; $i++, $this->position++) {
$character = $chunk[$i];
$this->inString = $this->inString($character);
$this->isEscaping = $character == '\\' && !$this->isEscaping;
$inString = ($character == '"' && $inString && $isEscaping)
|| ($character != '"' && $inString)
|| ($character == '"' && !$inString);
$isEscaping = $character == '\\' && !$isEscaping;
yield from $this->yieldOrBufferCharacter($character);
}
}
if ($inString || !isset(Tokens::BOUNDARIES[$character])) {
$buffer .= $character;
continue;
}
/**
* Determine whether the given character is within a string
*
* @param string $character
* @return bool
*/
private function inString(string $character): bool
{
return ($character == '"' && $this->inString && $this->isEscaping)
|| ($character != '"' && $this->inString)
|| ($character == '"' && !$this->inString);
}
/**
* Yield the given character or buffer it
*
* @param string $character
* @return Generator<int, Token>
*/
private function yieldOrBufferCharacter(string $character): Generator
{
if ($this->inString || !isset(Tokens::BOUNDARIES[$character])) {
$this->buffer .= $character;
return;
}
if ($this->buffer != '') {
yield Tokenizer::instance()->toToken($this->buffer);
$this->buffer = '';
if ($buffer != '') {
yield Tokenizer::instance()->toToken($buffer);
$buffer = '';
}
if (isset(Tokens::DELIMITERS[$character])) {
yield Tokenizer::instance()->toToken($character);
}
}
}
}
/**
* Retrieve the parsing progress
@ -116,6 +82,6 @@ final class Lexer implements IteratorAggregate
*/
public function progress(): Progress
{
return $this->progress->setTotal($this->source->size());
return $this->progress->setCurrent($this->position)->setTotal($this->source->size());
}
}

View File

@ -32,7 +32,7 @@ final class Pointer implements Stringable
*
* @var Closure
*/
private Closure $callback;
private ?Closure $callback;
/**
* Whether the pointer was found.
@ -51,7 +51,7 @@ final class Pointer implements Stringable
{
$this->referenceTokens = $this->toReferenceTokens();
$this->depth = count($this->referenceTokens);
$this->callback = $callback ?: fn (mixed $value) => $value;
$this->callback = $callback;
}
/**
@ -100,6 +100,10 @@ final class Pointer implements Stringable
*/
public function call(mixed $value, mixed $key): mixed
{
if ($this->callback === null) {
return $value;
}
return call_user_func($this->callback, $value, $key) ?? $value;
}
@ -126,7 +130,9 @@ final class Pointer implements Stringable
*/
public function matchesTree(Tree $tree): bool
{
return in_array($this->referenceTokens, [[], $tree->original(), $tree->wildcarded()]);
return $this->referenceTokens == []
|| $this->referenceTokens == $tree->original()
|| $this->referenceTokens == $tree->wildcarded();
}
/**

View File

@ -23,13 +23,14 @@ final class Progress
private ?int $total = null;
/**
* Advance the progress
* Set the current progress
*
* @param int $current
* @return static
*/
public function advance(): static
public function setCurrent(int $current): static
{
$this->current++;
$this->current = $current;
return $this;
}

View File

@ -146,8 +146,9 @@ final class State
{
$treeChanged = false;
$shouldTrackTree = $this->pointer == '' || $this->tree->depth() < $this->pointer->depth();
$tokenIsValue = $token->isValue();
if ($shouldTrackTree && $token->isValue() && !$this->inObject()) {
if ($shouldTrackTree && $tokenIsValue && !$this->inObject()) {
$this->tree->traverseArray($this->pointer->referenceTokens());
$treeChanged = true;
}
@ -161,27 +162,16 @@ final class State
$this->pointer = $this->pointers->matchTree($this->tree);
}
$this->bufferToken($token);
$token->mutateState($this);
}
/**
* Buffer the given token
*
* @param Token $token
* @return void
*/
private function bufferToken(Token $token): void
{
$shouldBuffer = $this->tree->depth() >= 0
&& $this->pointer->matchesTree($this->tree)
&& ($this->treeIsDeep() || ($token->isValue() && !$this->expectsKey));
&& (($tokenIsValue && !$this->expectsKey) || $this->treeIsDeep());
if ($shouldBuffer) {
$this->buffer .= $token;
$this->pointers->markAsFound($this->pointer);
}
$token->mutateState($this);
}
/**

View File

@ -22,15 +22,6 @@ final class Tokenizer
*/
private array $tokensMap;
/**
* Instantiate the class.
*
*/
private function __construct()
{
$this->setTokensMap();
}
/**
* Retrieve the singleton instance
*
@ -41,6 +32,15 @@ final class Tokenizer
return static::$instance ??= new static();
}
/**
* Instantiate the class.
*
*/
private function __construct()
{
$this->setTokensMap();
}
/**
* Set the tokens map
*

View File

@ -92,16 +92,6 @@ final class Tree
$this->original[$this->depth] = $trimmedKey;
$this->wildcarded[$this->depth] = $trimmedKey;
$this->trim();
}
/**
* Trim the tree after the latest traversed key
*
* @return void
*/
private function trim(): void
{
array_splice($this->original, $this->depth + 1);
array_splice($this->wildcarded, $this->depth + 1);
}
@ -120,7 +110,8 @@ final class Tree
$this->original[$this->depth] = is_int($index) ? $index + 1 : 0;
$this->wildcarded[$this->depth] = $referenceToken == '-' ? '-' : $this->original[$this->depth];
$this->trim();
array_splice($this->original, $this->depth + 1);
array_splice($this->wildcarded, $this->depth + 1);
}
/**