stack + testing

This commit is contained in:
AF 2023-04-22 13:14:38 +00:00
parent 4ababb524c
commit 8a3f9ac25f
11 changed files with 327 additions and 16 deletions

View File

@ -7,3 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
futures = "0.3.26" futures = "0.3.26"
hex = "0.4.3"
[dev-dependencies]
sha2 = "0.10.6"

View File

@ -12,7 +12,7 @@ pub trait Context {
type T: Monad; type T: Monad;
/// Type to represent resolution errors mainly arising in [`Resolver::resolve`]. /// Type to represent resolution errors mainly arising in [`Resolver::resolve`].
type LookupError: Error; type LookupError<'a>: 'a + Error;
/// Get [type@Hash] of a slice, mostly for use in [`Point`]. /// Get [type@Hash] of a slice, mostly for use in [`Point`].
fn hash(s: &[u8]) -> Hash; fn hash(s: &[u8]) -> Hash;
@ -28,7 +28,7 @@ pub enum ResolutionError<L, P> {
pub type ResolutionResult<'a, Ctx, A> = Result< pub type ResolutionResult<'a, Ctx, A> = Result<
Rc<A>, Rc<A>,
ResolutionError< ResolutionError<
<Ctx as Context>::LookupError, <Ctx as Context>::LookupError<'a>,
<<A as Mentionable<'a, Ctx>>::Fctr as Factory<'a, Ctx>>::ParseError, <<A as Mentionable<'a, Ctx>>::Fctr as Factory<'a, Ctx>>::ParseError,
>, >,
>; >;
@ -99,10 +99,10 @@ pub trait Mentionable<'a, Ctx: 'a + Context>: 'a + Serializable {
/// Short-hand for the type of values returned by [`Resolver::resolve`]. /// Short-hand for the type of values returned by [`Resolver::resolve`].
pub type HashResolution<'a, Ctx> = pub type HashResolution<'a, Ctx> =
Wrapped<'a, Ctx, Result<(Vec<u8>, Rc<dyn Resolver<'a, Ctx>>), <Ctx as Context>::LookupError>>; Wrapped<'a, Ctx, Result<(Vec<u8>, Rc<dyn Resolver<'a, Ctx>>), <Ctx as Context>::LookupError<'a>>>;
/// Value accepted by [`Resolver::resolve`]. Includes index to make it order-sensitive. /// Value accepted by [`Resolver::resolve`]. Includes index to make it order-sensitive.
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
pub struct Address { pub struct Address {
pub point: Hash, pub point: Hash,
/// Index of the point in the [`Mentionable::points()`]. /// Index of the point in the [`Mentionable::points()`].
@ -165,12 +165,12 @@ struct LocalOrigin<A>(Rc<A>);
impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Origin<'a, Ctx> for LocalOrigin<A> { impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Origin<'a, Ctx> for LocalOrigin<A> {
type Mtbl = A; type Mtbl = A;
fn factory(&self) -> <Self::Mtbl as Mentionable<'a, Ctx>>::Fctr { fn factory(&self) -> A::Fctr {
self.0.factory() self.0.factory()
} }
fn resolve(self: Rc<Self>) -> Resolution<'a, Ctx, Self::Mtbl> { fn resolve(self: Rc<Self>) -> Resolution<'a, Ctx, Self::Mtbl> {
<Ctx::T as Applicative>::pure(Ok(self.0.clone())) Ctx::T::pure(Ok(self.0.clone()))
} }
} }
@ -289,7 +289,7 @@ pub struct TypelessError<'a>(Box<dyn 'a + Error>);
impl<'a> Display for TypelessError<'a> { impl<'a> Display for TypelessError<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f) f.write_fmt(format_args!("typeless error: {}", self.0))
} }
} }

View File

@ -2,4 +2,6 @@ pub mod core;
pub mod func; pub mod func;
pub mod std; pub mod std;
#[cfg(test)] #[cfg(test)]
mod testing;
#[cfg(test)]
mod xrcs; mod xrcs;

