use std::num::NonZeroU32;
pub(crate) type NodeId = Option<NonZeroU32>;
pub(crate) const SYNTHETIC_ID: NodeId = None;
#[derive(Debug)]
pub(crate) struct CompileCtx {
next_id: NonZeroU32,
skip_fold: bool,
depth: usize,
pointer: Option<String>,
pointers: Vec<(u32, Box<str>)>,
config_folds: bool,
}
pub(crate) type NodePointers = Box<[(u32, Box<str>)]>;
const ID_ONE: NonZeroU32 = match NonZeroU32::new(1) {
Some(n) => n,
None => unreachable!(),
};
pub(crate) const MAX_COMPILE_DEPTH: usize = 256;
impl CompileCtx {
pub(crate) fn new() -> Self {
Self {
next_id: ID_ONE,
skip_fold: false,
depth: 0,
pointer: None,
pointers: Vec::new(),
config_folds: false,
}
}
pub(crate) fn no_fold() -> Self {
Self {
next_id: ID_ONE,
skip_fold: true,
depth: 0,
pointer: None,
pointers: Vec::new(),
config_folds: false,
}
}
#[cfg(feature = "trace")]
pub(crate) fn recording_pointers(mut self) -> Self {
self.pointer = Some(String::new());
self
}
#[inline]
pub(crate) fn descend(&mut self, token: &str) -> usize {
let Some(pointer) = &mut self.pointer else {
return 0;
};
let mark = pointer.len();
push_pointer_token(pointer, token);
mark
}
#[inline]
pub(crate) fn descend_index(&mut self, index: usize) -> usize {
let Some(pointer) = &mut self.pointer else {
return 0;
};
let mark = pointer.len();
let mut buf = itoa::Buffer::new();
pointer.push('/');
pointer.push_str(buf.format(index));
mark
}
#[inline]
pub(crate) fn ascend(&mut self, mark: usize) {
if let Some(pointer) = &mut self.pointer {
pointer.truncate(mark);
}
}
pub(crate) fn take_pointers(&mut self) -> Option<NodePointers> {
self.pointer
.is_some()
.then(|| std::mem::take(&mut self.pointers).into_boxed_slice())
}
#[inline]
pub(crate) fn enter(&mut self) -> crate::Result<()> {
self.depth += 1;
if self.depth > MAX_COMPILE_DEPTH {
return Err(crate::Error::configuration_error(format!(
"rule nesting exceeds the maximum compile depth of {MAX_COMPILE_DEPTH}"
)));
}
Ok(())
}
#[inline]
pub(crate) fn leave(&mut self) {
self.depth -= 1;
}
#[inline]
pub(crate) fn next_id(&mut self) -> NonZeroU32 {
let id = self.next_id;
self.next_id = self.next_id.saturating_add(1);
if let Some(pointer) = &self.pointer {
self.pointers.push((id.get(), pointer.as_str().into()));
}
id
}
#[inline]
pub(crate) fn note_config_fold(&mut self) {
self.config_folds = true;
}
pub(crate) fn has_config_folds(&self) -> bool {
self.config_folds
}
#[inline]
pub(crate) fn skip_fold(&self) -> bool {
self.skip_fold
}
}
pub(crate) fn push_pointer_token(pointer: &mut String, token: &str) {
pointer.push('/');
for c in token.chars() {
match c {
'~' => pointer.push_str("~0"),
'/' => pointer.push_str("~1"),
c => pointer.push(c),
}
}
}