330 lines
13 KiB
Rust
330 lines
13 KiB
Rust
use std::borrow::Borrow;
|
|
use std::borrow::Cow;
|
|
use std::cmp::Ordering;
|
|
use std::collections::BTreeMap;
|
|
use std::collections::BTreeSet;
|
|
use std::hash::Hash;
|
|
use std::hash::Hasher;
|
|
use std::io;
|
|
|
|
use crate::DefaultDomainCodec;
|
|
use crate::DomainDecode;
|
|
use crate::Reader;
|
|
use crate::error::ReadError;
|
|
use crate::reader::NextToken;
|
|
use crate::signed_integer::OutOfRange;
|
|
use crate::AtomClass;
|
|
use crate::CompoundClass;
|
|
use crate::Domain;
|
|
use crate::DomainEncode;
|
|
use crate::SignedInteger;
|
|
use crate::ValueClass;
|
|
use crate::Writer;
|
|
use crate::write_value;
|
|
|
|
use super::float::{eq_f32, eq_f64, cmp_f32, cmp_f64};
|
|
|
|
pub trait ValueReader<'de> {
|
|
type Impl: ValueImpl<'de>;
|
|
|
|
fn read_impl<R: Reader<'de> + ?Sized, Dec: DomainDecode<'de, <Self::Impl as ValueImpl<'de>>::Embedded>>(
|
|
r: &mut R,
|
|
read_annotations: bool,
|
|
dec: &mut Dec,
|
|
) -> Result<Self::Impl, ReadError>;
|
|
|
|
fn read_domain<R: Reader<'de> + ?Sized, Dec: DomainDecode<'de, <Self::Impl as ValueImpl<'de>>::Embedded>>(
|
|
r: &mut R,
|
|
read_annotations: bool,
|
|
dec: &mut Dec,
|
|
) -> Result<<Self::Impl as ValueImpl<'de>>::Handle, ReadError> {
|
|
Ok(Self::read_impl(r, read_annotations, dec)?.wrap())
|
|
}
|
|
|
|
fn read<R: Reader<'de> + ?Sized>(
|
|
r: &mut R,
|
|
read_annotations: bool,
|
|
) -> Result<<Self::Impl as ValueImpl<'de>>::Handle, ReadError> {
|
|
Self::read_domain(r, read_annotations, &mut DefaultDomainCodec)
|
|
}
|
|
|
|
fn read_iovalue<R: Reader<'de> + ?Sized>(
|
|
r: &mut R,
|
|
read_annotations: bool,
|
|
) -> Result<<Self::Impl as ValueImpl<'de>>::IOEmbedded, ReadError>;
|
|
|
|
fn gather_annotations<R: Reader<'de> + ?Sized>(
|
|
r: &mut R,
|
|
) -> io::Result<Option<(Vec<<<Self::Impl as ValueImpl<'de>>::Mapped<<Self::Impl as ValueImpl<'de>>::IOEmbedded> as ValueImpl<'de>>::Handle>, ValueClass)>> {
|
|
let mut anns = Vec::new();
|
|
loop {
|
|
match r.peek_class()? {
|
|
None => return Ok(None),
|
|
Some(NextToken::Value(v)) => return Ok(Some((anns, v))),
|
|
Some(NextToken::Annotation) => {
|
|
r.open_annotation()?;
|
|
anns.push(Self::read_iovalue(r, true)?.into());
|
|
r.close_annotation()?;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Atomic values from the specification.
|
|
pub trait ValueImpl<'de>: Sized {
|
|
type Handle: Borrow<Self> + Clone + From<Self> + Hash + Eq + Ord + PartialEq + PartialOrd;
|
|
type Embedded: Domain<'de>;
|
|
type Mapped<E: Domain<'de>>: ValueImpl<'de, Embedded = E>;
|
|
type Items<'a>: Iterator<Item = Self::Handle> + 'a where Self: 'a;
|
|
type Entries<'a>: Iterator<Item = (Self::Handle, Self::Handle)> + 'a where Self: 'a;
|
|
|
|
type IOEmbedded: From<<Self::Mapped<Self::IOEmbedded> as ValueImpl<'de>>::Handle> +
|
|
Into<<Self::Mapped<Self::IOEmbedded> as ValueImpl<'de>>::Handle> +
|
|
AsRef<<Self::Mapped<Self::IOEmbedded> as ValueImpl<'de>>::Handle> +
|
|
Domain<'de>;
|
|
|
|
fn wrap(self) -> Self::Handle;
|
|
|
|
fn write(&self, w: &mut dyn Writer, enc: &mut dyn DomainEncode<Self::Embedded>) -> io::Result<()> {
|
|
write_value(w, self, enc)
|
|
}
|
|
|
|
fn value_class(&self) -> ValueClass;
|
|
|
|
fn as_boolean(&self) -> Option<bool>;
|
|
fn as_float(&self) -> Option<f32>;
|
|
fn as_double(&self) -> Option<f64>;
|
|
fn as_signed_integer(&self) -> Option<Cow<'_, SignedInteger>>;
|
|
fn as_string(&self) -> Option<Cow<'_, str>>;
|
|
fn as_bytestring(&self) -> Option<Cow<'_, [u8]>>;
|
|
fn as_symbol(&self) -> Option<Cow<'_, str>>;
|
|
|
|
fn as_i8(&self) -> Option<Result<i8, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_u8(&self) -> Option<Result<u8, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_i16(&self) -> Option<Result<i16, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_u16(&self) -> Option<Result<u16, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_i32(&self) -> Option<Result<i32, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_u32(&self) -> Option<Result<u32, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_i64(&self) -> Option<Result<i64, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_u64(&self) -> Option<Result<u64, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_i128(&self) -> Option<Result<i128, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_u128(&self) -> Option<Result<u128, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_isize(&self) -> Option<Result<isize, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
fn as_usize(&self) -> Option<Result<usize, OutOfRange>> { self.as_signed_integer().map(|i| (&*i).try_into()) }
|
|
|
|
fn is_record(&self) -> bool;
|
|
fn label(&self) -> Self::Handle;
|
|
|
|
fn is_sequence(&self) -> bool;
|
|
fn len(&self) -> usize;
|
|
fn index(&self, _i: usize) -> Self::Handle;
|
|
fn iter(&self) -> Self::Items<'_>;
|
|
|
|
fn is_set(&self) -> bool;
|
|
fn has<E: ValueImpl<'de, Embedded = Self::Embedded>>(&self, _v: &E::Handle) -> bool;
|
|
|
|
fn is_dictionary(&self) -> bool;
|
|
fn get<K: ValueImpl<'de, Embedded = Self::Embedded>>(&self, _k: &K::Handle) -> Option<Self::Handle>;
|
|
fn entries(&self) -> Self::Entries<'_>;
|
|
|
|
fn as_embedded(&self) -> Option<Cow<'_, Self::Embedded>>;
|
|
|
|
// INVARIANT: return Some() *only* when the contained collection is nonempty
|
|
fn annotations(&self) -> Option<Cow<'_, [<Self::Mapped<Self::IOEmbedded> as ValueImpl<'de>>::Handle]>>;
|
|
|
|
fn peeled(v: &Self::Handle) -> Self::Handle;
|
|
|
|
fn copy<E: ValueImpl<'de>, F, Err>(w: &E::Handle, f: &mut F) -> Result<Self::Handle, Err>
|
|
where
|
|
F: FnMut(&E::Embedded) -> Result<Self::Handle, Err>;
|
|
|
|
fn map_embedded<E: Domain<'de>, F, Err>(v: &Self::Handle, f: &mut F) -> Result<<Self::Mapped<E> as ValueImpl<'de>>::Handle, Err>
|
|
where
|
|
F: FnMut(&Self::Embedded) -> Result<E, Err>;
|
|
}
|
|
|
|
pub fn value_hash<'de, V: ValueImpl<'de>, H: Hasher>(v: &V, state: &mut H) {
|
|
match v.value_class() {
|
|
ValueClass::Atomic(a) => match a {
|
|
AtomClass::Boolean => v.as_boolean().unwrap().hash(state),
|
|
AtomClass::Float => v.as_float().unwrap().to_bits().hash(state),
|
|
AtomClass::Double => v.as_double().unwrap().to_bits().hash(state),
|
|
AtomClass::SignedInteger => v.as_signed_integer().unwrap().hash(state),
|
|
AtomClass::String => v.as_string().unwrap().hash(state),
|
|
AtomClass::ByteString => v.as_bytestring().unwrap().hash(state),
|
|
AtomClass::Symbol => v.as_symbol().unwrap().hash(state),
|
|
}
|
|
ValueClass::Compound(c) => match c {
|
|
CompoundClass::Sequence |
|
|
CompoundClass::Set => {
|
|
state.write_usize(v.len());
|
|
for v in v.iter() { v.hash(state) }
|
|
}
|
|
CompoundClass::Record => {
|
|
v.label().hash(state);
|
|
state.write_usize(v.len());
|
|
for v in v.iter() { v.hash(state) }
|
|
}
|
|
CompoundClass::Dictionary => {
|
|
state.write_usize(v.len());
|
|
for (k, v) in v.entries() {
|
|
k.hash(state);
|
|
v.hash(state);
|
|
}
|
|
}
|
|
}
|
|
ValueClass::Embedded => v.as_embedded().unwrap().hash(state),
|
|
}
|
|
}
|
|
|
|
// TODO: when unstable feature iter_order_by stabilises, use that instead
|
|
fn iters_eq<'de, V: ValueImpl<'de>, W: ValueImpl<'de, Embedded = V::Embedded>>(i: &V::Items<'_>, j: &W::Items<'_>) -> bool {
|
|
loop {
|
|
match i.next() {
|
|
None => return j.next().is_none(),
|
|
Some(ii) => match j.next() {
|
|
None => return false,
|
|
Some(jj) => if !value_eq(ii.borrow(), jj.borrow()) { return false },
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn value_eq<'de, V: ValueImpl<'de>, W: ValueImpl<'de, Embedded = V::Embedded>>(v: &V, w: &W) -> bool {
|
|
let cls = v.value_class();
|
|
if cls != w.value_class() { return false; }
|
|
match cls {
|
|
ValueClass::Atomic(a) => match a {
|
|
AtomClass::Boolean =>
|
|
v.as_boolean().unwrap() == w.as_boolean().unwrap(),
|
|
AtomClass::Float =>
|
|
eq_f32(v.as_float().unwrap(), w.as_float().unwrap()),
|
|
AtomClass::Double =>
|
|
eq_f64(v.as_double().unwrap(), w.as_double().unwrap()),
|
|
AtomClass::SignedInteger =>
|
|
v.as_signed_integer().unwrap() == w.as_signed_integer().unwrap(),
|
|
AtomClass::String =>
|
|
v.as_string().unwrap() == w.as_string().unwrap(),
|
|
AtomClass::ByteString =>
|
|
v.as_bytestring().unwrap() == w.as_bytestring().unwrap(),
|
|
AtomClass::Symbol =>
|
|
v.as_symbol().unwrap() == w.as_symbol().unwrap(),
|
|
}
|
|
ValueClass::Compound(c) => match c {
|
|
CompoundClass::Record => {
|
|
if !value_eq(v.label().borrow(), w.label().borrow()) { return false; }
|
|
iters_eq::<V, W>(&v.iter(), &w.iter())
|
|
}
|
|
CompoundClass::Sequence => {
|
|
iters_eq::<V, W>(&v.iter(), &w.iter())
|
|
}
|
|
CompoundClass::Set => {
|
|
let s1 = v.iter().collect::<BTreeSet<_>>();
|
|
let s2 = w.iter().collect::<BTreeSet<_>>();
|
|
todo!() // s1 == s2
|
|
}
|
|
CompoundClass::Dictionary => {
|
|
let d1 = v.entries().collect::<BTreeMap<_, _>>();
|
|
let d2 = w.entries().collect::<BTreeMap<_, _>>();
|
|
todo!() // d1 == d2
|
|
}
|
|
}
|
|
ValueClass::Embedded => v.as_embedded().unwrap() == w.as_embedded().unwrap(),
|
|
}
|
|
}
|
|
|
|
// TODO: when unstable feature iter_order_by stabilises, use that instead
|
|
fn iters_cmp<'de, V: ValueImpl<'de>, W: ValueImpl<'de, Embedded = V::Embedded>>(i: &V::Items<'_>, j: &W::Items<'_>) -> Ordering {
|
|
loop {
|
|
match i.next() {
|
|
None => return if j.next().is_none() { Ordering::Equal } else { Ordering::Less },
|
|
Some(ii) => match j.next() {
|
|
None => return Ordering::Greater,
|
|
Some(jj) => {
|
|
let r = value_cmp(ii.borrow(), jj.borrow());
|
|
if !r.is_eq() { return r }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn value_cmp<'de, V: ValueImpl<'de>, W: ValueImpl<'de, Embedded = V::Embedded>>(v: &V, w: &W) -> Ordering {
|
|
let cls = v.value_class();
|
|
cls.cmp(&w.value_class()).then_with(|| match cls {
|
|
ValueClass::Atomic(a) => match a {
|
|
AtomClass::Boolean =>
|
|
v.as_boolean().cmp(&w.as_boolean()),
|
|
AtomClass::Float =>
|
|
cmp_f32(v.as_float().unwrap(), w.as_float().unwrap()),
|
|
AtomClass::Double =>
|
|
cmp_f64(v.as_double().unwrap(), w.as_double().unwrap()),
|
|
AtomClass::SignedInteger =>
|
|
v.as_signed_integer().cmp(&w.as_signed_integer()),
|
|
AtomClass::String =>
|
|
v.as_string().cmp(&w.as_string()),
|
|
AtomClass::ByteString =>
|
|
v.as_bytestring().cmp(&w.as_bytestring()),
|
|
AtomClass::Symbol =>
|
|
v.as_symbol().cmp(&w.as_symbol()),
|
|
},
|
|
ValueClass::Compound(c) => match c {
|
|
CompoundClass::Record =>
|
|
value_cmp(v.label().borrow(), w.label().borrow()).then_with(
|
|
|| iters_cmp::<V, W>(&v.iter(), &w.iter())),
|
|
CompoundClass::Sequence => iters_cmp::<V, W>(&v.iter(), &w.iter()),
|
|
CompoundClass::Set => {
|
|
let s1 = v.iter().collect::<BTreeSet<_>>();
|
|
let s2 = w.iter().collect::<BTreeSet<_>>();
|
|
todo!() // s1.cmp(&s2)
|
|
}
|
|
CompoundClass::Dictionary => {
|
|
let d1 = v.entries().collect::<BTreeMap<_, _>>();
|
|
let d2 = w.entries().collect::<BTreeMap<_, _>>();
|
|
todo!() // d1.cmp(&d2)
|
|
}
|
|
},
|
|
ValueClass::Embedded => v.as_embedded().unwrap().cmp(&w.as_embedded().unwrap()),
|
|
})
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! impl_value_methods {
|
|
({ $($gdecls:tt)* }, $t:ty) => {
|
|
impl< $($gdecls)* > std::fmt::Debug for $t {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
crate::TextWriter::fmt_value(f, &mut crate::DefaultDomainCodec, self)
|
|
.map_err(|_| std::fmt::Error)
|
|
}
|
|
}
|
|
|
|
impl< $($gdecls)* > PartialEq for $t {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
crate::value_eq(self, other)
|
|
}
|
|
}
|
|
|
|
impl< $($gdecls)* > Eq for $t {}
|
|
|
|
impl< $($gdecls)* > PartialOrd for $t {
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
Some(crate::value_cmp(self, other))
|
|
}
|
|
}
|
|
|
|
impl< $($gdecls)* > Ord for $t {
|
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
crate::value_cmp(self, other)
|
|
}
|
|
}
|
|
|
|
impl< $($gdecls)* > std::hash::Hash for $t {
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
crate::value_hash(self, state)
|
|
}
|
|
}
|
|
|
|
};
|
|
}
|