View File

@ -1,5 +1,6 @@
pub mod atomic; pub mod atomic;
pub mod cast; pub mod cast;
pub mod collections;
pub mod inlining; pub mod inlining;
pub mod nullable; pub mod nullable;
@ -135,7 +136,7 @@ impl Display for PointParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
PointParseError::WrongLength(length) => f.write_fmt(format_args!( PointParseError::WrongLength(length) => f.write_fmt(format_args!(
"expected {} bytes, received {}", "expected {} bytes, received {}.",
HASH_SIZE, length HASH_SIZE, length
)), )),
} }
@ -335,3 +336,21 @@ impl<D: ?Sized + Deserializer> ExtDeserializer for D {
} }
} }
} }
impl Display for Address {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}@{}", hex::encode(self.point), self.index))
}
}
pub trait ExtSerializable: Serializable {
fn to_vec(&self) -> Vec<u8>;
}
impl<S: Serializable> ExtSerializable for S {
fn to_vec(&self) -> Vec<u8> {
let mut vec = Vec::new();
self.serialize(&mut vec);
vec
}
}

View File

@ -1,9 +1,12 @@
//! This module allows to describe a primitive subset of [Mentionable] types, [Atomic]s, //! This module allows to describe a primitive subset of [Mentionable] types, [Atomic]s,
//! simple static types, which are completely [Context]-independent. //! simple static types, which are completely [Context]-independent.
pub mod plain;
use std::{error::Error, marker::PhantomData, rc::Rc}; use std::{error::Error, marker::PhantomData, rc::Rc};
use crate::core::*; use crate::core::*;
use crate::std::*;
/// This trait combines functionality of [`Mentionable`] and [`Factory`], /// This trait combines functionality of [`Mentionable`] and [`Factory`],
/// while limiting [`Mentionable::points`] (and corresponding [`Mentionable::topology`]) to an empty sequence. /// while limiting [`Mentionable::points`] (and corresponding [`Mentionable::topology`]) to an empty sequence.
@ -64,3 +67,29 @@ impl<'a, Ctx: 'a + Context, A: Atomic> Factory<'a, Ctx> for AtomicFactory<A> {
A::unexpected_tail(tail) A::unexpected_tail(tail)
} }
} }
fn _parse_slice<A: Atomic>(slice: &[u8]) -> Result<A, A::ParseError> {
let mut deserializer = SliceDeserializer::from(slice);
let mentionable = A::deserialize(&mut deserializer)?;
let tail = deserializer.read_all();
if tail.is_empty() {
Ok(mentionable)
} else {
Err(A::unexpected_tail(tail))
}
}
pub trait ExtAtomic: Atomic {
fn f() -> AtomicFactory<Self>;
fn parse_slice(slice: &[u8]) -> Result<Self, Self::ParseError>;
}
impl<A: Atomic> ExtAtomic for A {
fn f() -> AtomicFactory<Self> {
AtomicFactory::new()
}
fn parse_slice(slice: &[u8]) -> Result<Self, Self::ParseError> {
_parse_slice(slice)
}
}

55
src/std/atomic/plain.rs Normal file
View File

@ -0,0 +1,55 @@
use std::fmt::Display;
use crate::std::atomic::*;
#[derive(Clone)]
struct Plain {
pub data: Vec<u8>,
}
#[derive(Debug)]
enum PlainParseError {}
impl Display for PlainParseError {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {}
}
}
impl Error for PlainParseError {}
impl Serializable for Plain {
fn serialize(&self, serializer: &mut dyn Serializer) {
serializer.write(&self.data)
}
}
impl Atomic for Plain {
type ParseError = PlainParseError;
fn deserialize(deserializer: &mut dyn Deserializer) -> Result<Self, Self::ParseError> {
Ok(Plain {
data: deserializer.read_all().into(),
})
}
fn unexpected_tail(tail: &[u8]) -> Self::ParseError {
panic!(
"Plain must use read_all, therefore there must not be any extra tail (received {} bytes).",
tail.len()
)
}
}
#[cfg(test)]
mod tests {
use crate::std::*;
use super::*;
#[test]
fn test_plain() -> Result<(), PlainParseError> {
let plain = Plain::parse_slice(b"slice")?;
assert_eq!(plain.to_vec().as_slice(), b"slice");
Ok(())
}
}

