///|
fn SourceTokenizer::parse_attribute(
self : SourceTokenizer,
attrs : Map[String, String?],
) -> Unit {
let name_start = self.pos
if self.current() is Some('=') {
self.error_at("unexpected-equals-sign-before-attribute-name", self.pos)
ignore(self.advance_char())
}
while !self.is_eof() {
match self.current().unwrap() {
ch if ch.is_ascii_whitespace() || ch == '=' || ch == '/' || ch == '>' =>
break
'<' | '"' | '\'' => {
self.error_at("unexpected-character-in-attribute-name", self.pos)
ignore(self.advance_char())
}
_ => ignore(self.advance_char())
}
}
let (name, name_errors) = @impl.parse_lower_name_value_replacing_nulls(
self.input,
name_start,
self.pos,
)
for error in name_errors {
self.error_at(error.code, error.offset)
}
if name == "" {
ignore(self.advance_char())
return
}
let duplicate = attrs.contains(name)
if duplicate {
self.error_at("duplicate-attribute", name_start)
}
self.skip_ascii_whitespace()
if !(self.current() is Some('=')) {
if !duplicate {
attrs[name] = Some("")
}
return
}
ignore(self.advance_char())
self.skip_ascii_whitespace()
if self.current() is Some('>') {
self.error_at("missing-attribute-value", self.pos)
}
let value = self.parse_attribute_value()
if !duplicate {
attrs[name] = Some(value)
}
}
///|
fn SourceTokenizer::parse_attribute_value(self : SourceTokenizer) -> String {
match self.current() {
Some('"') => self.parse_quoted_attribute_value('"')
Some('\'') => self.parse_quoted_attribute_value('\'')
_ => {
let start = self.pos
while !self.is_eof() {
match self.current().unwrap() {
ch if ch.is_ascii_whitespace() || ch == '>' => break
'\u{0000}' => {
self.error_at("unexpected-null-character", self.pos)
ignore(self.advance_char())
}
'"' | '\'' | '<' | '=' | '`' => {
self.error_at(
"unexpected-character-in-unquoted-attribute-value",
self.pos,
)
ignore(self.advance_char())
}
_ => ignore(self.advance_char())
}
}
let view = self.input[start:self.pos]
let decoded = @impl.decode_attribute_value_with_errors(view)
for error in decoded.errors {
self.error_at(error.code, self.pos)
}
decoded.data.to_owned()
}
}
}
///|
fn SourceTokenizer::parse_quoted_attribute_value(
self : SourceTokenizer,
quote : Char,
) -> String {
ignore(self.advance_char())
let start = self.pos
while self.current() is Some(ch) && ch != quote {
if ch is '\u{0000}' {
self.error_at("unexpected-null-character", self.pos)
}
self.pos += ch.utf16_len()
}
let raw = self.input[start:self.pos]
if self.current() is Some(ch) && ch == quote {
ignore(self.advance_char())
match self.current() {
Some(ch) if !ch.is_ascii_whitespace() && ch != '>' && ch != '/' =>
self.error_at("missing-whitespace-between-attributes", self.pos)
_ => ()
}
}
let decoded = @impl.decode_attribute_value_with_errors(raw)
for error in decoded.errors {
self.error_at(error.code, self.pos)
}
decoded.data.to_owned()
}