stack + testing
This commit is contained in:
parent
4ababb524c
commit
8a3f9ac25f
@ -7,3 +7,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
futures = "0.3.26"
|
||||
hex = "0.4.3"
|
||||
|
||||
[dev-dependencies]
|
||||
sha2 = "0.10.6"
|
||||
|
14
src/core.rs
14
src/core.rs
@ -12,7 +12,7 @@ pub trait Context {
|
||||
type T: Monad;
|
||||
|
||||
/// 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`].
|
||||
fn hash(s: &[u8]) -> Hash;
|
||||
@ -28,7 +28,7 @@ pub enum ResolutionError<L, P> {
|
||||
pub type ResolutionResult<'a, Ctx, A> = Result<
|
||||
Rc<A>,
|
||||
ResolutionError<
|
||||
<Ctx as Context>::LookupError,
|
||||
<Ctx as Context>::LookupError<'a>,
|
||||
<<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`].
|
||||
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.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Address {
|
||||
pub point: Hash,
|
||||
/// 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> {
|
||||
type Mtbl = A;
|
||||
|
||||
fn factory(&self) -> <Self::Mtbl as Mentionable<'a, Ctx>>::Fctr {
|
||||
fn factory(&self) -> A::Fctr {
|
||||
self.0.factory()
|
||||
}
|
||||
|
||||
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> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
f.write_fmt(format_args!("typeless error: {}", self.0))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,4 +2,6 @@ pub mod core;
|
||||
pub mod func;
|
||||
pub mod std;
|
||||
#[cfg(test)]
|
||||
mod testing;
|
||||
#[cfg(test)]
|
||||
mod xrcs;
|
||||
|
21
src/std.rs
21
src/std.rs
@ -1,5 +1,6 @@
|
||||
pub mod atomic;
|
||||
pub mod cast;
|
||||
pub mod collections;
|
||||
pub mod inlining;
|
||||
pub mod nullable;
|
||||
|
||||
@ -135,7 +136,7 @@ impl Display for PointParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PointParseError::WrongLength(length) => f.write_fmt(format_args!(
|
||||
"expected {} bytes, received {}",
|
||||
"expected {} bytes, received {}.",
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
//! This module allows to describe a primitive subset of [Mentionable] types, [Atomic]s,
|
||||
//! simple static types, which are completely [Context]-independent.
|
||||
|
||||
pub mod plain;
|
||||
|
||||
use std::{error::Error, marker::PhantomData, rc::Rc};
|
||||
|
||||
use crate::core::*;
|
||||
use crate::std::*;
|
||||
|
||||
/// This trait combines functionality of [`Mentionable`] and [`Factory`],
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
||||
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
55
src/std/atomic/plain.rs
Normal 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(())
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ pub enum CastError<'a> {
|
||||
|
||||
impl<'a, Ctx: 'a + Context> Resolver<'a, Ctx> for CastResolver<'a, Ctx>
|
||||
where
|
||||
Ctx::LookupError: From<CastError<'a>>,
|
||||
Ctx::LookupError<'a>: From<CastError<'a>>,
|
||||
{
|
||||
fn resolve(self: Rc<Self>, address: Address) -> HashResolution<'a, Ctx> {
|
||||
let point = match self.points.get(address.index) {
|
||||
@ -37,12 +37,10 @@ where
|
||||
Ctx::T::fmap(
|
||||
|resolved| match resolved {
|
||||
Ok(mentionable) => {
|
||||
let mut vec = Vec::new();
|
||||
mentionable.serialize(&mut vec);
|
||||
let resolver: Rc<dyn Resolver<'a, Ctx>> = Rc::new(CastResolver {
|
||||
points: mentionable.points(),
|
||||
});
|
||||
Ok((vec, resolver))
|
||||
Ok((mentionable.to_vec(), resolver))
|
||||
}
|
||||
Err(error) => Err(match 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>;
|
||||
impl<'a, Ctx: Context> TypelessMentionable<'a, Ctx>
|
||||
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> {
|
||||
let mut vec = Vec::new();
|
||||
self.serialize(&mut vec);
|
||||
factory.parse_slice(
|
||||
&vec,
|
||||
&self.to_vec(),
|
||||
Rc::new(CastResolver {
|
||||
points: self.points(),
|
||||
}),
|
||||
|
1
src/std/collections.rs
Normal file
1
src/std/collections.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod stack;
|
129
src/std/collections/stack.rs
Normal file
129
src/std/collections/stack.rs
Normal 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(),
|
||||
)
|
||||
}
|
||||
}
|
@ -79,3 +79,23 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Factory<'a, Ctx>
|
||||
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
56
src/testing.rs
Normal 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))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user