use super::*;
const SYNTAX: [u16; 10] = [
b'(' as u16,
b')' as u16,
b'[' as u16,
b']' as u16,
b'{' as u16,
b'}' as u16,
b'/' as u16,
b'-' as u16,
b'\\' as u16,
b'|' as u16,
];
const DOUBLE: [u16; 19] = [
b'&' as u16,
b'!' as u16,
b'#' as u16,
b'$' as u16,
b'%' as u16,
b'*' as u16,
b'+' as u16,
b',' as u16,
b'.' as u16,
b':' as u16,
b';' as u16,
b'<' as u16,
b'=' as u16,
b'>' as u16,
b'?' as u16,
b'@' as u16,
b'^' as u16,
b'`' as u16,
b'~' as u16,
];
const RESERVED: [u16; 14] = [
b'&' as u16,
b'-' as u16,
b'!' as u16,
b'#' as u16,
b'%' as u16,
b',' as u16,
b':' as u16,
b';' as u16,
b'<' as u16,
b'=' as u16,
b'>' as u16,
b'@' as u16,
b'`' as u16,
b'~' as u16,
];
impl Parser<'_, '_> {
fn unsupported(&self) -> CompileError {
CompileError::Unsupported {
feature: "Unicode sets",
utf16_offset: self.cursor.position(),
}
}
pub(super) fn class_set(&mut self) -> Result<u32, CompileError> {
let negative = self.eat(b'^')?;
let start = self.range_used;
self.class_set_union()?;
let count = u32::try_from(self.range_used - start).map_err(|_| CompileError::SizeLimit)?;
if count & NEGATED != 0 {
return Err(CompileError::SizeLimit);
}
self.leaf(
CLASS,
start as u32,
count | if negative { NEGATED } else { 0 },
)
}
fn class_set_union(&mut self) -> Result<(), CompileError> {
let mut members = 0;
loop {
self.step()?;
let Some(c) = self.peek() else {
return Err(self.error());
};
if c == b']' as u16 {
self.take()?;
return Ok(());
}
if self.doubled(b'-')? || self.doubled(b'&')? {
if members == 0 {
return Err(self.error());
}
return Err(self.unsupported());
}
self.class_set_operand()?;
members += 1;
}
}
fn doubled(&mut self, c: u8) -> Result<bool, CompileError> {
let mut ahead = self.cursor;
if ahead.next_unit() != Some(u16::from(c)) {
return Ok(false);
}
Ok(ahead.next_unit() == Some(u16::from(c)))
}
fn class_set_operand(&mut self) -> Result<(), CompileError> {
if self.eat(b'[')? {
if self.peek() == Some(b'^' as u16) {
return Err(self.unsupported());
}
return self.class_set_union();
}
let Some(low) = self.class_set_character()? else {
return Ok(());
};
if self.peek() == Some(b'-' as u16) && !self.doubled(b'-')? {
self.take()?;
if self.peek() == Some(b']' as u16) {
return Err(self.error());
}
let Some(high) = self.class_set_character()? else {
return Err(self.error());
};
if low > high {
return Err(self.error());
}
return self.range(low, high);
}
self.range(low, low)
}
fn class_set_character(&mut self) -> Result<Option<u32>, CompileError> {
self.step()?;
let Some(c) = self.peek() else {
return Err(self.error());
};
if c == b'\\' as u16 {
self.take()?;
let Some(escape) = self.take()? else {
return Err(self.error());
};
if escape == b'q' as u16 {
if self.peek() != Some(u16::from(b'{')) {
return Err(self.error());
}
return Err(self.unsupported());
}
if RESERVED.contains(&escape) {
return Ok(Some(u32::from(escape)));
}
if self.builtin(escape)? {
return Ok(None);
}
return self.escaped_point(escape, true).map(Some);
}
if SYNTAX.contains(&c) {
return Err(self.error());
}
if DOUBLE.contains(&c) {
let mut ahead = self.cursor;
ahead.next_unit();
if ahead.next_unit() == Some(c) {
return Err(self.error());
}
}
self.cursor
.next_point()
.map(Some)
.ok_or_else(|| self.error())
}
}