Skip to content

Commit

Permalink
attr: mv event push to Parser
Browse files Browse the repository at this point in the history
allow reuse from outside Attributes::parse
  • Loading branch information
hellux committed Mar 20, 2023
1 parent 79a4ce1 commit 5e3bdf2
Showing 1 changed file with 105 additions and 75 deletions.
180 changes: 105 additions & 75 deletions src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,34 +119,14 @@ impl<'s> Attributes<'s> {
}

pub(crate) fn parse(&mut self, input: &'s str) -> bool {
let mut p = Parser::new();
let mut span_key = None;
for c in input.chars() {
if let Some((ty, sp)) = p.step(c) {
match ty {
Element::Class => self.insert("class", sp.of(input).into()),
Element::Identifier => self.insert("id", sp.of(input).into()),
Element::Key => span_key = Some(sp),
Element::Value { continuation } => {
if continuation {
dbg!(&self);
self.0.as_mut().unwrap().last_mut().unwrap().1 = format!(
"{} {}",
self.0.as_ref().unwrap().last().unwrap().1,
sp.of(input),
)
.into();
} else {
self.insert(span_key.take().unwrap().of(input), sp.of(input).into())
}
}
}
}
if matches!(p.state, State::Done | State::Invalid) {
break;
let mut parser = Parser::new(self, input);
loop {
match parser.step() {
StepResult::Done => return true,
StepResult::Invalid | StepResult::More => return false,
StepResult::Valid => {}
}
}
matches!(p.state, State::Done)
}

/// Combine all attributes from both objects, prioritizing self on conflicts.
Expand Down Expand Up @@ -218,6 +198,105 @@ impl<'s> FromIterator<(&'s str, &'s str)> for Attributes<'s> {
}
}

pub enum StepResult {
/// Attributes are valid and completed.
Done,
/// Attributes are invalid.
Invalid,
/// Attributes are valid so far.
Valid,
/// Attributes are valid so far, more input is needed.
More,
}

pub struct Parser<'a, 's> {
input: &'s str,
chars: std::str::Chars<'s>,
attrs: &'a mut Attributes<'s>,
span_key: Option<Span>,

pos: usize,
pos_prev: usize,
state: State,
}

impl<'a, 's> Parser<'a, 's> {
pub fn new(attrs: &'a mut Attributes<'s>, input: &'s str) -> Self {
Self {
input,
chars: input.chars(),
attrs,
span_key: None,
pos: 0,
pos_prev: 0,
state: State::Start,
}
}

pub fn step(&mut self) -> StepResult {
self.chars.next().map_or(StepResult::More, |c| {
use State::*;

let state_next = self.state.step(c);
let st = std::mem::replace(&mut self.state, state_next);

let ev = if st != self.state
&& !matches!((st, self.state), (ValueEscape, _) | (_, ValueEscape))
{
let span = Span::new(self.pos_prev, self.pos);
self.pos_prev = self.pos;
match st {
ClassFirst | IdentifierFirst | ValueFirst | ValueNewline | Comment | Start
| Whitespace | Done | Invalid | ValueEscape => None,
Key => Some((Element::Key, span)),
Class => Some((Element::Class, span)),
Identifier => Some((Element::Identifier, span)),
Value | ValueQuoted | ValueContinued => Some((
Element::Value {
continuation: matches!(st, ValueContinued),
},
span.skip(usize::from(matches!(st, ValueQuoted))),
)),
}
} else {
None
};

self.pos += c.len_utf8();

match ev {
Some((Element::Class, sp)) => self.attrs.insert("class", sp.of(self.input).into()),
Some((Element::Identifier, sp)) => {
self.attrs.insert("id", sp.of(self.input).into())
}
Some((Element::Key, sp)) => self.span_key = Some(sp),
Some((Element::Value { continuation }, sp)) => {
if continuation {
self.attrs.0.as_mut().unwrap().last_mut().unwrap().1 = format!(
"{} {}",
self.attrs.0.as_ref().unwrap().last().unwrap().1,
sp.of(self.input),
)
.into();
} else {
self.attrs.insert(
self.span_key.take().unwrap().of(self.input),
sp.of(self.input).into(),
)
}
}
None => {}
}

match self.state {
State::Done => StepResult::Done,
State::Invalid => StepResult::Invalid,
_ => StepResult::Valid,
}
})
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum State {
Start,
Expand Down Expand Up @@ -280,55 +359,6 @@ impl State {
}
}

struct Parser {
pos: usize,
pos_prev: usize,
state: State,
}

impl Parser {
fn new() -> Self {
Parser {
pos: 0,
pos_prev: 0,
state: State::Start,
}
}

fn step(&mut self, c: char) -> Option<(Element, Span)> {
use State::*;

let state_next = self.state.step(c);
let st = std::mem::replace(&mut self.state, state_next);

let elem = if st != self.state
&& !matches!((st, self.state), (ValueEscape, _) | (_, ValueEscape))
{
let span = Span::new(self.pos_prev, self.pos);
self.pos_prev = self.pos;
match st {
ClassFirst | IdentifierFirst | ValueFirst | ValueNewline | Comment | Start
| Whitespace | Done | Invalid | ValueEscape => None,
Key => Some((Element::Key, span)),
Class => Some((Element::Class, span)),
Identifier => Some((Element::Identifier, span)),
Value | ValueQuoted | ValueContinued => Some((
Element::Value {
continuation: matches!(st, ValueContinued),
},
span.skip(usize::from(matches!(st, ValueQuoted))),
)),
}
} else {
None
};

self.pos += c.len_utf8();

elem
}
}

pub fn is_name_start(c: char) -> bool {
c.is_ascii_alphanumeric() || matches!(c, '_' | ':')
}
Expand Down

0 comments on commit 5e3bdf2

Please sign in to comment.