diff --git a/implementations/rust/oo/src/lib.rs b/implementations/rust/oo/src/lib.rs index 09fcad1..46f3d0e 100644 --- a/implementations/rust/oo/src/lib.rs +++ b/implementations/rust/oo/src/lib.rs @@ -8,7 +8,7 @@ pub mod reader; pub mod repr; pub mod signed_integer; pub mod source; -// pub mod text; +pub mod text; pub mod types; pub mod writer; @@ -26,6 +26,7 @@ pub use repr::Bytes; pub use repr::Embedded; pub use repr::IOValue; pub use repr::Map; +pub use repr::PlainValue; pub use repr::NoValue; pub use repr::Record; pub use repr::Set; @@ -37,16 +38,17 @@ pub use signed_integer::SignedInteger; pub use source::BinarySource; pub use source::BytesBinarySource; pub use source::IOBinarySource; -// pub use text::TextReader; -// pub use text::TextWriter; -// pub use text::annotated_from_str; -// pub use text::annotated_iovalue_from_str; -// pub use text::from_str; -// pub use text::iovalue_from_str; +pub use text::TextReader; +pub use text::TextWriter; +pub use text::annotated_from_str; +pub use text::annotated_iovalue_from_str; +pub use text::from_str; +pub use text::iovalue_from_str; pub use types::AtomClass; pub use types::CompoundClass; pub use types::ValueClass; pub use writer::Writer; +pub use writer::write_value; #[cfg(test)] mod demo { @@ -57,16 +59,17 @@ mod demo { } #[test] fn a() { - let l = Symbol::new("label"); - let r = Record::new(owned::(l.clone()), vec![owned(1), owned(2), owned(3)]); - let r2 = Record::new(owned::(l), vec![owned(1), owned(2), owned(4)]); + let l: PlainValue = "label".parse().unwrap(); + let r = Record::new(l.value_clone(), vec![owned(1), owned(2), owned(3)]); + let r2 = Record::new(l, vec![owned(1), owned(2), owned(4)]); - let mut v: Map>, String> = Map::new(); - v.insert(Box::new("abc"), "def".to_owned()); - v.insert(Box::new(123), "xyz".to_owned()); - v.insert(Box::new(vec![1, 2, 3]), "www".to_owned()); - v.insert(Box::new(r2), "bbb".to_owned()); - v.insert(Box::new(r), "aaa".to_owned()); + let mut v: Map, PlainValue> = Map::new(); + v.insert("\"abc\"".parse().unwrap(), "def".parse().unwrap()); + v.insert("abc".parse().unwrap(), "DEF".parse().unwrap()); + v.insert(owned(123), "xyz".parse().unwrap()); + v.insert(owned(vec![1, 2, 3]), "{a: 1, b: 2}".parse().unwrap()); + v.insert(owned(r2), "bbb".parse().unwrap()); + v.insert(owned(r), "".parse().unwrap()); let w: &dyn Value = &v; println!("GETw abc {:?}", w.get(&"abc")); println!("GETw 123 {:?}", w.get(&123)); @@ -75,15 +78,16 @@ mod demo { println!("GETv 123 {:?}", v.get(value(&123))); println!("GETv qqq {:?}", v.get(value(&"qqq"))); for (kk, vv) in w.entries() { - println!("{:?} {:?} ==> {:?} {:?}", kk.value_class(), kk, vv.value_class(), vv); + println!("{:#?} ==> {:#?}", kk, vv); } // { // use std::io::BufRead; // for line in std::io::stdin().lock().lines() { // let line = line.unwrap(); - // let val = w.get(&line); - // println!("{:?} ==> {:?} == {:?}", line, val, getit(&v, &line)); + // let key = line.parse::>().unwrap(); + // let val = w.get(&key); + // println!("{:?} == {:?} ==> {:?} == {:?}", line, &key, val, getit(&v, &line)); // } // } } diff --git a/implementations/rust/oo/src/repr.rs b/implementations/rust/oo/src/repr.rs index aaeb4c9..cd8dd14 100644 --- a/implementations/rust/oo/src/repr.rs +++ b/implementations/rust/oo/src/repr.rs @@ -6,13 +6,14 @@ use std::fmt::Debug; use std::hash::{Hash, Hasher}; use std::io; use std::marker::PhantomData; +use std::str::FromStr; use std::sync::Arc; use std::vec::Vec; pub use std::collections::BTreeSet as Set; pub use std::collections::BTreeMap as Map; -use crate::AtomClass; +use crate::{AtomClass, TextWriter, DefaultDomainCodec, write_value}; use crate::CompoundClass; use crate::Domain; use crate::SignedInteger; @@ -23,11 +24,15 @@ use crate::domain::{NoEmbeddedDomainCodec, DomainEncode, IOValueDomainCodec}; use super::float::{eq_f32, eq_f64, cmp_f32, cmp_f64}; -/// Atomic values from the specification. -pub trait Value: Debug { - fn write(&self, w: &mut dyn Writer, enc: &mut dyn DomainEncode) -> io::Result<()>; +pub type PlainValue<'a, D = IOValue> = Box + 'a>; - fn value_clone(&self) -> Box> where D: 'static; +/// Atomic values from the specification. +pub trait Value { + fn write(&self, w: &mut dyn Writer, enc: &mut dyn DomainEncode) -> io::Result<()> { + write_value(w, self, enc) + } + + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static; fn value_class(&self) -> ValueClass; fn as_boolean(&self) -> Option { None } @@ -66,7 +71,7 @@ pub fn value>(v: &V) -> &dyn Value { v } -pub fn owned + 'static>(v: V) -> Box> { +pub fn owned + 'static>(v: V) -> PlainValue<'static, D> { Box::new(v) } @@ -74,9 +79,15 @@ pub fn iovalue + 'static>(v: V) -> IOValue { IOValue(Arc::new(v)) } +impl<'a, D: Domain + 'static> From<&'a dyn Value> for PlainValue<'static, D> { + fn from(v: &'a dyn Value) -> Self { + v.value_clone() + } +} + impl<'a, D: Domain, V: Value + ?Sized> Value for &'a V { fn write(&self, w: &mut dyn Writer, enc: &mut dyn DomainEncode) -> io::Result<()> { (*self).write(w, enc) } - fn value_clone(&self) -> Box> where D: 'static { (*self).value_clone() } + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { (*self).value_clone() } fn value_class(&self) -> ValueClass { (*self).value_class() } fn as_boolean(&self) -> Option { (*self).as_boolean() } fn as_float(&self) -> Option { (*self).as_float() } @@ -102,9 +113,9 @@ impl<'a, D: Domain, V: Value + ?Sized> Value for &'a V { fn annotations(&self) -> Option<&[IOValue]> { (*self).annotations() } } -impl Value for Box> { +impl<'a, D: Domain> Value for PlainValue<'a, D> { fn write(&self, w: &mut dyn Writer, enc: &mut dyn DomainEncode) -> io::Result<()> { self.as_ref().write(w, enc) } - fn value_clone(&self) -> Box> where D: 'static { self.as_ref().value_clone() } + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { self.as_ref().value_clone() } fn value_class(&self) -> ValueClass { self.as_ref().value_class() } fn as_boolean(&self) -> Option { self.as_ref().as_boolean() } fn as_float(&self) -> Option { self.as_ref().as_float() } @@ -130,6 +141,20 @@ impl Value for Box> { fn annotations(&self) -> Option<&[IOValue]> { self.as_ref().annotations() } } +impl<'a, D: Domain> Debug for dyn Value + 'a { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + TextWriter::fmt_value(f, &mut DefaultDomainCodec, self).map_err(|_| std::fmt::Error) + } +} + +impl<'a, Err: Into, D: Domain + FromStr + 'static> FromStr for PlainValue<'a, D> { + type Err = io::Error; + + fn from_str(s: &str) -> Result { + crate::from_str(s, &mut DefaultDomainCodec) + } +} + impl<'a, D: Domain> Hash for dyn Value + 'a { fn hash(&self, state: &mut H) { match self.value_class() { @@ -308,7 +333,7 @@ pub enum Atom<'a> { } impl<'a> Atom<'a> { - pub fn into_value(self) -> Box> { + pub fn into_value(self) -> PlainValue<'static, D> { match self { Atom::Boolean(b) => Box::new(b), Atom::Float(f) => Box::new(f), @@ -321,6 +346,20 @@ impl<'a> Atom<'a> { } } +impl<'r, 'a> From<&'r Atom<'a>> for AtomClass { + fn from(a: &'r Atom<'a>) -> Self { + match a { + Atom::Boolean(_) => AtomClass::Boolean, + Atom::Float(_) => AtomClass::Float, + Atom::Double(_) => AtomClass::Double, + Atom::SignedInteger(_) => AtomClass::SignedInteger, + Atom::String(_) => AtomClass::String, + Atom::ByteString(_) => AtomClass::ByteString, + Atom::Symbol(_) => AtomClass::Symbol, + } + } +} + impl<'a, D: Domain> Value for Atom<'a> { fn write(&self, w: &mut dyn Writer, _enc: &mut dyn DomainEncode) -> io::Result<()> { match self { @@ -334,20 +373,12 @@ impl<'a, D: Domain> Value for Atom<'a> { } } - fn value_clone(&self) -> Box> where D: 'static { + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { self.clone().into_value() } fn value_class(&self) -> ValueClass { - ValueClass::Atomic(match self { - Atom::Boolean(_) => AtomClass::Boolean, - Atom::Float(_) => AtomClass::Float, - Atom::Double(_) => AtomClass::Double, - Atom::SignedInteger(_) => AtomClass::SignedInteger, - Atom::String(_) => AtomClass::String, - Atom::ByteString(_) => AtomClass::ByteString, - Atom::Symbol(_) => AtomClass::Symbol, - }) + ValueClass::Atomic(self.into()) } fn as_boolean(&self) -> Option { @@ -388,22 +419,30 @@ impl Domain for NoValue { type Encode = NoEmbeddedDomainCodec; } +impl FromStr for NoValue { + type Err = io::Error; + + fn from_str(_s: &str) -> Result { + Err(io::Error::new(io::ErrorKind::Unsupported, "Embedded values not supported here")) + } +} + impl Value for NoValue { fn write(&self, _w: &mut dyn Writer, _enc: &mut dyn DomainEncode) -> io::Result<()> { unreachable!() } - fn value_clone(&self) -> Box> where D: 'static { unreachable!() } + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { unreachable!() } fn value_class(&self) -> ValueClass { unreachable!() } } impl Value for bool { fn write(&self, w: &mut dyn Writer, __enc: &mut dyn DomainEncode) -> io::Result<()> { w.write_bool(*self) } - fn value_clone(&self) -> Box> where D: 'static { Box::new(*self) } + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { Box::new(*self) } fn value_class(&self) -> ValueClass { ValueClass::Atomic(AtomClass::Boolean) } fn as_boolean(&self) -> Option { Some(*self) } } impl Value for u64 { fn write(&self, w: &mut dyn Writer, _enc: &mut dyn DomainEncode) -> io::Result<()> { w.write_u64(*self) } - fn value_clone(&self) -> Box> where D: 'static { Box::new(*self) } + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { Box::new(*self) } fn value_class(&self) -> ValueClass { ValueClass::Atomic(AtomClass::SignedInteger) } fn as_signed_integer(&self) -> Option { Some((*self).into()) @@ -412,7 +451,7 @@ impl Value for u64 { impl Value for SignedInteger { fn write(&self, w: &mut dyn Writer, _enc: &mut dyn DomainEncode) -> io::Result<()> { w.write_signed_integer(self) } - fn value_clone(&self) -> Box> where D: 'static { Box::new(self.clone()) } + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { Box::new(self.clone()) } fn value_class(&self) -> ValueClass { ValueClass::Atomic(AtomClass::SignedInteger) } fn as_signed_integer(&self) -> Option { Some(self.clone()) @@ -421,7 +460,7 @@ impl Value for SignedInteger { impl Value for f32 { fn write(&self, w: &mut dyn Writer, _enc: &mut dyn DomainEncode) -> io::Result<()> { w.write_f32(*self) } - fn value_clone(&self) -> Box> where D: 'static { Box::new(*self) } + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { Box::new(*self) } fn value_class(&self) -> ValueClass { ValueClass::Atomic(AtomClass::Float) } fn as_float(&self) -> Option { Some(*self) } fn as_double(&self) -> Option { Some(*self as f64) } @@ -429,7 +468,7 @@ impl Value for f32 { impl Value for f64 { fn write(&self, w: &mut dyn Writer, _enc: &mut dyn DomainEncode) -> io::Result<()> { w.write_f64(*self) } - fn value_clone(&self) -> Box> where D: 'static { Box::new(*self) } + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { Box::new(*self) } fn value_class(&self) -> ValueClass { ValueClass::Atomic(AtomClass::Float) } fn as_float(&self) -> Option { Some(*self as f32) } fn as_double(&self) -> Option { Some(*self) } @@ -437,14 +476,14 @@ impl Value for f64 { impl Value for str { fn write(&self, w: &mut dyn Writer, _enc: &mut dyn DomainEncode) -> io::Result<()> { w.write_string(self) } - fn value_clone(&self) -> Box> where D: 'static { Box::new(self.to_owned()) } + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { Box::new(self.to_owned()) } fn value_class(&self) -> ValueClass { ValueClass::Atomic(AtomClass::String) } fn as_string(&self) -> Option> { Some(Cow::Borrowed(self)) } } impl Value for String { fn write(&self, w: &mut dyn Writer, _enc: &mut dyn DomainEncode) -> io::Result<()> { w.write_string(self) } - fn value_clone(&self) -> Box> where D: 'static { Box::new(self.clone()) } + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { Box::new(self.clone()) } fn value_class(&self) -> ValueClass { ValueClass::Atomic(AtomClass::String) } fn as_string(&self) -> Option> { Some(Cow::Borrowed(self)) } } @@ -455,24 +494,24 @@ pub struct Bytes>(T); impl + Debug, D: Domain> Value for Bytes { fn write(&self, w: &mut dyn Writer, _enc: &mut dyn DomainEncode) -> io::Result<()> { w.write_bytes(self.0.as_ref()) } - fn value_clone(&self) -> Box> where D: 'static { Box::new(Bytes(self.0.as_ref().to_owned())) } + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { Box::new(Bytes(self.0.as_ref().to_owned())) } fn value_class(&self) -> ValueClass { ValueClass::Atomic(AtomClass::ByteString) } fn as_bytestring(&self) -> Option> { Some(Cow::Borrowed(self.0.as_ref())) } } #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] #[repr(transparent)] -pub struct Symbol + Debug>(T); +pub struct Symbol + Debug = String>(T); impl + Debug> Symbol { - pub fn new(t: T) -> Self { - Symbol(t) + pub fn new>(t: R) -> Self { + Symbol(t.into()) } } impl + Debug, D: Domain> Value for Symbol { fn write(&self, w: &mut dyn Writer, _enc: &mut dyn DomainEncode) -> io::Result<()> { w.write_symbol(self.0.as_ref()) } - fn value_clone(&self) -> Box> where D: 'static { Box::new(Symbol(self.0.as_ref().to_owned())) } + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { Box::new(Symbol(self.0.as_ref().to_owned())) } fn value_class(&self) -> ValueClass { ValueClass::Atomic(AtomClass::Symbol) } fn as_symbol(&self) -> Option> { Some(Cow::Borrowed(self.0.as_ref())) } } @@ -513,7 +552,7 @@ impl> Value for Record { w.end_record() } - fn value_clone(&self) -> Box> where D: 'static { + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { Box::new(Record(self.0.iter().map(|v| v.value_clone()).collect())) } @@ -532,7 +571,7 @@ impl> Value for Vec { (&self[..]).write(w, enc) } - fn value_clone(&self) -> Box> where D: 'static { + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { (&self[..]).value_clone() } @@ -559,7 +598,7 @@ impl> Value for [V] { w.end_sequence() } - fn value_clone(&self) -> Box> where D: 'static { + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { Box::new(self.iter().map(|v| v.value_clone()).collect::>()) } @@ -572,7 +611,7 @@ impl> Value for [V] { } } -impl<'e, D: Domain, E: for<'a> Borrow> + Debug + Ord + 'e> Value for Set { +impl<'e, D: Domain, E: for<'a> Borrow> + Ord + 'e> Value for Set { fn write(&self, w: &mut dyn Writer, enc: &mut dyn DomainEncode) -> io::Result<()> { w.start_set()?; let mut b = B::Type::default(); @@ -586,7 +625,7 @@ impl<'e, D: Domain, E: for<'a> Borrow> + Debug + Ord + 'e> Value f w.end_set() } - fn value_clone(&self) -> Box> where D: 'static { + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { Box::new(self.iter().map(|v| Key::peel_ref(&v.borrow()).value_clone()).collect::>()) } @@ -620,7 +659,7 @@ impl<'a, 'b: 'a, D: Domain> Borrow> for &'b (dyn Value + 'b) { } } -impl<'k, D: Domain, V: Value, K: for<'a> Borrow> + Debug + Ord + 'k> Value +impl<'k, D: Domain, V: Value, K: for<'a> Borrow> + Ord + 'k> Value for Map { fn write(&self, w: &mut dyn Writer, enc: &mut dyn DomainEncode) -> io::Result<()> { @@ -639,7 +678,7 @@ impl<'k, D: Domain, V: Value, K: for<'a> Borrow> + Debug + Ord + ' w.end_dictionary() } - fn value_clone(&self) -> Box> where D: 'static { + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { Box::new(Value::entries(self).map(|(k, v)| (k.value_clone(), v.value_clone())).collect::>()) } @@ -663,8 +702,8 @@ impl<'k, D: Domain, V: Value, K: for<'a> Borrow> + Debug + Ord + ' pub struct Embedded(D); impl Embedded { - pub fn new(d: D) -> Self { - Embedded(d) + pub fn new>(d: E) -> Self { + Embedded(d.into()) } pub fn embedded_value(&self) -> &D { @@ -683,7 +722,7 @@ impl Value for Embedded { w.end_embedded() } - fn value_clone(&self) -> Box> where D: 'static { Box::new(self.clone()) } + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { Box::new(self.clone()) } fn value_class(&self) -> ValueClass { ValueClass::Embedded } fn is_embedded(&self) -> bool { true } fn embedded(&self) -> Cow<'_, D> { Cow::Borrowed(&self.0) } @@ -723,7 +762,7 @@ impl> Value for Annotations { } } - fn value_clone(&self) -> Box> where D: 'static { + fn value_clone(&self) -> PlainValue<'static, D> where D: 'static { Box::new(Annotations(self.0.value_clone(), self.1.iter().map(|v| v.value_clone().into()).collect(), PhantomData)) @@ -780,17 +819,31 @@ impl> Ord for Annotations { } } -#[derive(Debug, Clone, Eq, Hash, PartialOrd, Ord)] +#[derive(Clone, Eq, Hash, PartialOrd, Ord)] pub struct IOValue(Arc>); +impl Debug for IOValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + value(self).fmt(f) + } +} + +impl FromStr for IOValue { + type Err = io::Error; + + fn from_str(s: &str) -> Result { + crate::iovalue_from_str(s) + } +} + impl PartialEq for IOValue { fn eq(&self, other: &Self) -> bool { &self.0 == &other.0 } } -impl From>> for IOValue { - fn from(b: Box>) -> Self { +impl From> for IOValue { + fn from(b: PlainValue<'static, IOValue>) -> Self { IOValue(Arc::from(b)) } } @@ -803,7 +856,7 @@ impl<'a> Borrow> for IOValue { impl Value for IOValue { fn write(&self, w: &mut dyn Writer, enc: &mut dyn DomainEncode) -> io::Result<()> { self.0.write(w, enc) } - fn value_clone(&self) -> Box> { Box::new(self.clone()) } + fn value_clone(&self) -> PlainValue<'static, IOValue> { Box::new(self.clone()) } fn value_class(&self) -> ValueClass { self.0.value_class() } fn as_boolean(&self) -> Option { self.0.as_boolean() } fn as_float(&self) -> Option { self.0.as_float() } diff --git a/implementations/rust/oo/src/source.rs b/implementations/rust/oo/src/source.rs index 86f2301..2fe3762 100644 --- a/implementations/rust/oo/src/source.rs +++ b/implementations/rust/oo/src/source.rs @@ -31,9 +31,9 @@ pub trait BinarySource<'de>: Sized { super::PackedReader::new(self) } - // fn text(&mut self) -> super::TextReader<'de, '_, Self> { - // super::TextReader::new(self) - // } + fn text(&mut self) -> super::TextReader<'de, '_, Self> { + super::TextReader::new(self) + } } #[derive(Debug)] diff --git a/implementations/rust/oo/src/text/mod.rs b/implementations/rust/oo/src/text/mod.rs index 3c66d2d..d4027ed 100644 --- a/implementations/rust/oo/src/text/mod.rs +++ b/implementations/rust/oo/src/text/mod.rs @@ -6,6 +6,7 @@ pub mod writer; pub use reader::TextReader; pub use writer::TextWriter; +use crate::BinarySource; use crate::BytesBinarySource; use crate::Domain; use crate::DomainDecode; diff --git a/implementations/rust/oo/src/text/reader.rs b/implementations/rust/oo/src/text/reader.rs index a22d3ad..cbdef36 100644 --- a/implementations/rust/oo/src/text/reader.rs +++ b/implementations/rust/oo/src/text/reader.rs @@ -1,4 +1,5 @@ use crate::Atom; +use crate::ValueClass; use crate::error::Error; use crate::error::ExpectedKind; use crate::error::io_eof; @@ -6,25 +7,41 @@ use crate::error::io_eof; use crate::hex; use crate::CompoundClass; -use crate::Domain; -use crate::DomainDecode; -use crate::Map; use crate::Reader; -use crate::Record; -use crate::Set; -use crate::Value; use crate::boundary as B; +use crate::reader::NextToken; use crate::reader::ReaderResult; use crate::source::BinarySource; -use num::bigint::BigInt; +use num_bigint::BigInt; +use std::borrow::Cow; use std::io; -use std::iter::FromIterator; use std::marker::PhantomData; +enum Classification { + Atom(Atom<'static>), + Compound(CompoundClass), + Embedded, + CommentAnnotation, + OrdinaryAnnotation, +} + +impl<'r> From<&'r Classification> for NextToken { + fn from(c: &'r Classification) -> Self { + match c { + Classification::Atom(a) => NextToken::Value(ValueClass::Atomic(a.into())), + Classification::Compound(c) => NextToken::Value(ValueClass::Compound(c.clone())), + Classification::Embedded => NextToken::Value(ValueClass::Embedded), + Classification::CommentAnnotation | + Classification::OrdinaryAnnotation => NextToken::Annotation, + } + } +} + pub struct TextReader<'de, 'src, S: BinarySource<'de>> { pub source: &'src mut S, + classification_cache: Option, phantom: PhantomData<&'de ()>, } @@ -33,6 +50,7 @@ impl<'de, 'src, S: BinarySource<'de>> TextReader<'de, 'src, S> pub fn new(source: &'src mut S) -> Self { TextReader { source, + classification_cache: None, phantom: PhantomData, } } @@ -89,7 +107,7 @@ impl<'de, 'src, S: BinarySource<'de>> TextReader<'de, 'src, S> } } - fn read_intpart(&mut self, mut bs: Vec, c: u8) -> io::Result> { + fn read_intpart(&mut self, mut bs: Vec, c: u8) -> io::Result> { match c { b'0' => { bs.push(c); @@ -102,7 +120,7 @@ impl<'de, 'src, S: BinarySource<'de>> TextReader<'de, 'src, S> } } - fn read_fracexp(&mut self, mut bs: Vec) -> io::Result> { + fn read_fracexp(&mut self, mut bs: Vec) -> io::Result> { let mut is_float = false; match self.peek_noeof() { Ok(b'.') => { @@ -122,7 +140,7 @@ impl<'de, 'src, S: BinarySource<'de>> TextReader<'de, 'src, S> } } - fn read_sign_and_exp(&mut self, mut bs: Vec) -> io::Result> { + fn read_sign_and_exp(&mut self, mut bs: Vec) -> io::Result> { match self.peek_noeof()? { b'+' | b'-' => bs.push(self.next_byte()?), _ => (), @@ -132,7 +150,7 @@ impl<'de, 'src, S: BinarySource<'de>> TextReader<'de, 'src, S> self.finish_number(bs, true) } - fn finish_number(&mut self, bs: Vec, is_float: bool) -> io::Result> { + fn finish_number(&mut self, bs: Vec, is_float: bool) -> io::Result> { let s = self.decode_utf8(bs)?; if is_float { match self.peek_noeof() { @@ -150,7 +168,7 @@ impl<'de, 'src, S: BinarySource<'de>> TextReader<'de, 'src, S> } else { Ok(Atom::SignedInteger(s.parse::().map_err( |_| self.syntax_error(&format!( - "Invalid signed-integer number: {:?}", s)))?)) + "Invalid signed-integer number: {:?}", s)))?.into())) } } @@ -251,23 +269,23 @@ impl<'de, 'src, S: BinarySource<'de>> TextReader<'de, 'src, S> self.decode_utf8(raw) } - fn read_literal_binary(&mut self) -> io::Result> { - Ok(Atom::Bytes(&self.read_stringlike( + fn read_literal_binary(&mut self) -> io::Result> { + Ok(Atom::ByteString(Cow::Owned(self.read_stringlike( Vec::new(), |_r, bs, b| Ok(bs.push(b)), b'"', b'x', - |r, bs| Ok(bs.push(r.hexnum(2)? as u8)))?[..])) + |r, bs| Ok(bs.push(r.hexnum(2)? as u8)))?))) } - fn read_hex_binary(&mut self) -> io::Result> { + fn read_hex_binary(&mut self) -> io::Result> { let mut s = String::new(); loop { self.skip_whitespace(); let c1 = self.next_byte()? as char; if c1 == '"' { let bs = hex::HexParser::Strict.decode(&s).unwrap(); - return Ok(Atom::Bytes(&bs[..])); + return Ok(Atom::ByteString(Cow::Owned(bs))); } let c2 = self.next_byte()? as char; if !(c1.is_digit(16) && c2.is_digit(16)) { @@ -278,7 +296,7 @@ impl<'de, 'src, S: BinarySource<'de>> TextReader<'de, 'src, S> } } - fn read_base64_binary(&mut self) -> io::Result> { + fn read_base64_binary(&mut self) -> io::Result> { let mut bs = Vec::new(); loop { self.skip_whitespace(); @@ -286,7 +304,7 @@ impl<'de, 'src, S: BinarySource<'de>> TextReader<'de, 'src, S> if c == b']' { let bs = base64::decode_config(&self.decode_utf8(bs)?, base64::STANDARD_NO_PAD) .map_err(|_| self.syntax_error("Invalid base64 character"))?; - return Ok(Atom::Bytes(&bs[..])); + return Ok(Atom::ByteString(Cow::Owned(bs))); } if c == b'-' { c = b'+'; } if c == b'_' { c = b'/'; } @@ -295,7 +313,7 @@ impl<'de, 'src, S: BinarySource<'de>> TextReader<'de, 'src, S> } } - fn read_raw_symbol(&mut self, mut bs: Vec) -> io::Result> { + fn read_raw_symbol(&mut self, mut bs: Vec) -> io::Result> { loop { let c = match self.peek()? { None => b' ', @@ -305,7 +323,7 @@ impl<'de, 'src, S: BinarySource<'de>> TextReader<'de, 'src, S> match c { b'(' | b')' | b'{' | b'}' | b'[' | b']' | b'<' | b'>' | b'"' | b';' | b',' | b'@' | b'#' | b':' | b'|' | b' ' => - return Ok(Atom::Symbol(&self.decode_utf8(bs)?)), + return Ok(Atom::Symbol(Cow::Owned(self.decode_utf8(bs)?))), c => { self.skip()?; bs.push(c) @@ -313,142 +331,120 @@ impl<'de, 'src, S: BinarySource<'de>> TextReader<'de, 'src, S> } } } + + fn read_classification(&mut self) -> io::Result { + self.skip_whitespace(); + let c = match self.peek()? { + None => Err(io_eof())?, + Some(c) => c, + }; + self.skip()?; + + Ok(match c { + b'-' => { + let c1 = self.next_byte()?; + Classification::Atom(self.read_intpart(vec![b'-'], c1)?) + } + b'0' | b'1' | b'2' | b'3' | b'4' | b'5' | b'6' | b'7' | b'8' | b'9' => { + Classification::Atom(self.read_intpart(Vec::new(), c)?) + } + b'"' => Classification::Atom(Atom::String(Cow::Owned(self.read_string(b'"')?))), + b'|' => Classification::Atom(Atom::Symbol(Cow::Owned(self.read_string(b'|')?))), + b':' => Err(self.syntax_error("Unexpected key/value separator between items"))?, + b';' => Classification::CommentAnnotation, + b'@' => Classification::OrdinaryAnnotation, + b'#' => { + match self.next_byte()? { + b'f' => Classification::Atom(Atom::Boolean(false)), + b't' => Classification::Atom(Atom::Boolean(true)), + b'{' => Classification::Compound(CompoundClass::Set), + b'"' => Classification::Atom(self.read_literal_binary()?), + b'x' => if self.next_byte()? == b'"' { + Classification::Atom(self.read_hex_binary()?) + } else { + Err(self.syntax_error("Expected open-quote at start of hex ByteString"))? + }, + b'[' => Classification::Atom(self.read_base64_binary()?), + b'=' => { + todo!("Remove machine text syntax") + } + b'!' => Classification::Embedded, + other => Err(self.syntax_error(&format!("Invalid # syntax: {:?}", other)))?, + } + } + b'<' => Classification::Compound(CompoundClass::Record), + b'[' => Classification::Compound(CompoundClass::Sequence), + b'{' => Classification::Compound(CompoundClass::Dictionary), + b'>' => Err(self.syntax_error("Unexpected >"))?, + b']' => Err(self.syntax_error("Unexpected ]"))?, + b'}' => Err(self.syntax_error("Unexpected }"))?, + other => Classification::Atom(self.read_raw_symbol(vec![other])?), + }) + } } impl<'de, 'src, S: BinarySource<'de>> Reader<'de> for TextReader<'de, 'src, S> { - fn peek_class(&mut self) -> io::Result> { - self.skip_whitespace(); - let c = match self.peek()? { - None => return Ok(None), - Some(c) => c, - }; -XX Ok(Some(match c { - b'-' => { - self.skip()?; - let c1 = self.next_byte()?; - self.read_intpart(vec![b'-'], c1)? - } - b'0' | b'1' | b'2' | b'3' | b'4' | b'5' | b'6' | b'7' | b'8' | b'9' => { - self.skip()?; - self.read_intpart(Vec::new(), c)? - } - b'"' => { - self.skip()?; - N::new(self.read_string(b'"')?) - } - b'|' => { - self.skip()?; - N::symbol(&self.read_string(b'|')?) - } - b';' | b'@' => { - if read_annotations { - let mut annotations = self.gather_annotations(decode_embedded)?; - let av: N = self.demand_next_domain(read_annotations, decode_embedded)?; - let (existing_annotations, v) = av.pieces(); - if let Some(vs) = existing_annotations { - annotations.extend_from_slice(&vs[..]); - } - N::wrap(Some(Box::new(annotations)), v) - } else { - self.skip_annotations()?; - self.demand_next_domain(read_annotations, decode_embedded)? - } - } - b':' => { - return Err(self.syntax_error("Unexpected key/value separator between items")); - } - b'#' => { - self.skip()?; - match self.next_byte()? { - b'f' => N::new(false), - b't' => N::new(true), - b'{' => N::new(Set::from_iter(self.upto(b'}', read_annotations, decode_embedded)?.into_iter())), - b'"' => self.read_literal_binary()?, - b'x' => if self.next_byte()? == b'"' { - self.read_hex_binary()? - } else { - return Err(self.syntax_error("Expected open-quote at start of hex ByteString")); - }, - b'[' => self.read_base64_binary()?, - b'=' => { - let bs_val = self.next_iovalue(true)?; - if let Some(anns) = bs_val.annotations() { - if anns.len() > 0 { - return Err(self.syntax_error("Annotations not permitted after #=")); - } - } - match bs_val.value().as_bytestring() { - None => - return Err(self.syntax_error("ByteString must follow #=")), - Some(bs) => - crate::value::BytesBinarySource::new(bs) - .packed() - .demand_next_domain(read_annotations, decode_embedded)? - } - } - b'!' => Value::Embedded(decode_embedded.decode_embedded(self, read_annotations)?).wrap(), - other => return Err(self.syntax_error(&format!("Invalid # syntax: {:?}", other))), - } - } - b'<' => { - self.skip()?; - let vs = self.upto(b'>', read_annotations, decode_embedded)?; - if vs.is_empty() { - return Err(self.syntax_error("Missing record label")); - } - Value::Record(Record(vs)).wrap() - } - b'[' => { - self.skip()?; - N::new(self.upto(b']', read_annotations, decode_embedded)?) - } - b'{' => { - self.skip()?; - self.read_dictionary(read_annotations, decode_embedded)? - } - b'>' => return Err(self.syntax_error("Unexpected >")), - b']' => return Err(self.syntax_error("Unexpected ]")), - b'}' => return Err(self.syntax_error("Unexpected }")), - other => { - self.skip()?; - self.read_raw_symbol(vec![other])? - } - })) + fn peek_class(&mut self) -> io::Result> { + if let Some(a) = &self.classification_cache { + Ok(Some(a.into())) + } else { + let a = self.read_classification()?; + let result = (&a).into(); + self.classification_cache = Some(a); + Ok(Some(result)) + } + } + + fn next_atom(&mut self) -> ReaderResult> { + self.skip_annotations()?; + + let a = self.classification_cache.take().map_or_else( + || self.read_classification(), + |c| Ok(c))?; + + match a { + Classification::Atom(a) => Ok(a), + Classification::Compound(_) => Err(self.syntax_error("Unexpected compound value"))?, + Classification::Embedded => Err(self.syntax_error("Unexpected embedded value"))?, + Classification::CommentAnnotation | Classification::OrdinaryAnnotation => + unreachable!("Annotations are supposed to have been skipped already"), + } } fn open_record(&mut self) -> ReaderResult<()> { self.skip_annotations()?; - if self.peek()? != Some(b'<') { return Err(Error::Expected(ExpectedKind::Record)); } - self.skip()?; + if self.peek_class()? != Some(NextToken::Value(ValueClass::Compound(CompoundClass::Record))) { + return Err(Error::Expected(ExpectedKind::Record)); + } + self.classification_cache = None; Ok(()) } fn open_sequence(&mut self) -> ReaderResult<()> { self.skip_annotations()?; - if self.peek()? != Some(b'[') { return Err(Error::Expected(ExpectedKind::Sequence)); } - self.skip()?; + if self.peek_class()? != Some(NextToken::Value(ValueClass::Compound(CompoundClass::Sequence))) { + return Err(Error::Expected(ExpectedKind::Sequence)); + } + self.classification_cache = None; Ok(()) } fn open_set(&mut self) -> ReaderResult<()> { self.skip_annotations()?; - let mark = self.mark()?; - match self.next_byte()? { - b'#' => match self.next_byte()? { - b'{' => return Ok(()), - _ => (), - }, - _ => (), + if self.peek_class()? != Some(NextToken::Value(ValueClass::Compound(CompoundClass::Set))) { + return Err(Error::Expected(ExpectedKind::Set)); } - self.restore(&mark)?; - Err(Error::Expected(ExpectedKind::Set)) + self.classification_cache = None; + Ok(()) } fn open_dictionary(&mut self) -> ReaderResult<()> { self.skip_annotations()?; - if self.peek()? != Some(b'{') { return Err(Error::Expected(ExpectedKind::Dictionary)); } - self.skip()?; + if self.peek_class()? != Some(NextToken::Value(ValueClass::Compound(CompoundClass::Dictionary))) { + return Err(Error::Expected(ExpectedKind::Dictionary)); + } + self.classification_cache = None; Ok(()) } @@ -486,58 +482,47 @@ XX Ok(Some(match c { fn open_embedded(&mut self) -> ReaderResult<()> { self.skip_annotations()?; - let mark = self.mark()?; - match self.next_byte()? { - b'#' => match self.next_byte()? { - b'!' => return Ok(()), - _ => (), - }, - _ => (), + if self.peek_class()? != Some(NextToken::Value(ValueClass::Embedded)) { + return Err(Error::Expected(ExpectedKind::Embedded)); } - self.restore(&mark)?; - Err(Error::Expected(ExpectedKind::Embedded)) + self.classification_cache = None; + Ok(()) } fn close_embedded(&mut self) -> ReaderResult<()> { Ok(()) } - type Mark = S::Mark; - - fn mark(&mut self) -> io::Result { + fn mark(&mut self) -> io::Result { + if self.classification_cache.is_some() { + panic!("Cannot mark with full classification_cache"); + } self.source.mark() } - fn restore(&mut self, mark: &Self::Mark) -> io::Result<()> { + fn restore(&mut self, mark: usize) -> io::Result<()> { + self.classification_cache = None; self.source.restore(mark) } - fn next_token>( - &mut self, - read_embedded_annotations: bool, - decode_embedded: &mut Dec, - ) -> io::Result> { - self.skip_annotations()?; - let mark = self.mark()?; - Ok(match self.next_byte()? { - b'<' => Token::Compound(CompoundClass::Record), - b'[' => Token::Compound(CompoundClass::Sequence), - b'{' => Token::Compound(CompoundClass::Dictionary), - b'>' => Token::End, - b']' => Token::End, - b'}' => Token::End, - b'#' => match self.next_byte()? { - b'!' => Token::Embedded(decode_embedded.decode_embedded(self, read_embedded_annotations)?), - b'{' => Token::Compound(CompoundClass::Set), - _ => { - self.restore(&mark)?; - Token::Atom(self.demand_next_domain(false, decode_embedded)?) - } - }, - _ => { - self.restore(&mark)?; - Token::Atom(self.demand_next_domain(false, decode_embedded)?) + fn open_annotation(&mut self) -> ReaderResult<()> { + let _ = self.peek_class()?; + match self.classification_cache { + None => unreachable!("peek_class should have primed the cache"), + Some(Classification::CommentAnnotation) => { + self.classification_cache = Some(Classification::Atom( + Atom::String(Cow::Owned(self.comment_line()?)))); + Ok(()) } - }) + Some(Classification::OrdinaryAnnotation) => { + self.classification_cache = None; + Ok(()) + } + Some(_) => Err(Error::Expected(ExpectedKind::Annotation))?, + } + } + + fn close_annotation(&mut self) -> ReaderResult<()> { + Ok(()) } } diff --git a/implementations/rust/oo/src/text/writer.rs b/implementations/rust/oo/src/text/writer.rs index b3cd2b1..777020e 100644 --- a/implementations/rust/oo/src/text/writer.rs +++ b/implementations/rust/oo/src/text/writer.rs @@ -1,10 +1,11 @@ -use crate::value::DomainEncode; -use crate::value::IOValue; -use crate::value::IOValueDomainCodec; -use crate::value::NestedValue; -use crate::value::Writer; +use crate::Domain; +use crate::DomainEncode; +use crate::IOValue; +use crate::IOValueDomainCodec; +use crate::Value; +use crate::Writer; -use num::bigint::BigInt; +use num_bigint::BigInt; use std::io; @@ -32,25 +33,25 @@ impl std::default::Default for CommaStyle { } impl TextWriter<&mut Vec> { - pub fn fmt_value>( + pub fn fmt_value>( f: &mut std::fmt::Formatter<'_>, enc: &mut Enc, - v: &crate::value::Value, + v: &dyn Value, ) -> io::Result<()> { let mut buf: Vec = Vec::new(); let mut w = TextWriter::new(&mut buf); if f.alternate() { w.indentation = 4 } - w.write_value(enc, v)?; + v.write(&mut w, enc)?; f.write_str(std::str::from_utf8(&buf).expect("valid UTF-8 from TextWriter")).map_err( |_| io::Error::new(io::ErrorKind::Other, "could not append to Formatter")) } - pub fn encode>( + pub fn encode>( enc: &mut Enc, - v: &N, + v: &dyn Value, ) -> io::Result { let mut buf: Vec = Vec::new(); - TextWriter::new(&mut buf).write(enc, v)?; + v.write(&mut TextWriter::new(&mut buf), enc)?; Ok(String::from_utf8(buf).expect("valid UTF-8 from TextWriter")) } diff --git a/implementations/rust/oo/src/writer.rs b/implementations/rust/oo/src/writer.rs index a86fa30..a863c43 100644 --- a/implementations/rust/oo/src/writer.rs +++ b/implementations/rust/oo/src/writer.rs @@ -1,9 +1,16 @@ use num_bigint::BigInt; use std::io; +use crate::AtomClass; +use crate::CompoundClass; +use crate::Domain; +use crate::DomainEncode; +use crate::IOValueDomainCodec; use crate::SignedInteger; +use crate::ValueClass; use crate::boundary as B; use crate::signed_integer::SignedIntegerRepr; +use crate::Value; pub trait Writer { fn boundary(&mut self, b: &B::Type) -> io::Result<()>; @@ -60,3 +67,103 @@ pub trait Writer { } } } + +pub fn write_value>( + w: &mut dyn Writer, + v: V, + enc: &mut dyn DomainEncode, +) -> io::Result<()> { + let annotations = v.annotations(); + let mut annotation_b = B::Type::default(); + + if let Some(anns) = annotations { + w.start_annotations()?; + for ann in anns { + annotation_b.shift(Some(B::Item::Annotation)); + w.boundary(&annotation_b)?; + ann.write(w, &mut IOValueDomainCodec)?; + } + annotation_b.shift(Some(B::Item::AnnotatedValue)); + w.boundary(&annotation_b)?; + } + + match v.value_class() { + ValueClass::Atomic(a) => match a { + AtomClass::Boolean => w.write_bool(v.as_boolean().unwrap())?, + AtomClass::Float => w.write_f32(v.as_float().unwrap())?, + AtomClass::Double => w.write_f64(v.as_double().unwrap())?, + AtomClass::SignedInteger => w.write_signed_integer(&v.as_signed_integer().unwrap())?, + AtomClass::String => w.write_string(&v.as_string().unwrap())?, + AtomClass::ByteString => w.write_bytes(&v.as_bytestring().unwrap())?, + AtomClass::Symbol => w.write_symbol(&v.as_symbol().unwrap())?, + } + ValueClass::Compound(c) => match c { + CompoundClass::Record => { + w.start_record()?; + let mut b = B::start(B::Item::RecordLabel); + w.boundary(&b)?; + v.label().write(w, enc)?; + for e in v.iter() { + b.shift(Some(B::Item::RecordField)); + w.boundary(&b)?; + e.write(w, enc)?; + } + b.shift(None); + w.boundary(&b)?; + w.end_record()? + } + CompoundClass::Sequence => { + w.start_sequence()?; + let mut b = B::Type::default(); + for e in v.iter() { + b.shift(Some(B::Item::SequenceValue)); + w.boundary(&b)?; + e.write(w, enc)?; + } + b.shift(None); + w.boundary(&b)?; + w.end_sequence()? + } + CompoundClass::Set => { + w.start_set()?; + let mut b = B::Type::default(); + for e in v.iter() { + b.shift(Some(B::Item::SetValue)); + w.boundary(&b)?; + e.write(w, enc)?; + } + b.shift(None); + w.boundary(&b)?; + w.end_set()? + } + CompoundClass::Dictionary => { + w.start_dictionary()?; + let mut b = B::Type::default(); + for (k, v) in v.entries() { + b.shift(Some(B::Item::DictionaryKey)); + w.boundary(&b)?; + k.write(w, enc)?; + b.shift(Some(B::Item::DictionaryValue)); + w.boundary(&b)?; + v.write(w, enc)?; + } + b.shift(None); + w.boundary(&b)?; + w.end_dictionary()? + } + } + ValueClass::Embedded => { + w.start_embedded()?; + enc.encode_embedded(w, &v.embedded())?; + w.end_embedded()? + } + } + + if let Some(_) = annotations { + annotation_b.shift(None); + w.boundary(&annotation_b)?; + w.end_annotations()? + } + + Ok(()) +}