Lex attributes in a much more robust way :)

This commit is contained in:
ConnorSkees 2020-01-26 19:07:24 -05:00
parent c7efbc7e05
commit 15edae53d6
3 changed files with 60 additions and 43 deletions

View File

@ -127,14 +127,17 @@ impl<'a> Lexer<'a> {
TokenKind::Keyword(Keyword::Important) TokenKind::Keyword(Keyword::Important)
} }
fn devour_whitespace(&mut self) { fn devour_whitespace(&mut self) -> bool {
let mut found_whitespace = false;
while let Some(c) = self.buf.peek() { while let Some(c) = self.buf.peek() {
if !is_whitespace(*c) { if !is_whitespace(*c) {
break; return found_whitespace;
} }
found_whitespace = true;
self.buf.next(); self.buf.next();
self.pos.next_char(); self.pos.next_char();
} }
found_whitespace
} }
fn lex_at_rule(&mut self) -> TokenKind { fn lex_at_rule(&mut self) -> TokenKind {
@ -300,21 +303,28 @@ impl<'a> Lexer<'a> {
let mut case_sensitive = CaseKind::Sensitive; let mut case_sensitive = CaseKind::Sensitive;
while let Some(c) = self.buf.peek() { while let Some(c) = self.buf.peek() {
if c == &']' && !c.is_whitespace() { if c == &']' || c.is_whitespace() {
break; break;
} }
if c == &'i' || c == &'I' {
if c == &'i' {
case_sensitive = CaseKind::InsensitiveLowercase;
} else if c == &'I' {
case_sensitive = CaseKind::InsensitiveCapital;
}
let tok = self let tok = self
.buf .buf
.next() .next()
.expect("this is impossible because we have already peeked"); .expect("this is impossible because we have already peeked");
self.pos.next_char(); self.pos.next_char();
value.push(tok);
}
if self.devour_whitespace() {
let n = self.buf.next();
match n {
Some('i') | Some('I') => {
let case_sensitive = match n {
Some('i') => CaseKind::InsensitiveLowercase,
Some('I') => CaseKind::InsensitiveCapital,
_ => unsafe { std::hint::unreachable_unchecked() },
};
self.pos.next_char();
self.devour_whitespace(); self.devour_whitespace();
match self.buf.next() { match self.buf.next() {
Some(']') => { Some(']') => {
@ -325,27 +335,29 @@ impl<'a> Lexer<'a> {
case_sensitive, case_sensitive,
}) })
} }
Some(val) => { Some(_) => todo!("modifier must be 1 character"),
self.pos.next_char(); None => todo!("unexpected EOF"),
value.push(tok);
value.push(val);
} }
None => todo!("expected something to come after "),
} }
continue; Some(']') => {
return TokenKind::Attribute(Attribute {
kind,
attr,
value,
case_sensitive,
})
} }
Some(c) => {
let tok = self value.push(' ');
.buf value.push(c.clone());
.next()
.expect("this is impossible because we have already peeked");
self.pos.next_char();
value.push(tok);
}
self.devour_whitespace(); self.devour_whitespace();
assert!(self.buf.next() == Some(']')); assert!(self.buf.next() == Some(']'));
}
None => todo!(),
}
} else {
assert!(self.buf.next() == Some(']'));
}
TokenKind::Attribute(Attribute { TokenKind::Attribute(Attribute {
kind, kind,

View File

@ -348,8 +348,8 @@ pub(crate) enum CaseKind {
impl Display for CaseKind { impl Display for CaseKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::InsensitiveCapital => write!(f, "I"), Self::InsensitiveCapital => write!(f, " I"),
Self::InsensitiveLowercase => write!(f, "i"), Self::InsensitiveLowercase => write!(f, " i"),
Self::Sensitive => write!(f, ""), Self::Sensitive => write!(f, ""),
} }
} }

View File

@ -143,10 +143,15 @@ mod test_selectors {
selector_attribute_i_in_attr, selector_attribute_i_in_attr,
"[atitr=val] {\n color: red;\n}\n" "[atitr=val] {\n color: red;\n}\n"
); );
// test!( test!(
// selector_attribute_i_in_val, selector_attribute_i_in_val,
// "[attr=vail] {\n color: red;\n}\n" "[attr=vail] {\n color: red;\n}\n"
// ); );
test!(
selector_attribute_whitespace,
"[attr *= val ] {\n color: red;\n}\n",
"[attr*=val] {\n color: red;\n}\n"
);
test!( test!(
selector_attribute_equals, selector_attribute_equals,
"[attr=val] {\n color: red;\n}\n" "[attr=val] {\n color: red;\n}\n"