View File

@ -17,7 +17,7 @@ pub enum CastError<'a> {
impl<'a, Ctx: 'a + Context> Resolver<'a, Ctx> for CastResolver<'a, Ctx> impl<'a, Ctx: 'a + Context> Resolver<'a, Ctx> for CastResolver<'a, Ctx>
where where
Ctx::LookupError: From<CastError<'a>>, Ctx::LookupError<'a>: From<CastError<'a>>,
{ {
fn resolve(self: Rc<Self>, address: Address) -> HashResolution<'a, Ctx> { fn resolve(self: Rc<Self>, address: Address) -> HashResolution<'a, Ctx> {
let point = match self.points.get(address.index) { let point = match self.points.get(address.index) {
@ -37,12 +37,10 @@ where
Ctx::T::fmap( Ctx::T::fmap(
|resolved| match resolved { |resolved| match resolved {
Ok(mentionable) => { Ok(mentionable) => {
let mut vec = Vec::new();
mentionable.serialize(&mut vec);
let resolver: Rc<dyn Resolver<'a, Ctx>> = Rc::new(CastResolver { let resolver: Rc<dyn Resolver<'a, Ctx>> = Rc::new(CastResolver {
points: mentionable.points(), points: mentionable.points(),
}); });
Ok((vec, resolver)) Ok((mentionable.to_vec(), resolver))
} }
Err(error) => Err(match error { Err(error) => Err(match error {
ResolutionError::Lookup(lookup_error) => lookup_error, ResolutionError::Lookup(lookup_error) => lookup_error,
@ -58,13 +56,11 @@ pub type CastResult<'a, Ctx, A> =
Result<A, <<A as Mentionable<'a, Ctx>>::Fctr as Factory<'a, Ctx>>::ParseError>; Result<A, <<A as Mentionable<'a, Ctx>>::Fctr as Factory<'a, Ctx>>::ParseError>;
impl<'a, Ctx: Context> TypelessMentionable<'a, Ctx> impl<'a, Ctx: Context> TypelessMentionable<'a, Ctx>
where where
Ctx::LookupError: From<CastError<'a>>, Ctx::LookupError<'a>: From<CastError<'a>>,
{ {
pub fn cast<A: Mentionable<'a, Ctx>>(&self, factory: A::Fctr) -> CastResult<'a, Ctx, A> { pub fn cast<A: Mentionable<'a, Ctx>>(&self, factory: A::Fctr) -> CastResult<'a, Ctx, A> {
let mut vec = Vec::new();
self.serialize(&mut vec);
factory.parse_slice( factory.parse_slice(
&vec, &self.to_vec(),
Rc::new(CastResolver { Rc::new(CastResolver {
points: self.points(), points: self.points(),
}), }),

1
src/std/collections.rs Normal file
View File

@ -0,0 +1 @@
pub mod stack;

View File

@ -0,0 +1,129 @@
use crate::core::*;
use crate::std::nullable::*;
use crate::std::*;
pub struct StackNode<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> {
pub rest: Stack<'a, Ctx, A>,
pub element: A,
}
type Stack<'a, Ctx, A> = Nullable<'a, Ctx, StackNode<'a, Ctx, A>>;
pub struct StackNodeFactory<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> {
element_factory: A::Fctr,
}
impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> StackNodeFactory<'a, Ctx, A> {
fn new(factory: A::Fctr) -> Self {
StackNodeFactory {
element_factory: factory,
}
}
}
impl<'a, Ctx: 'a + Context, 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: 'a + Context, 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(&self) -> Vec<Point<'a, Ctx, TypelessMentionable<'a, Ctx>>> {
let mut points = self.rest.points();
points.extend(self.element.points());
points
}
}
impl<'a, Ctx: 'a + Context, 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: 'a + Context, A: Mentionable<'a, Ctx>> Factory<'a, Ctx>
for StackNodeFactory<'a, Ctx, A>
{
type Mtbl = StackNode<'a, Ctx, A>;
type ParseError = StackParseError<<A::Fctr as Factory<'a, Ctx>>::ParseError>;
fn deserialize(
&self,
deserializer: &mut dyn Deserializer,
resolver: Rc<dyn Resolver<'a, Ctx>>,
) -> ParseResult<'a, Ctx, Self> {
let rest =
match NullableFactory::new(self.clone()).deserialize(deserializer, resolver.clone()) {
Ok(rest) => rest,
Err(ppe) => {
return Err(StackParseError::Point(ppe));
}
};
let element = match self.element_factory.deserialize(deserializer, resolver) {
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))
}
}
pub trait ExtStack<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>>: Mentionable<'a, Ctx> {
fn empty(factory: A::Fctr) -> Self;
fn f(factory: A::Fctr) -> Self::Fctr;
fn add(self, element: A) -> Self;
}
impl<'a, Ctx: 'a + Context, 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,
}
.into(),
)
}
}

View File

@ -79,3 +79,23 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Factory<'a, Ctx>
PointParseError::WrongLength(HASH_SIZE + tail.len()) PointParseError::WrongLength(HASH_SIZE + tail.len())
} }
} }
impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Nullable<'a, Ctx, Nullable<'a, Ctx, A>> {
pub fn join(&self) -> Resolution<'a, Ctx, Nullable<'a, Ctx, A>> {
match self {
Nullable::Null(nullable_factory) => {
let NullableFactory { factory } = nullable_factory;
Ctx::T::pure(Ok(Rc::new(Nullable::Null(factory.clone()))))
},
Nullable::NotNull(point) => {
point.resolve()
},
}
}
}
impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> NullableFactory<'a, Ctx, A> {
pub fn new(factory: A::Fctr) -> Self {
Self { factory }
}
}

56
src/testing.rs Normal file
View File

@ -0,0 +1,56 @@
use std::error::Error;
use std::fmt::Display;
use crate::core::*;
use crate::func::*;
use sha2::{Digest, Sha256};
pub struct TestContext;
#[derive(Debug)]
pub enum TestLookupError<'a> {
Typeless(TypelessError<'a>),
EmptyResolverAccess(Address),
}
impl<'a> From<TypelessError<'a>> for TestLookupError<'a> {
fn from(value: TypelessError<'a>) -> Self {
Self::Typeless(value)
}
}
impl<'a> Display for TestLookupError<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TestLookupError::Typeless(typeless_error) => {
f.write_fmt(format_args!("typeless lookup failure: {}", typeless_error))
}
TestLookupError::EmptyResolverAccess(address) => f.write_fmt(format_args!(
"accessed an empty resolved at address {}.",
address
)),
}
}
}
impl<'a> Error for TestLookupError<'a> {}
impl Context for TestContext {
type T = classes::solo::SoloClass;
type LookupError<'a> = TestLookupError<'a>;
fn hash(s: &[u8]) -> Hash {
let mut hasher = Sha256::new();
hasher.update(s);
hasher.finalize().into()
}
}
pub struct EmptyResolver;
impl<'a> Resolver<'a, TestContext> for EmptyResolver {
fn resolve(self: std::rc::Rc<Self>, address: Address) -> HashResolution<'a, TestContext> {
Err(TestLookupError::EmptyResolverAccess(address))
}
}