Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions benchmarks/LexerBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,24 @@ class LexerBench
{
private Source $introQuery;

private Source $deeplyIndentedQuery;

public function setUp(): void
{
$this->introQuery = new Source(Introspection::getIntrospectionQuery());
$this->deeplyIndentedQuery = new Source($this->buildDeeplyIndentedQuery());
}

/** Query dominated by long runs of leading-tab indentation, to weigh the whitespace-skipping path. */
private function buildDeeplyIndentedQuery(): string
{
$indent = str_repeat("\t", 16);
$fields = [];
for ($i = 0; $i < 200; ++$i) {
$fields[] = "{$indent}field{$i}(arg: \"{$i}\")";
}

return "query DeepIndent {\n" . implode("\n", $fields) . "\n}\n";
}

/**
Expand All @@ -36,4 +51,20 @@ public function benchIntrospectionQuery(): void
$token = $lexer->advance();
} while ($token->kind !== Token::EOF);
}

/**
* @Warmup(2)
*
* @Revs(100)
*
* @Iterations(5)
*/
public function benchDeeplyIndentedQuery(): void
{
$lexer = new Lexer($this->deeplyIndentedQuery);

do {
$token = $lexer->advance();
} while ($token->kind !== Token::EOF);
}
}
17 changes: 14 additions & 3 deletions src/Language/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class Lexer
*/
private const STRING_STOP_BYTES = "\"\\\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";

/** Ignored single-byte characters that never start or end a line, so runs of them can be skipped in bulk. */
private const HORIZONTAL_WHITESPACE_BYTES = "\t ,";

public Source $source;

/** @phpstan-var ParserOptions */
Expand Down Expand Up @@ -640,12 +643,20 @@ private function assertValidBlockStringCharacterCode(int $code, int $position):
*/
private function positionAfterWhitespace(): void
{
$body = $this->source->body;

while ($this->position < $this->source->length) {
// Tab, space, and comma runs carry no line/column bookkeeping, so
// an entire run can be consumed in one native scan instead of one
// readChar()/moveStringCursor() call per character.
$run = strspn($body, self::HORIZONTAL_WHITESPACE_BYTES, $this->byteStreamPosition);
if ($run > 0) {
$this->moveStringCursor($run, $run);
}

[, $code, $bytes] = $this->readChar();

// Skip whitespace
// tab | space | comma | BOM
if (in_array($code, [9, 32, 44, 0xFEFF], true)) {
if ($code === 0xFEFF) { // BOM
$this->moveStringCursor(1, $bytes);
} elseif ($code === 10) { // new line
$this->moveStringCursor(1, $bytes);
Expand Down
Loading