%PDF- %PDF-
Direktori : /home/q/g/b/qgbqkvz/www/wp-content/plugins/wp-scss/scssphp/src/Parser/ |
Current File : /home/q/g/b/qgbqkvz/www/wp-content/plugins/wp-scss/scssphp/src/Parser/StringScanner.php |
<?php /** * SCSSPHP * * @copyright 2012-2020 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * * @link http://scssphp.github.io/scssphp */ namespace ScssPhp\ScssPhp\Parser; use ScssPhp\ScssPhp\SourceSpan\FileSpan; use ScssPhp\ScssPhp\SourceSpan\SourceFile; /** * A port of Dart's string_scanner package to be used by the parser. * * The scanner only supports UTF-8 strings. * * Differences with Dart: * - reading a character is reading a byte, not an UTF-16 code unit (as PHP strings are not UTF-16). The * {@see readUtf8Char} method can be used to consume an UTF-8 char. * - characters are represented as a single-char string, not as an integer with their UTF-16 char code * - offsets are based on bytes, not on UTF-16 code units. In practice, parsing Sass generally needs * to peak following chars only when already knowing that the current char is an ASCII one, which * makes this safe. When this assumption does not hold anymore, a different logic should be used * - as strings and regexp cannot be used interchangeably in PHP (in Dart, regexps are a different * object, and both String and Regexp are implementing a Pattern interface for matching), the scanner * exposes supports only strings in scan() and expect(). Should we need support for regexps, a * separate method will be added. * * @internal */ class StringScanner { /** * @var string * @readonly */ private $string; /** * @var int */ private $position = 0; /** * @var SourceFile * @readonly */ private $sourceFile; public function __construct(string $content, ?string $sourceUrl = null) { $this->string = $content; $this->sourceFile = new SourceFile($content, $sourceUrl); } public function getString(): string { return $this->string; } public function getPosition(): int { return $this->position; } public function setPosition(int $position): void { $this->position = $position; } public function spanFrom(int $start, ?int $end = null): FileSpan { $end = $end ?? $this->position; return $this->sourceFile->span($start, $end); } /** * Returns an empty span at the current location. */ public function getEmptySpan(): FileSpan { return $this->sourceFile->span($this->position, $this->position); } public function isDone(): bool { return $this->position === \strlen($this->string); } /** * @throws FormatException if the end of the string is reached * * @phpstan-impure */ public function readChar(): string { if ($this->position === \strlen($this->string)) { $this->fail('more input'); } return $this->string[$this->position++]; } /** * @throws FormatException if the end of the string is reached * * @phpstan-impure */ public function readUtf8Char(): string { if ($this->position === \strlen($this->string)) { $this->fail('more input'); } if (\ord($this->string[$this->position]) < 0x80) { return $this->string[$this->position++]; } if (!preg_match('/./usA', $this->string, $m, 0, $this->position)) { $this->fail('utf-8 char'); } $this->position += \strlen($m[0]); return $m[0]; } /** * Consumes the next character in the string if it the provided character. * * @param string $char * * @return bool Whether the character was consumed. * * @phpstan-impure */ public function scanChar(string $char): bool { if ($this->position === \strlen($this->string)) { return false; } if ($this->string[$this->position] !== $char) { return false; } ++$this->position; return true; } /** * Consumes the provided string if it appears at the current position. * * @param string $string * * @return bool Whether the string was consumed. * * @phpstan-impure */ public function scan(string $string): bool { if (!$this->matches($string)) { return false; } $this->position += \strlen($string); return true; } /** * Returns whether or not the provided string appears at the current position. * * This doesn't move the scan pointer forward. */ public function matches(string $string): bool { if ($this->position - 1 + \strlen($string) >= \strlen($this->string)) { return false; } return substr($this->string, $this->position, \strlen($string)) === $string; } /** * If the next character in the string is $character, consumes it. * * If $character could not be consumed, throws an exception * describing the position of the failure. $name is used in this error as * the expected name of the character being matched; if it's `null`, the * character itself is used instead. * * @param string $character * @param string|null $name * * @return void * * @throws FormatException * * @phpstan-impure */ public function expectChar(string $character, ?string $name = null): void { if ($this->scanChar($character)) { return; } if ($name === null) { $name = '"' . $character . '"'; } $this->fail($name); } /** * @param string $string * * @return void * * @throws FormatException * * @phpstan-impure */ public function expect(string $string): void { if ($this->scan($string)) { return; } $this->fail('"' . $string . '"'); } /** * @throws FormatException */ public function expectDone(): void { if ($this->isDone()) { return; } $this->fail('no more input'); } /** * Returns the character at the given offset of the current position. * * The offset can be negative to peek already seen characters. * Returns null if the offset goes out of range. * This does not affect the position or the last match. * * @param int $offset * * @return string|null */ public function peekChar(int $offset = 0): ?string { $pos = $this->position + $offset; if ($pos < 0 || $pos >= \strlen($this->string)) { return null; } return $this->string[$pos]; } /** * Returns the substring of the string between $start and $end (excluded). * * $end defaults to the current position. * * @param int $start * @param int|null $end * * @return string */ public function substring(int $start, ?int $end = null): string { if ($end === null) { $end = $this->position; } if ($end < $start) { return ''; } return substr($this->string, $start, $end - $start); } /** * The scanner's current (zero-based) line number. * * @return int */ public function getLine(): int { return $this->sourceFile->getLine($this->position); } /** * The scanner's current (zero-based) column number. * * @return int */ public function getColumn(): int { return $this->sourceFile->getColumn($this->position); } /** * @param string $message * @param int|null $position * @param int|null $length * * @throws FormatException * * @return never-returns */ public function error(string $message, ?int $position = null, ?int $length = null) { $position = $position ?? $this->position; $length = $length ?? 0; $span = $this->sourceFile->span($position, $position + $length); throw new FormatException($message, $span); } /** * @param string $message * * @throws FormatException * * @return never-returns */ private function fail(string $message) { $this->error("expected $message."); } }