use alloc::string::String;
use alloc::vec::Vec;
use core::fmt::{Debug, Display};
use core::hash::Hash;
use core::{convert::TryFrom, fmt::Write};
use crate::{
datum_error, DatumAtom, DatumMayContainAtom, DatumOffset, DatumPipe, DatumResult, DatumToken,
DatumTokenType, DatumWriter,
};
#[derive(Clone, PartialEq, PartialOrd, Debug)]
pub enum DatumValue {
Atom(DatumAtom<String>),
List(Vec<DatumValue>),
}
impl Default for DatumValue {
#[cfg(not(tarpaulin_include))]
#[inline]
fn default() -> Self {
Self::Atom(DatumAtom::Nil)
}
}
impl Hash for DatumValue {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
match self {
Self::Atom(atm) => {
atm.hash(state);
}
Self::List(vec) => {
state.write_u8(6);
vec.hash(state);
}
}
}
}
impl DatumValue {
pub fn write_to(&self, f: &mut dyn Write, writer: &mut DatumWriter) -> core::fmt::Result {
match self {
DatumValue::Atom(v) => writer.write_atom(f, v),
DatumValue::List(v) => {
let ls: DatumToken<&str> = DatumToken::ListStart(0);
let le: DatumToken<&str> = DatumToken::ListEnd(0);
writer.write_token(f, &ls)?;
for e in v {
e.write_to(f, writer)?;
}
writer.write_token(f, &le)
}
}
}
pub fn as_list(&self) -> Option<&Vec<DatumValue>> {
match self {
DatumValue::List(list) => Some(list),
_ => None,
}
}
}
impl Display for DatumValue {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.write_to(f, &mut DatumWriter::default())
}
}
impl DatumMayContainAtom<String> for DatumValue {
fn as_atom(&self) -> Option<&DatumAtom<String>> {
if let DatumValue::Atom(a) = self {
Some(a)
} else {
None
}
}
}
#[derive(Clone, Debug, Default)]
pub struct DatumParser {
start: DatumOffset,
stack: Vec<Vec<DatumValue>>,
}
impl DatumPipe for DatumParser {
type Input = DatumToken<String>;
type Output = DatumValue;
fn feed<F: FnMut(DatumOffset, DatumValue) -> DatumResult<()>>(
&mut self,
at: DatumOffset,
token: Option<Self::Input>,
f: &mut F,
) -> DatumResult<()> {
if token.is_none() {
return if !self.stack.is_empty() {
Err(datum_error!(Interrupted, at, "eof inside list"))
} else {
Ok(())
};
}
let token = token.unwrap();
if self.stack.is_empty() {
self.start = at;
}
match token.token_type() {
DatumTokenType::ListStart => {
let list = Vec::new();
self.stack.push(list);
Ok(())
}
DatumTokenType::ListEnd => {
let res = self.stack.pop();
if let Some(v) = res {
self.feed_value(DatumValue::List(v), f)
} else {
Err(datum_error!(BadData, at, "end of list while not in list"))
}
}
_ => match DatumAtom::try_from(token) {
Err(e) => Err(e),
Ok(v) => self.feed_value(DatumValue::Atom(v), f),
},
}
}
}
impl DatumParser {
fn feed_value<F: FnMut(DatumOffset, DatumValue) -> DatumResult<()>>(
&mut self,
v: DatumValue,
f: &mut F,
) -> DatumResult<()> {
match self.stack.pop() {
None => f(self.start, v),
Some(mut list) => {
list.push(v);
self.stack.push(list);
Ok(())
}
}
}
}