namespace XFESystem.Browser;
/// <summary>A necessary positive key in the subject compound, never a substitute for selector matching.</summary>
internal static class BrowserSelectorKey
{
internal static (char Kind, string Value)? Find(string selector)
{
// Escapes, namespaces and non-ASCII identifiers stay on the general
// path. In particular, a prefix of an escaped/Unicode name is no key.
foreach (char c in selector) if (c == '\\' || c == '|' || c > 127) return null;
int start = 0, brackets = 0, parentheses = 0;
char quote = '\0';
for (int i = 0; i < selector.Length; i++)
{
char c = selector[i];
if (quote != '\0') { if (c == quote) quote = '\0'; continue; }
if (c is '\'' or '"') { quote = c; continue; }
if (c == '[') brackets++;
else if (c == ']') brackets--;
else if (brackets == 0 && c == '(') parentheses++;
else if (brackets == 0 && c == ')') parentheses--;
else if (brackets == 0 && parentheses == 0 && (char.IsWhiteSpace(c) || c is '>' or '+' or '~')) start = i + 1;
}
if (brackets != 0 || parentheses != 0 || quote != '\0') return null;
string? tag = null, name = null;
if (start < selector.Length && (char.IsAsciiLetter(selector[start]) || selector[start] is '_' or '-'))
{
int end = start;
while (end < selector.Length && Identifier(selector[end])) end++;
tag = selector[start..end];
}
for (int i = start; i < selector.Length; i++)
{
char c = selector[i];
if (quote != '\0') { if (c == quote) quote = '\0'; continue; }
if (c is '\'' or '"') { quote = c; continue; }
if (c == '[') brackets++;
else if (c == ']') brackets--;
else if (brackets == 0 && c == '(') parentheses++;
else if (brackets == 0 && c == ')') parentheses--;
else if (brackets == 0 && parentheses == 0 && c is '#' or '.')
{
int end = i + 1;
while (end < selector.Length && Identifier(selector[end])) end++;
if (end == i + 1) return null;
if (c == '#') return ('#', selector[(i + 1)..end]);
name ??= selector[(i + 1)..end];
i = end - 1;
}
}
return name is not null ? ('.', name) : tag is not null ? ('t', tag) : null;
}
private static bool Identifier(char c) => char.IsAsciiLetterOrDigit(c) || c is '_' or '-';
}
namespace XFESystem.Browser;
/// <summary>A necessary positive key in the subject compound, never a substitute for selector matching.</summary>
internal static class BrowserSelectorKey
{
internal static (char Kind, string Value)? Find(string selector)
{
// Escapes, namespaces and non-ASCII identifiers stay on the general
// path. In particular, a prefix of an escaped/Unicode name is no key.
foreach (char c in selector) if (c == '\\' || c == '|' || c > 127) return null;
int start = 0, brackets = 0, parentheses = 0;
char quote = '\0';
for (int i = 0; i < selector.Length; i++)
{
char c = selector[i];
if (quote != '\0') { if (c == quote) quote = '\0'; continue; }
if (c is '\'' or '"') { quote = c; continue; }
if (c == '[') brackets++;
else if (c == ']') brackets--;
else if (brackets == 0 && c == '(') parentheses++;
else if (brackets == 0 && c == ')') parentheses--;
else if (brackets == 0 && parentheses == 0 && (char.IsWhiteSpace(c) || c is '>' or '+' or '~')) start = i + 1;
}
if (brackets != 0 || parentheses != 0 || quote != '\0') return null;
string? tag = null, name = null;
if (start < selector.Length && (char.IsAsciiLetter(selector[start]) || selector[start] is '_' or '-'))
{
int end = start;
while (end < selector.Length && Identifier(selector[end])) end++;
tag = selector[start..end];
}
for (int i = start; i < selector.Length; i++)
{
char c = selector[i];
if (quote != '\0') { if (c == quote) quote = '\0'; continue; }
if (c is '\'' or '"') { quote = c; continue; }
if (c == '[') brackets++;
else if (c == ']') brackets--;
else if (brackets == 0 && c == '(') parentheses++;
else if (brackets == 0 && c == ')') parentheses--;
else if (brackets == 0 && parentheses == 0 && c is '#' or '.')
{
int end = i + 1;
while (end < selector.Length && Identifier(selector[end])) end++;
if (end == i + 1) return null;
if (c == '#') return ('#', selector[(i + 1)..end]);
name ??= selector[(i + 1)..end];
i = end - 1;
}
}
return name is not null ? ('.', name) : tag is not null ? ('t', tag) : null;
}
private static bool Identifier(char c) => char.IsAsciiLetterOrDigit(c) || c is '_' or '-';
}