*/ final class GenericNameParser implements Parser { private const IDENTIFIER_PATTERN = <<<'PATTERN' /\G (?: "(?[^"]*(?:""[^"]*)*)" # ANSI SQL double-quoted | `(?[^`]*(?:``[^`]*)*)` # MySQL-style backtick-quoted | \[(?[^]]*(?:]][^]]*)*)] # SQL Server-style square-bracket-quoted | (?[^\s."`\[\]]+) # Unquoted ) /x PATTERN; public function parse(string $input): GenericName { $offset = 0; $identifiers = []; $length = strlen($input); while (true) { if ($offset >= $length) { throw ExpectedNextIdentifier::new(); } if (preg_match(self::IDENTIFIER_PATTERN, $input, $matches, 0, $offset) === 0) { throw UnableToParseIdentifier::new($offset); } if (isset($matches['ansi']) && strlen($matches['ansi']) > 0) { $identifier = Identifier::quoted(str_replace('""', '"', $matches['ansi'])); } elseif (isset($matches['mysql']) && strlen($matches['mysql']) > 0) { $identifier = Identifier::quoted(str_replace('``', '`', $matches['mysql'])); } elseif (isset($matches['sqlserver']) && strlen($matches['sqlserver']) > 0) { $identifier = Identifier::quoted(str_replace(']]', ']', $matches['sqlserver'])); } else { assert(isset($matches['unquoted']) && strlen($matches['unquoted']) > 0); $identifier = Identifier::unquoted($matches['unquoted']); } $identifiers[] = $identifier; $offset += strlen($matches[0]); if ($offset >= $length) { break; } $character = $input[$offset]; if ($character !== '.') { throw ExpectedDot::new($offset, $character); } $offset++; } assert(count($identifiers) > 0); return new GenericName(...$identifiers); } }