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\Token;
use Cerbero\JsonParser\Tokens\Tokenizer; use Cerbero\JsonParser\Tokens\Tokenizer;
use Cerbero\JsonParser\Tokens\Tokens; use Cerbero\JsonParser\Tokens\Tokens;
use Generator;
use IteratorAggregate; use IteratorAggregate;
use Traversable; use Traversable;
@ -25,25 +24,11 @@ final class Lexer implements IteratorAggregate
private Progress $progress; private Progress $progress;
/** /**
* The buffer to yield. * The current position.
* *
* @var string * @var int
*/ */
private string $buffer = ''; private int $position = 0;
/**
* 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;
/** /**
* Instantiate the class. * Instantiate the class.
@ -62,53 +47,34 @@ final class Lexer implements IteratorAggregate
*/ */
public function getIterator(): Traversable public function getIterator(): Traversable
{ {
$buffer = '';
$inString = $isEscaping = false;
foreach ($this->source as $chunk) { 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]; $character = $chunk[$i];
$this->inString = $this->inString($character); $inString = ($character == '"' && $inString && $isEscaping)
$this->isEscaping = $character == '\\' && !$this->isEscaping; || ($character != '"' && $inString)
|| ($character == '"' && !$inString);
$isEscaping = $character == '\\' && !$isEscaping;
yield from $this->yieldOrBufferCharacter($character); if ($inString || !isset(Tokens::BOUNDARIES[$character])) {
$buffer .= $character;
continue;
}
if ($buffer != '') {
yield Tokenizer::instance()->toToken($buffer);
$buffer = '';
}
if (isset(Tokens::DELIMITERS[$character])) {
yield Tokenizer::instance()->toToken($character);
}
} }
} }
} }
/**
* 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 (isset(Tokens::DELIMITERS[$character])) {
yield Tokenizer::instance()->toToken($character);
}
}
/** /**
* Retrieve the parsing progress * Retrieve the parsing progress
* *
@ -116,6 +82,6 @@ final class Lexer implements IteratorAggregate
*/ */
public function progress(): Progress 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 * @var Closure
*/ */
private Closure $callback; private ?Closure $callback;
/** /**
* Whether the pointer was found. * Whether the pointer was found.
@ -51,7 +51,7 @@ final class Pointer implements Stringable
{ {
$this->referenceTokens = $this->toReferenceTokens(); $this->referenceTokens = $this->toReferenceTokens();
$this->depth = count($this->referenceTokens); $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 public function call(mixed $value, mixed $key): mixed
{ {
if ($this->callback === null) {
return $value;
}
return call_user_func($this->callback, $value, $key) ?? $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 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; private ?int $total = null;
/** /**
* Advance the progress * Set the current progress
* *
* @param int $current
* @return static * @return static
*/ */
public function advance(): static public function setCurrent(int $current): static
{ {
$this->current++; $this->current = $current;
return $this; return $this;
} }

View File

@ -146,8 +146,9 @@ final class State
{ {
$treeChanged = false; $treeChanged = false;
$shouldTrackTree = $this->pointer == '' || $this->tree->depth() < $this->pointer->depth(); $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()); $this->tree->traverseArray($this->pointer->referenceTokens());
$treeChanged = true; $treeChanged = true;
} }
@ -161,27 +162,16 @@ final class State
$this->pointer = $this->pointers->matchTree($this->tree); $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 $shouldBuffer = $this->tree->depth() >= 0
&& $this->pointer->matchesTree($this->tree) && $this->pointer->matchesTree($this->tree)
&& ($this->treeIsDeep() || ($token->isValue() && !$this->expectsKey)); && (($tokenIsValue && !$this->expectsKey) || $this->treeIsDeep());
if ($shouldBuffer) { if ($shouldBuffer) {
$this->buffer .= $token; $this->buffer .= $token;
$this->pointers->markAsFound($this->pointer); $this->pointers->markAsFound($this->pointer);
} }
$token->mutateState($this);
} }
/** /**

View File

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

View File

@ -92,16 +92,6 @@ final class Tree
$this->original[$this->depth] = $trimmedKey; $this->original[$this->depth] = $trimmedKey;
$this->wildcarded[$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->original, $this->depth + 1);
array_splice($this->wildcarded, $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->original[$this->depth] = is_int($index) ? $index + 1 : 0;
$this->wildcarded[$this->depth] = $referenceToken == '-' ? '-' : $this->original[$this->depth]; $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);
} }
/** /**