diff options
Diffstat (limited to 'vendor/doctrine/lexer/src')
| -rw-r--r-- | vendor/doctrine/lexer/src/AbstractLexer.php | 328 | ||||
| -rw-r--r-- | vendor/doctrine/lexer/src/Token.php | 56 |
2 files changed, 384 insertions, 0 deletions
diff --git a/vendor/doctrine/lexer/src/AbstractLexer.php b/vendor/doctrine/lexer/src/AbstractLexer.php new file mode 100644 index 0000000..2436885 --- /dev/null +++ b/vendor/doctrine/lexer/src/AbstractLexer.php | |||
| @@ -0,0 +1,328 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\Common\Lexer; | ||
| 6 | |||
| 7 | use ReflectionClass; | ||
| 8 | use UnitEnum; | ||
| 9 | |||
| 10 | use function implode; | ||
| 11 | use function preg_split; | ||
| 12 | use function sprintf; | ||
| 13 | use function substr; | ||
| 14 | |||
| 15 | use const PREG_SPLIT_DELIM_CAPTURE; | ||
| 16 | use const PREG_SPLIT_NO_EMPTY; | ||
| 17 | use const PREG_SPLIT_OFFSET_CAPTURE; | ||
| 18 | |||
| 19 | /** | ||
| 20 | * Base class for writing simple lexers, i.e. for creating small DSLs. | ||
| 21 | * | ||
| 22 | * @template T of UnitEnum|string|int | ||
| 23 | * @template V of string|int | ||
| 24 | */ | ||
| 25 | abstract class AbstractLexer | ||
| 26 | { | ||
| 27 | /** | ||
| 28 | * Lexer original input string. | ||
| 29 | */ | ||
| 30 | private string $input; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * Array of scanned tokens. | ||
| 34 | * | ||
| 35 | * @var list<Token<T, V>> | ||
| 36 | */ | ||
| 37 | private array $tokens = []; | ||
| 38 | |||
| 39 | /** | ||
| 40 | * Current lexer position in input string. | ||
| 41 | */ | ||
| 42 | private int $position = 0; | ||
| 43 | |||
| 44 | /** | ||
| 45 | * Current peek of current lexer position. | ||
| 46 | */ | ||
| 47 | private int $peek = 0; | ||
| 48 | |||
| 49 | /** | ||
| 50 | * The next token in the input. | ||
| 51 | * | ||
| 52 | * @var Token<T, V>|null | ||
| 53 | */ | ||
| 54 | public Token|null $lookahead; | ||
| 55 | |||
| 56 | /** | ||
| 57 | * The last matched/seen token. | ||
| 58 | * | ||
| 59 | * @var Token<T, V>|null | ||
| 60 | */ | ||
| 61 | public Token|null $token; | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Composed regex for input parsing. | ||
| 65 | * | ||
| 66 | * @var non-empty-string|null | ||
| 67 | */ | ||
| 68 | private string|null $regex = null; | ||
| 69 | |||
| 70 | /** | ||
| 71 | * Sets the input data to be tokenized. | ||
| 72 | * | ||
| 73 | * The Lexer is immediately reset and the new input tokenized. | ||
| 74 | * Any unprocessed tokens from any previous input are lost. | ||
| 75 | * | ||
| 76 | * @param string $input The input to be tokenized. | ||
| 77 | * | ||
| 78 | * @return void | ||
| 79 | */ | ||
| 80 | public function setInput(string $input) | ||
| 81 | { | ||
| 82 | $this->input = $input; | ||
| 83 | $this->tokens = []; | ||
| 84 | |||
| 85 | $this->reset(); | ||
| 86 | $this->scan($input); | ||
| 87 | } | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Resets the lexer. | ||
| 91 | * | ||
| 92 | * @return void | ||
| 93 | */ | ||
| 94 | public function reset() | ||
| 95 | { | ||
| 96 | $this->lookahead = null; | ||
| 97 | $this->token = null; | ||
| 98 | $this->peek = 0; | ||
| 99 | $this->position = 0; | ||
| 100 | } | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Resets the peek pointer to 0. | ||
| 104 | * | ||
| 105 | * @return void | ||
| 106 | */ | ||
| 107 | public function resetPeek() | ||
| 108 | { | ||
| 109 | $this->peek = 0; | ||
| 110 | } | ||
| 111 | |||
| 112 | /** | ||
| 113 | * Resets the lexer position on the input to the given position. | ||
| 114 | * | ||
| 115 | * @param int $position Position to place the lexical scanner. | ||
| 116 | * | ||
| 117 | * @return void | ||
| 118 | */ | ||
| 119 | public function resetPosition(int $position = 0) | ||
| 120 | { | ||
| 121 | $this->position = $position; | ||
| 122 | } | ||
| 123 | |||
| 124 | /** | ||
| 125 | * Retrieve the original lexer's input until a given position. | ||
| 126 | * | ||
| 127 | * @return string | ||
| 128 | */ | ||
| 129 | public function getInputUntilPosition(int $position) | ||
| 130 | { | ||
| 131 | return substr($this->input, 0, $position); | ||
| 132 | } | ||
| 133 | |||
| 134 | /** | ||
| 135 | * Checks whether a given token matches the current lookahead. | ||
| 136 | * | ||
| 137 | * @param T $type | ||
| 138 | * | ||
| 139 | * @return bool | ||
| 140 | * | ||
| 141 | * @psalm-assert-if-true !=null $this->lookahead | ||
| 142 | */ | ||
| 143 | public function isNextToken(int|string|UnitEnum $type) | ||
| 144 | { | ||
| 145 | return $this->lookahead !== null && $this->lookahead->isA($type); | ||
| 146 | } | ||
| 147 | |||
| 148 | /** | ||
| 149 | * Checks whether any of the given tokens matches the current lookahead. | ||
| 150 | * | ||
| 151 | * @param list<T> $types | ||
| 152 | * | ||
| 153 | * @return bool | ||
| 154 | * | ||
| 155 | * @psalm-assert-if-true !=null $this->lookahead | ||
| 156 | */ | ||
| 157 | public function isNextTokenAny(array $types) | ||
| 158 | { | ||
| 159 | return $this->lookahead !== null && $this->lookahead->isA(...$types); | ||
| 160 | } | ||
| 161 | |||
| 162 | /** | ||
| 163 | * Moves to the next token in the input string. | ||
| 164 | * | ||
| 165 | * @return bool | ||
| 166 | * | ||
| 167 | * @psalm-assert-if-true !null $this->lookahead | ||
| 168 | */ | ||
| 169 | public function moveNext() | ||
| 170 | { | ||
| 171 | $this->peek = 0; | ||
| 172 | $this->token = $this->lookahead; | ||
| 173 | $this->lookahead = isset($this->tokens[$this->position]) | ||
| 174 | ? $this->tokens[$this->position++] : null; | ||
| 175 | |||
| 176 | return $this->lookahead !== null; | ||
| 177 | } | ||
| 178 | |||
| 179 | /** | ||
| 180 | * Tells the lexer to skip input tokens until it sees a token with the given value. | ||
| 181 | * | ||
| 182 | * @param T $type The token type to skip until. | ||
| 183 | * | ||
| 184 | * @return void | ||
| 185 | */ | ||
| 186 | public function skipUntil(int|string|UnitEnum $type) | ||
| 187 | { | ||
| 188 | while ($this->lookahead !== null && ! $this->lookahead->isA($type)) { | ||
| 189 | $this->moveNext(); | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | /** | ||
| 194 | * Checks if given value is identical to the given token. | ||
| 195 | * | ||
| 196 | * @return bool | ||
| 197 | */ | ||
| 198 | public function isA(string $value, int|string|UnitEnum $token) | ||
| 199 | { | ||
| 200 | return $this->getType($value) === $token; | ||
| 201 | } | ||
| 202 | |||
| 203 | /** | ||
| 204 | * Moves the lookahead token forward. | ||
| 205 | * | ||
| 206 | * @return Token<T, V>|null The next token or NULL if there are no more tokens ahead. | ||
| 207 | */ | ||
| 208 | public function peek() | ||
| 209 | { | ||
| 210 | if (isset($this->tokens[$this->position + $this->peek])) { | ||
| 211 | return $this->tokens[$this->position + $this->peek++]; | ||
| 212 | } | ||
| 213 | |||
| 214 | return null; | ||
| 215 | } | ||
| 216 | |||
| 217 | /** | ||
| 218 | * Peeks at the next token, returns it and immediately resets the peek. | ||
| 219 | * | ||
| 220 | * @return Token<T, V>|null The next token or NULL if there are no more tokens ahead. | ||
| 221 | */ | ||
| 222 | public function glimpse() | ||
| 223 | { | ||
| 224 | $peek = $this->peek(); | ||
| 225 | $this->peek = 0; | ||
| 226 | |||
| 227 | return $peek; | ||
| 228 | } | ||
| 229 | |||
| 230 | /** | ||
| 231 | * Scans the input string for tokens. | ||
| 232 | * | ||
| 233 | * @param string $input A query string. | ||
| 234 | * | ||
| 235 | * @return void | ||
| 236 | */ | ||
| 237 | protected function scan(string $input) | ||
| 238 | { | ||
| 239 | if (! isset($this->regex)) { | ||
| 240 | $this->regex = sprintf( | ||
| 241 | '/(%s)|%s/%s', | ||
| 242 | implode(')|(', $this->getCatchablePatterns()), | ||
| 243 | implode('|', $this->getNonCatchablePatterns()), | ||
| 244 | $this->getModifiers(), | ||
| 245 | ); | ||
| 246 | } | ||
| 247 | |||
| 248 | $flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE; | ||
| 249 | $matches = preg_split($this->regex, $input, -1, $flags); | ||
| 250 | |||
| 251 | if ($matches === false) { | ||
| 252 | // Work around https://bugs.php.net/78122 | ||
| 253 | $matches = [[$input, 0]]; | ||
| 254 | } | ||
| 255 | |||
| 256 | foreach ($matches as $match) { | ||
| 257 | // Must remain before 'value' assignment since it can change content | ||
| 258 | $firstMatch = $match[0]; | ||
| 259 | $type = $this->getType($firstMatch); | ||
| 260 | |||
| 261 | $this->tokens[] = new Token( | ||
| 262 | $firstMatch, | ||
| 263 | $type, | ||
| 264 | $match[1], | ||
| 265 | ); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | /** | ||
| 270 | * Gets the literal for a given token. | ||
| 271 | * | ||
| 272 | * @param T $token | ||
| 273 | * | ||
| 274 | * @return int|string | ||
| 275 | */ | ||
| 276 | public function getLiteral(int|string|UnitEnum $token) | ||
| 277 | { | ||
| 278 | if ($token instanceof UnitEnum) { | ||
| 279 | return $token::class . '::' . $token->name; | ||
| 280 | } | ||
| 281 | |||
| 282 | $className = static::class; | ||
| 283 | |||
| 284 | $reflClass = new ReflectionClass($className); | ||
| 285 | $constants = $reflClass->getConstants(); | ||
| 286 | |||
| 287 | foreach ($constants as $name => $value) { | ||
| 288 | if ($value === $token) { | ||
| 289 | return $className . '::' . $name; | ||
| 290 | } | ||
| 291 | } | ||
| 292 | |||
| 293 | return $token; | ||
| 294 | } | ||
| 295 | |||
| 296 | /** | ||
| 297 | * Regex modifiers | ||
| 298 | * | ||
| 299 | * @return string | ||
| 300 | */ | ||
| 301 | protected function getModifiers() | ||
| 302 | { | ||
| 303 | return 'iu'; | ||
| 304 | } | ||
| 305 | |||
| 306 | /** | ||
| 307 | * Lexical catchable patterns. | ||
| 308 | * | ||
| 309 | * @return string[] | ||
| 310 | */ | ||
| 311 | abstract protected function getCatchablePatterns(); | ||
| 312 | |||
| 313 | /** | ||
| 314 | * Lexical non-catchable patterns. | ||
| 315 | * | ||
| 316 | * @return string[] | ||
| 317 | */ | ||
| 318 | abstract protected function getNonCatchablePatterns(); | ||
| 319 | |||
| 320 | /** | ||
| 321 | * Retrieve token type. Also processes the token value if necessary. | ||
| 322 | * | ||
| 323 | * @return T|null | ||
| 324 | * | ||
| 325 | * @param-out V $value | ||
| 326 | */ | ||
| 327 | abstract protected function getType(string &$value); | ||
| 328 | } | ||
diff --git a/vendor/doctrine/lexer/src/Token.php b/vendor/doctrine/lexer/src/Token.php new file mode 100644 index 0000000..b6df694 --- /dev/null +++ b/vendor/doctrine/lexer/src/Token.php | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\Common\Lexer; | ||
| 6 | |||
| 7 | use UnitEnum; | ||
| 8 | |||
| 9 | use function in_array; | ||
| 10 | |||
| 11 | /** | ||
| 12 | * @template T of UnitEnum|string|int | ||
| 13 | * @template V of string|int | ||
| 14 | */ | ||
| 15 | final class Token | ||
| 16 | { | ||
| 17 | /** | ||
| 18 | * The string value of the token in the input string | ||
| 19 | * | ||
| 20 | * @readonly | ||
| 21 | * @var V | ||
| 22 | */ | ||
| 23 | public string|int $value; | ||
| 24 | |||
| 25 | /** | ||
| 26 | * The type of the token (identifier, numeric, string, input parameter, none) | ||
| 27 | * | ||
| 28 | * @readonly | ||
| 29 | * @var T|null | ||
| 30 | */ | ||
| 31 | public $type; | ||
| 32 | |||
| 33 | /** | ||
| 34 | * The position of the token in the input string | ||
| 35 | * | ||
| 36 | * @readonly | ||
| 37 | */ | ||
| 38 | public int $position; | ||
| 39 | |||
| 40 | /** | ||
| 41 | * @param V $value | ||
| 42 | * @param T|null $type | ||
| 43 | */ | ||
| 44 | public function __construct(string|int $value, $type, int $position) | ||
| 45 | { | ||
| 46 | $this->value = $value; | ||
| 47 | $this->type = $type; | ||
| 48 | $this->position = $position; | ||
| 49 | } | ||
| 50 | |||
| 51 | /** @param T ...$types */ | ||
| 52 | public function isA(...$types): bool | ||
| 53 | { | ||
| 54 | return in_array($this->type, $types, true); | ||
| 55 | } | ||
| 56 | } | ||
