css_parse/
parser.rs

1use crate::{
2	Cursor, Feature, Kind, KindSet, ParserCheckpoint, ParserReturn, Result, SourceOffset, Span, ToCursors, diagnostics,
3	traits::{Parse, Peek},
4};
5use bitmask_enum::bitmask;
6use bumpalo::Bump;
7use css_lexer::{Lexer, SourceCursor};
8use miette::Error;
9use std::mem::take;
10
11#[derive(Debug)]
12pub struct Parser<'a> {
13	pub(crate) source_text: &'a str,
14
15	pub(crate) lexer: Lexer<'a>,
16
17	#[allow(dead_code)]
18	pub(crate) features: Feature,
19
20	pub(crate) errors: Vec<Error>,
21
22	pub(crate) trivia: Vec<Cursor>,
23
24	pub(crate) state: State,
25
26	pub(crate) bump: &'a Bump,
27
28	skip: KindSet,
29
30	stop: KindSet,
31
32	#[cfg(debug_assertions)]
33	pub(crate) last_cursor: Option<Cursor>,
34}
35
36#[bitmask(u8)]
37#[bitmask_config(vec_debug)]
38#[derive(Default)]
39pub enum State {
40	Nested = 0b0000_0001,
41}
42
43impl<'a> Parser<'a> {
44	/// Create a new parser
45	pub fn new(bump: &'a Bump, source_text: &'a str) -> Self {
46		Self::new_with_features(bump, source_text, Feature::none())
47	}
48
49	pub fn with_features(mut self, features: Feature) -> Self {
50		self.features = features;
51		self
52	}
53
54	pub fn new_with_features(bump: &'a Bump, source_text: &'a str, features: Feature) -> Self {
55		Self {
56			source_text,
57			lexer: Lexer::new_with_features(source_text, features.into()),
58			features,
59			errors: vec![],
60			trivia: vec![],
61			state: State::none(),
62			skip: KindSet::TRIVIA,
63			stop: KindSet::NONE,
64			bump,
65			#[cfg(debug_assertions)]
66			last_cursor: None,
67		}
68	}
69
70	#[inline]
71	pub fn bump(&self) -> &'a Bump {
72		self.bump
73	}
74
75	#[inline]
76	pub fn enabled(&self, other: Feature) -> bool {
77		self.features.contains(other)
78	}
79
80	#[inline]
81	pub fn is(&self, state: State) -> bool {
82		self.state.contains(state)
83	}
84
85	#[inline]
86	pub fn set_state(&mut self, state: State) -> State {
87		let old = self.state;
88		self.state = state;
89		old
90	}
91
92	#[inline]
93	pub fn set_skip(&mut self, skip: KindSet) -> KindSet {
94		let old = self.skip;
95		self.skip = skip;
96		old
97	}
98
99	#[inline]
100	pub fn set_stop(&mut self, stop: KindSet) -> KindSet {
101		let old = self.stop;
102		self.stop = stop;
103		old
104	}
105
106	pub fn parse_entirely<T: Parse<'a> + ToCursors>(&mut self) -> ParserReturn<'a, T> {
107		let output = match T::parse(self) {
108			Ok(output) => Some(output),
109			Err(error) => {
110				self.errors.push(error);
111				None
112			}
113		};
114		if !self.at_end() && self.peek_next() != Kind::Eof {
115			let start = self.offset();
116			loop {
117				let cursor = self.next();
118				self.trivia.push(cursor);
119				if cursor == Kind::Eof {
120					break;
121				}
122			}
123			self.errors.push(diagnostics::ExpectedEnd(Span::new(start, self.offset())).into());
124		}
125		ParserReturn::new(output, self.source_text, take(&mut self.errors), take(&mut self.trivia))
126	}
127
128	pub fn parse<T: Parse<'a>>(&mut self) -> Result<T> {
129		T::parse(self)
130	}
131
132	pub fn peek<T: Peek<'a>>(&self) -> bool {
133		T::peek(self, self.peek_next())
134	}
135
136	pub fn parse_if_peek<T: Peek<'a> + Parse<'a>>(&mut self) -> Result<Option<T>> {
137		if T::peek(self, self.peek_next()) { T::parse(self).map(Some) } else { Ok(None) }
138	}
139
140	pub fn try_parse<T: Parse<'a>>(&mut self) -> Result<T> {
141		T::try_parse(self)
142	}
143
144	pub fn try_parse_if_peek<T: Peek<'a> + Parse<'a>>(&mut self) -> Result<Option<T>> {
145		if T::peek(self, self.peek_next()) { T::try_parse(self).map(Some) } else { Ok(None) }
146	}
147
148	#[inline]
149	pub fn parse_raw_str(&self, c: Cursor) -> &'a str {
150		c.str_slice(self.lexer.source())
151	}
152
153	#[inline]
154	pub fn parse_str(&self, c: Cursor) -> &str {
155		c.parse_str(self.lexer.source(), self.bump)
156	}
157
158	#[inline]
159	pub fn parse_str_lower(&self, c: Cursor) -> &str {
160		c.parse_str_lower(self.lexer.source(), self.bump)
161	}
162
163	#[inline]
164	pub fn eq_ignore_ascii_case(&self, c: Cursor, other: &'static str) -> bool {
165		c.eq_ignore_ascii_case(self.lexer.source(), other)
166	}
167
168	#[inline(always)]
169	pub fn offset(&self) -> SourceOffset {
170		self.lexer.offset()
171	}
172
173	#[inline(always)]
174	pub fn at_end(&self) -> bool {
175		self.lexer.at_end()
176	}
177
178	pub fn rewind(&mut self, checkpoint: ParserCheckpoint) {
179		let ParserCheckpoint { cursor, errors_pos, trivia_pos } = checkpoint;
180		self.lexer.rewind(cursor);
181		self.errors.truncate(errors_pos as usize);
182		self.trivia.truncate(trivia_pos as usize);
183		#[cfg(debug_assertions)]
184		{
185			self.last_cursor = None;
186		}
187	}
188
189	#[inline]
190	pub fn checkpoint(&self) -> ParserCheckpoint {
191		ParserCheckpoint {
192			cursor: self.lexer.checkpoint(),
193			errors_pos: self.errors.len() as u8,
194			trivia_pos: self.trivia.len() as u16,
195		}
196	}
197
198	#[inline]
199	pub fn next_is_stop(&self) -> bool {
200		let mut lexer = self.lexer.clone();
201		loop {
202			let t = lexer.advance();
203			if t.kind() != self.skip {
204				return t.kind() == self.stop;
205			}
206		}
207	}
208
209	#[inline]
210	pub(crate) fn peek_next(&self) -> Cursor {
211		let mut lexer = self.lexer.clone();
212		loop {
213			let offset = lexer.offset();
214			let t = lexer.advance();
215			if t == Kind::Eof || t != self.skip {
216				return t.with_cursor(offset);
217			}
218		}
219	}
220
221	#[inline]
222	pub(crate) fn peek_next_including_whitespace(&self) -> Cursor {
223		let mut lexer = self.lexer.clone();
224		loop {
225			let offset = lexer.offset();
226			let t = lexer.advance();
227			if t == Kind::Eof || t == Kind::Whitespace || t != self.skip {
228				return t.with_cursor(offset);
229			}
230		}
231	}
232
233	pub fn peek_n(&self, n: u8) -> Cursor {
234		let mut lex = self.lexer.clone();
235		let mut remaining = n;
236		loop {
237			let offset = lex.offset();
238			let t = lex.advance();
239			if t == Kind::Eof {
240				return t.with_cursor(offset);
241			}
242			if t != self.skip {
243				remaining -= 1;
244				if remaining == 0 {
245					return t.with_cursor(offset);
246				}
247			}
248		}
249	}
250
251	pub fn to_source_cursor(&self, cursor: Cursor) -> SourceCursor<'a> {
252		SourceCursor::from(cursor, cursor.str_slice(self.source_text))
253	}
254
255	pub fn consume_trivia(&mut self) {
256		loop {
257			let offset = self.lexer.offset();
258			let c = self.lexer.advance().with_cursor(offset);
259			if c == Kind::Eof {
260				return;
261			} else if c == self.skip {
262				self.trivia.push(c)
263			} else {
264				self.lexer.rewind(c);
265				return;
266			}
267		}
268	}
269
270	#[allow(clippy::should_implement_trait)]
271	pub fn next(&mut self) -> Cursor {
272		let mut c;
273		let mut offset;
274		loop {
275			offset = self.offset();
276			c = self.lexer.advance().with_cursor(offset);
277			if c == Kind::Eof || c != self.skip {
278				break;
279			}
280			self.trivia.push(c)
281		}
282
283		#[cfg(debug_assertions)]
284		if let Some(last_cursor) = self.last_cursor {
285			debug_assert!(last_cursor != c, "Detected a next loop, {c:?} was fetched twice");
286		}
287		#[cfg(debug_assertions)]
288		if c == Kind::Eof {
289			self.last_cursor = None;
290		} else {
291			self.last_cursor = Some(c);
292		}
293
294		c
295	}
296}