radn-rs/src/rstd/collections/stack.rs
2023-05-30 15:39:48 +00:00

295 lines
9.3 KiB
Rust

//! Basic implementation of a stack/linked list.
use crate::func::context::*;
use crate::rcore::*;
use crate::rstd::{inlining::*, nullable::*, point::*, *};
/// Node containing a (nullable) reference to the next node and an element.
pub struct StackNode<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> {
/// Reference comes first due to being inlineable.
pub rest: Stack<'a, Ctx, A>,
/// Unlike the original implementation in Python, doesn't default to using Point.
pub element: Rc<A>,
}
/// Type representing a stack, an alias to a [Nullable] of a [StackNode].
pub type Stack<'a, Ctx, A> = Nullable<'a, Ctx, StackNode<'a, Ctx, A>>;
pub struct StackNodeFactory<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> {
element_factory: A::Fctr,
}
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> StackNodeFactory<'a, Ctx, A> {
fn new(factory: A::Fctr) -> Self {
StackNodeFactory {
element_factory: factory,
}
}
}
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> Serializable for StackNode<'a, Ctx, A> {
fn serialize(&self, serializer: &mut dyn Serializer) {
self.rest.serialize(serializer);
self.element.serialize(serializer);
}
}
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> Mentionable<'a, Ctx> for StackNode<'a, Ctx, A> {
type Fctr = StackNodeFactory<'a, Ctx, A>;
fn factory(&self) -> Self::Fctr {
StackNodeFactory::new(self.element.factory())
}
fn points_typed(&self, points: &mut impl TakesPoints<'a, Ctx>) {
self.rest.points_typed(points);
self.element.points_typed(points);
}
}
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> Clone for StackNodeFactory<'a, Ctx, A> {
fn clone(&self) -> Self {
StackNodeFactory::new(self.element_factory.clone())
}
}
#[derive(Debug)]
pub enum StackParseError<ElementParseError: Error> {
Point(PointParseError),
Element(ElementParseError),
}
impl<ElementParseError: Error> Display for StackParseError<ElementParseError> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StackParseError::Point(ppe) => {
f.write_fmt(format_args!("can't parse stack's next pointer: {}", ppe))
}
StackParseError::Element(epe) => {
f.write_fmt(format_args!("can't parse stack's element: {}", epe))
}
}
}
}
impl<ElementParseError: Error> Error for StackParseError<ElementParseError> {}
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> Factory<'a, Ctx>
for StackNodeFactory<'a, Ctx, A>
{
type Mtbl = StackNode<'a, Ctx, A>;
type ParseError = StackParseError<ParseError<'a, Ctx, A::Fctr>>;
fn deserialize(
&self,
deserializer: &mut dyn Deserializer,
resolver: Rc<dyn Resolver<'a, Ctx>>,
addresses: &mut Addresses,
) -> ParseResult<'a, Ctx, Self> {
let rest = match NullableFactory::new(self.clone()).deserialize(
deserializer,
resolver.clone(),
addresses,
) {
Ok(rest) => rest,
Err(ppe) => {
return Err(StackParseError::Point(ppe));
}
};
let element = Rc::new(
match self
.element_factory
.deserialize(deserializer, resolver, addresses)
{
Ok(element) => element,
Err(epe) => {
return Err(StackParseError::Element(epe));
}
},
);
Ok(StackNode { rest, element })
}
fn unexpected_tail(&self, tail: &[u8]) -> Self::ParseError {
StackParseError::Element(self.element_factory.unexpected_tail(tail))
}
}
/// See [`StackVecResult`].
pub type StackFaiure<'a, Ctx, A> = ResolutionFailure<'a, Ctx, StackNode<'a, Ctx, A>>;
/// See [`StackVecWrapped`].
pub type StackVecResult<'a, Ctx, A> = Result<Vec<Rc<A>>, StackFaiure<'a, Ctx, A>>;
/// See [`ExtStack::vec`].
pub type StackVecWrapped<'a, Ctx, A> = Wrapped<'a, Ctx, StackVecResult<'a, Ctx, A>>;
/// Extention trait with helper methods for [Stack]s.
pub trait ExtStack<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>>: Mentionable<'a, Ctx> {
/// Get an empty stack ([`Nullable::Null`]).
fn empty(factory: A::Fctr) -> Self;
/// Get the corresponding factory.
fn f(factory: A::Fctr) -> Self::Fctr;
/// Add one element.
///
/// Note: consumes the stack. For non-consuming version do `.clone()` before adding.
fn add(self, element: A) -> Self;
/// Collect all the elements into a [`Vec`].
fn vec(self) -> StackVecWrapped<'a, Ctx, A>;
}
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> ExtStack<'a, Ctx, A> for Stack<'a, Ctx, A> {
fn empty(factory: A::Fctr) -> Self {
Nullable::Null(StackNodeFactory::new(factory.clone()))
}
fn f(factory: A::Fctr) -> Self::Fctr {
NullableFactory::new(StackNodeFactory::new(factory.clone()))
}
fn add(self, element: A) -> Self {
Nullable::NotNull(
StackNode {
rest: self,
element: Rc::new(element),
}
.into(),
)
}
fn vec(self) -> StackVecWrapped<'a, Ctx, A> {
Ctx::T::iterate_mut((vec![], self), |(mut vec, stack)| match stack {
Nullable::Null(_) => Ctx::pure(ControlFlow::Break(Ok(vec))),
Nullable::NotNull(point) => Ctx::fmap(point.resolve(), |resolved| {
let node = match resolved {
Ok(node) => node,
Err(error) => {
return ControlFlow::Break(Err(error));
}
};
vec.push(node.element.clone());
ControlFlow::Continue((vec, node.rest.clone()))
}),
})
}
}
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> InlineableFactory
for StackNodeFactory<'a, Ctx, A>
where
A::Fctr: InlineableFactory,
{
}
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> FixedSizeFactory
for StackNodeFactory<'a, Ctx, A>
where
A::Fctr: FixedSizeFactory,
{
fn size(&self) -> usize {
Stack::<'a, Ctx, A>::SIZE + self.element_factory.size()
}
}
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> ConstSizeFactory
for StackNodeFactory<'a, Ctx, A>
where
A::Fctr: ConstSizeFactory,
{
const SIZE: usize = Stack::<'a, Ctx, A>::SIZE + A::Fctr::SIZE;
}
#[cfg(test)]
mod tests {
use std::rc::Rc;
use crate::rstd::{
atomic::{atomic_object::*, plain::*},
tracing::*,
};
use crate::testing::{counted::*, traced::*, *};
use super::*;
type T<Ctx> = Stack<'static, Ctx, AtomicObject<Plain>>;
fn unstack(
stack: &T<TestContextPlain>,
) -> Result<Vec<Vec<u8>>, StackFaiure<'static, TestContextPlain, AtomicObject<Plain>>> {
Ok(stack
.clone()
.vec()?
.iter()
.map(|plain| plain.raw())
.collect())
}
fn make_stack<Ctx: Context<'static>>() -> T<Ctx> {
let stack: T<Ctx> = Stack::empty(Plain::f());
let stack: T<Ctx> = stack.add(Plain::from_slice(b"A0").into());
let stack: T<Ctx> = stack.add(Plain::from_slice(b"B1").into());
let stack: T<Ctx> = stack.add(Plain::from_slice(b"C2").into());
stack
}
fn validate_stack(stack: &T<TestContextPlain>) {
let mut vec: Vec<Vec<u8>> = unstack(stack).unwrap();
vec.reverse();
assert_eq!(vec, vec![b"A0".to_vec(), b"B1".to_vec(), b"C2".to_vec()]);
}
#[test]
fn test_stack() -> Result<(), StackFaiure<'static, TestContextPlain, AtomicObject<Plain>>> {
let stack: T<TestContextPlain> = make_stack();
validate_stack(&stack);
let stack: T<TestContextPlain> = reparse(stack.into());
validate_stack(&stack);
Ok(())
}
#[test]
fn test_counted() -> Result<(), point::PointParseError> {
let stack: T<TestContextCounted> = make_stack();
let count = stack.clone().vec().count();
assert_eq!(count, 0);
let stack: T<TestContextCounted> = Rc::new(stack).delay()?;
let count = stack.clone().vec().count();
assert_eq!(count, 3);
Ok(())
}
#[test]
fn test_traced() -> Result<(), point::PointParseError> {
let stack: T<TestContextTraced> = make_stack();
let traced = stack.clone().vec();
assert_eq!(traced.length(), 0);
assert_eq!(traced.width(), 0);
assert_eq!(format!("{}", traced.effect), ".");
let stack: T<TestContextTraced> = Rc::new(stack).trace()?;
let traced = stack.clone().vec();
assert_eq!(traced.length(), 3);
assert_eq!(traced.width(), 1);
assert_eq!(format!("{}", traced.effect), "( ? > ? > ? )");
Ok(())
}
#[test]
fn test_rendered() -> Result<(), point::PointParseError> {
let stack: T<TestContextTraced> = make_stack();
let rendered = stack.clone().vec().render();
assert_eq!(rendered.length(), 0);
assert_eq!(rendered.width(), 0);
assert_eq!(format!("{}", rendered), ".");
let stack: T<TestContextTraced> = Rc::new(stack).trace()?;
let rendered = stack.clone().vec().render();
assert_eq!(rendered.length(), 3);
assert_eq!(rendered.width(), 1);
assert_eq!(format!("{}", rendered), "( ? > ? > ? )");
Ok(())
}
}