use crate::{asm_core, stream::Stream};
use super::Selector;
pub struct Parser<'a> {
stream: Stream<'a, u8>,
}
impl<'a> Parser<'a> {
pub fn new(input: &'a [u8]) -> Self {
Self {
stream: Stream::new(input),
}
}
fn skip_whitespaces(&mut self) -> bool {
let skipped = asm_core::count_spaces(&self.stream.data()[self.stream.idx..]);
self.stream.idx += skipped;
skipped != 0
}
fn read_identifier(&mut self) -> &'a [u8] {
let start = self.stream.idx;
let len = asm_core::count_ident(&self.stream.data()[start..]);
self.stream.advance_by(len);
self.stream.slice(start, self.stream.idx)
}
fn parse_combinator(&mut self, left: Selector<'a>) -> Option<Selector<'a>> {
let has_whitespaces = self.skip_whitespaces();
let tok = if let Some(tok) = self.stream.current_cpy() {
tok
} else {
return Some(left);
};
let combinator = match asm_core::selector_combinator_kind(tok, has_whitespaces) {
1 => {
self.stream.advance();
let right = self.selector()?;
Selector::Or(Box::new(left), Box::new(right))
}
2 => {
self.stream.advance();
let right = self.selector()?;
Selector::Parent(Box::new(left), Box::new(right))
}
3 => {
let right = self.selector()?;
Selector::Descendant(Box::new(left), Box::new(right))
}
4 => {
let right = self.selector()?;
Selector::And(Box::new(left), Box::new(right))
}
_ => unreachable!(),
};
Some(combinator)
}
fn parse_attribute<const MAX_SELECTOR_NODES: usize>(
&mut self,
) -> Option<Selector<'a, MAX_SELECTOR_NODES>> {
let attribute = self.read_identifier();
let op = self
.stream
.current_cpy()
.map(asm_core::selector_attr_op_kind)?;
let ty = match op {
1 => {
self.stream.advance();
Selector::Attribute(attribute)
}
2 => {
self.stream.advance();
let quote = self.stream.current_cpy().filter(|&c| asm_core::is_quote(c));
if quote.is_some() {
self.stream.advance();
}
let value = self.read_identifier();
if let Some(quote) = quote {
self.stream.expect_and_skip(quote)?;
}
self.stream.expect_and_skip(b']')?;
Selector::AttributeValue(attribute, value)
}
c @ (3..=6) => {
self.stream.advance();
self.stream.expect_and_skip(b'=')?;
let quote = self.stream.current_cpy().filter(|&c| asm_core::is_quote(c));
if quote.is_some() {
self.stream.advance();
}
let value = self.read_identifier();
if let Some(quote) = quote {
self.stream.expect_and_skip(quote)?;
}
self.stream.expect_and_skip(b']')?;
match c {
3 => Selector::AttributeValueWhitespacedContains(attribute, value),
4 => Selector::AttributeValueStartsWith(attribute, value),
5 => Selector::AttributeValueEndsWith(attribute, value),
6 => Selector::AttributeValueSubstring(attribute, value),
_ => unreachable!(),
}
}
_ => return None,
};
Some(ty)
}
pub fn selector(&mut self) -> Option<Selector<'a>> {
self.skip_whitespaces();
let tok = self.stream.current_cpy()?;
let left = match asm_core::selector_token_kind(tok) {
1 => {
self.stream.advance();
let id = self.read_identifier();
Selector::Id(id)
}
2 => {
self.stream.advance();
let class = self.read_identifier();
Selector::Class(class)
}
3 => {
self.stream.advance();
Selector::All
}
4 => {
self.stream.advance();
self.parse_attribute::<0>()?
}
5 => {
let tag = self.read_identifier();
Selector::Tag(tag)
}
_ => return None,
};
self.parse_combinator(left)
}
}