use super::*;
struct OnceCell<T>(core::cell::Cell<Option<T>>);
impl<T> OnceCell<T> {
pub fn new() -> Self {
Self(core::cell::Cell::new(None))
}
pub fn set(&self, x: T) -> Result<(), ()> {
unsafe {
let vacant = (*self.0.as_ptr()).is_none();
if vacant {
self.0.as_ptr().write(Some(x));
Ok(())
} else {
Err(())
}
}
}
#[inline]
pub fn get(&self) -> Option<&T> {
unsafe { (*self.0.as_ptr()).as_ref() }
}
}
enum RecursiveInner<T: ?Sized> {
Owned(Rc<T>),
Unowned(rc::Weak<T>),
}
pub type Direct<'src, 'b, I, O, Extra> = DynParser<'src, 'b, I, O, Extra>;
pub struct Indirect<'src, 'b, I: Input<'src>, O, Extra: ParserExtra<'src, I>> {
inner: OnceCell<Box<DynParser<'src, 'b, I, O, Extra>>>,
}
pub struct Recursive<P: ?Sized> {
inner: RecursiveInner<P>,
}
impl<'src, 'b, I: Input<'src>, O, E: ParserExtra<'src, I>> Recursive<Indirect<'src, 'b, I, O, E>> {
pub fn declare() -> Self {
Recursive {
inner: RecursiveInner::Owned(Rc::new(Indirect {
inner: OnceCell::new(),
})),
}
}
#[track_caller]
pub fn define<P: Parser<'src, I, O, E> + Clone + 'b>(&mut self, parser: P) {
let location = *Location::caller();
self.parser()
.inner
.set(Box::new(parser))
.unwrap_or_else(|_| {
panic!("recursive parsers can only be defined once, trying to redefine it at {location}")
});
}
}
impl<P: ?Sized> Recursive<P> {
#[inline]
fn parser(&self) -> Rc<P> {
match &self.inner {
RecursiveInner::Owned(x) => x.clone(),
RecursiveInner::Unowned(x) => x
.upgrade()
.expect("Recursive parser used before being defined"),
}
}
}
impl<P: ?Sized> Clone for Recursive<P> {
fn clone(&self) -> Self {
Self {
inner: match &self.inner {
RecursiveInner::Owned(x) => RecursiveInner::Owned(x.clone()),
RecursiveInner::Unowned(x) => RecursiveInner::Unowned(x.clone()),
},
}
}
}
#[cfg(feature = "stacker")]
#[inline]
pub(crate) fn recurse<R, F: FnOnce() -> R>(f: F) -> R {
stacker::maybe_grow(1024 * 64, 1024 * 1024, f)
}
#[cfg(not(feature = "stacker"))]
#[inline]
pub(crate) fn recurse<R, F: FnOnce() -> R>(f: F) -> R {
f()
}
impl<'src, I, O, E> Parser<'src, I, O, E> for Recursive<Indirect<'src, '_, I, O, E>>
where
I: Input<'src>,
E: ParserExtra<'src, I>,
{
#[doc(hidden)]
#[cfg(feature = "debug")]
fn node_info(&self, scope: &mut debug::NodeScope) -> debug::NodeInfo {
#[allow(clippy::incompatible_msrv)]
let ptr = match &self.inner {
RecursiveInner::Owned(x) => Rc::as_ptr(x).addr(),
RecursiveInner::Unowned(x) => rc::Weak::as_ptr(x).addr(),
};
scope.lookup_rec(ptr, |scope| {
self.parser()
.inner
.get()
.expect("Recursive parser used before being defined")
.as_ref()
.node_info(scope)
})
}
#[inline]
fn go<M: Mode>(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult<M, O> {
recurse(move || {
M::invoke(
self.parser()
.inner
.get()
.expect("Recursive parser used before being defined")
.as_ref(),
inp,
)
})
}
go_extra!(O);
}
impl<'src, I, O, E> Parser<'src, I, O, E> for Recursive<Direct<'src, '_, I, O, E>>
where
I: Input<'src>,
E: ParserExtra<'src, I>,
{
#[doc(hidden)]
#[cfg(feature = "debug")]
fn node_info(&self, scope: &mut debug::NodeScope) -> debug::NodeInfo {
#[allow(clippy::incompatible_msrv)]
let ptr = match &self.inner {
RecursiveInner::Owned(x) => Rc::as_ptr(x).addr(),
RecursiveInner::Unowned(x) => rc::Weak::as_ptr(x).addr(),
};
scope.lookup_rec(ptr, |scope| self.parser().node_info(scope))
}
#[inline]
fn go<M: Mode>(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult<M, O> {
recurse(move || M::invoke(&*self.parser(), inp))
}
go_extra!(O);
}
pub fn recursive<'src, 'b, I, O, E, A, F>(f: F) -> Recursive<Direct<'src, 'b, I, O, E>>
where
I: Input<'src>,
E: ParserExtra<'src, I>,
A: Parser<'src, I, O, E> + Clone + 'b,
F: FnOnce(Recursive<Direct<'src, 'b, I, O, E>>) -> A,
{
let rc = Rc::new_cyclic(|rc| {
let rc: rc::Weak<DynParser<'src, 'b, I, O, E>> = rc.clone() as _;
let parser = Recursive {
inner: RecursiveInner::Unowned(rc.clone()),
};
f(parser)
});
Recursive {
inner: RecursiveInner::Owned(rc),
}
}