///|
fn Parser::parse_string_group(
self : Parser,
optional : Bool,
) -> Token? raise ParseFailure {
match self.gullet.scan_argument(optional) {
None => None
Some(arg_token) => {
let builder = StringBuilder()
for ;; {
let token = self.fetch()
if token.text == "EOF" {
self.consume()
arg_token.text = builder.to_string()
break Some(arg_token)
}
builder.write_string(token.text)
self.consume()
}
}
}
}
///|
fn normalized_color(text : String) -> String? {
if text.length() > 0 && text[0] == '#' {
let digits = text.unsafe_substring(start=1, end=text.length())
if (
digits.length() == 3 ||
digits.length() == 4 ||
digits.length() == 6 ||
digits.length() == 8
) &&
digits.all(Char::is_ascii_hexdigit) {
Some(text)
} else {
None
}
} else if text.length() == 6 && text.all(Char::is_ascii_hexdigit) {
Some("#\{text}")
} else if !text.is_empty() && text.all(Char::is_ascii_alphabetic) {
Some(text)
} else {
None
}
}
///|
fn Parser::parse_color_group(
self : Parser,
optional : Bool,
) -> ParseNode? raise ParseFailure {
self
.parse_string_group(optional)
.map(token => {
guard normalized_color(token.text) is Some(color) else {
raise InvalidArgument(
message="Invalid color: '\{token.text}'",
loc=token.loc,
)
}
ColorToken(mode=self.mode, color~)
})
}