use crate::{Arena, Handle, Term, TermError};
use core::fmt;
use std::cmp::Ordering;
impl fmt::Debug for View<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
View::Int(i) => f.debug_tuple("Int").field(&i).finish(),
View::Real(r) => f.debug_tuple("Real").field(&r).finish(),
View::Date(d) => f.debug_tuple("Date").field(&d).finish(),
View::Var(v) => f.debug_tuple("Var").field(&v).finish(),
View::Atom(a) => f.debug_tuple("Atom").field(&a).finish(),
View::Str(s) => f.debug_tuple("Str").field(&s).finish(),
View::Bin(b) => f.debug_tuple("Bin").field(&b).finish(),
View::Func(a, fr, ts) => f
.debug_tuple("Func")
.field(&a.arena_id)
.field(&fr)
.field(&ts.iter().map(|t| t.view(a)).collect::<Vec<_>>())
.finish(),
View::List(a, ts, tail) => f
.debug_tuple("List")
.field(&a.arena_id)
.field(&ts.iter().map(|t| t.view(a)).collect::<Vec<_>>())
.field(&tail.view(a))
.finish(),
View::Tuple(a, ts) => f
.debug_tuple("Tuple")
.field(&a.arena_id)
.field(&ts.iter().map(|t| t.view(a)).collect::<Vec<_>>())
.finish(),
}
}
}
#[derive(Clone, Copy)]
pub enum View<'a> {
Int(i64),
Real(f64),
Date(i64),
Var(&'a str),
Atom(&'a str),
Str(&'a str),
Bin(&'a [u8]),
Func(&'a Arena, &'a Term, &'a [Term]),
List(&'a Arena, &'a [Term], &'a Term),
Tuple(&'a Arena, &'a [Term]),
}
impl Term {
#[inline]
pub fn view<'a>(&'a self, arena: &'a Arena) -> Result<View<'a>, TermError> {
match &self.0 {
Handle::Int(i) => Ok(View::Int(*i)),
Handle::Real(f) => Ok(View::Real(*f)),
Handle::Date(d) => Ok(View::Date(*d)),
Handle::Var(vs) => {
let s_bytes = &vs.bytes[..vs.len as usize];
let s = unsafe { core::str::from_utf8_unchecked(s_bytes) };
Ok(View::Var(s))
}
Handle::VarRef(vr) => Ok(View::Var(unsafe {
core::str::from_utf8_unchecked(
arena
.byte_slice(vr)
.map_err(|_| TermError::InvalidTerm(*self))?,
)
})),
Handle::Atom(a) => {
let s_bytes = &a.bytes[..a.len as usize];
let s = unsafe { core::str::from_utf8_unchecked(s_bytes) };
Ok(View::Atom(s))
}
Handle::AtomRef(ar) => Ok(View::Atom(unsafe {
core::str::from_utf8_unchecked(
arena
.byte_slice(ar)
.map_err(|_| TermError::InvalidTerm(*self))?,
)
})),
Handle::Str(ss) => {
let s_bytes = &ss.bytes[..ss.len as usize];
let s = unsafe { core::str::from_utf8_unchecked(s_bytes) };
Ok(View::Str(s))
}
Handle::StrRef(sr) => Ok(View::Str(unsafe {
core::str::from_utf8_unchecked(
arena
.byte_slice(sr)
.map_err(|_| TermError::InvalidTerm(*self))?,
)
})),
Handle::Bin(bs) => {
let b = &bs.bytes[..bs.len as usize];
Ok(View::Bin(b))
}
Handle::BinRef(br) => Ok(View::Bin(
arena
.byte_slice(br)
.map_err(|_| TermError::InvalidTerm(*self))?,
)),
Handle::FuncRef(fr) => {
let slice = arena
.term_slice(fr)
.map_err(|_| TermError::InvalidTerm(*self))?;
let functor = &slice[0];
let args = &slice[1..];
Ok(View::Func(arena, functor, args))
}
Handle::ListRef(lr) => {
let slice = arena
.term_slice(lr)
.map_err(|_| TermError::InvalidTerm(*self))?;
Ok(View::List(arena, slice, &Term::NIL))
}
Handle::ListCRef(lr) => {
let slice = arena
.term_slice(lr)
.map_err(|_| TermError::InvalidTerm(*self))?;
let last = slice.len() - 1;
Ok(View::List(arena, &slice[..last], &slice[last]))
}
Handle::TupleRef(tr) => {
let slice = arena
.term_slice(tr)
.map_err(|_| TermError::InvalidTerm(*self))?;
Ok(View::Tuple(arena, slice))
}
}
}
}
impl Arena {
#[inline]
pub fn view<'a>(&'a self, term: &'a Term) -> Result<View<'a>, TermError> {
term.view(self)
}
}
impl<'a> PartialEq for View<'a> {
fn eq(&self, other: &Self) -> bool {
let order_a = kind_order(self);
let order_b = kind_order(other);
if order_a != order_b {
return false;
}
match (self, other) {
(
View::Int(_) | View::Real(_) | View::Date(_),
View::Int(_) | View::Real(_) | View::Date(_),
) => {
let a = numeric_value(self);
let b = numeric_value(other);
a == b
}
(View::Var(a), View::Var(b)) => a == b,
(View::Atom(a), View::Atom(b)) => a == b,
(View::Str(a), View::Str(b)) => a == b,
(View::Bin(a), View::Bin(b)) => a == b,
(View::Func(arena_a, functor_a, args_a), View::Func(arena_b, functor_b, args_b)) => {
if args_a.len() != args_b.len() {
return false;
}
if functor_a != functor_b {
return false;
}
args_a.iter().zip(args_b.iter()).all(|(a, b)| {
a.view(arena_a).expect("arena mismatch")
== b.view(arena_b).expect("arena mismatch")
})
}
(View::List(arena_a, args_a, tail_a), View::List(arena_b, args_b, tail_b)) => {
if args_a.len() != args_b.len() {
return false;
}
args_a.iter().zip(args_b.iter()).all(|(a, b)| {
a.view(arena_a).expect("arena mismatch")
== b.view(arena_b).expect("arena mismatch")
}) && tail_a.view(arena_a).expect("arena mismatch")
== tail_b.view(arena_b).expect("arena mismatch")
}
(View::Tuple(arena_a, args_a), View::Tuple(arena_b, args_b)) => {
if args_a.len() != args_b.len() {
return false;
}
args_a.iter().zip(args_b.iter()).all(|(a, b)| {
a.view(arena_a).expect("arena mismatch")
== b.view(arena_b).expect("arena mismatch")
})
}
_ => unreachable!(),
}
}
}
impl<'a> Eq for View<'a> {}
impl core::cmp::PartialOrd for View<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl core::cmp::Ord for View<'_> {
fn cmp(&self, other: &Self) -> Ordering {
let order_a = kind_order(self);
let order_b = kind_order(other);
if order_a != order_b {
return order_a.cmp(&order_b);
}
match (self, other) {
(
View::Int(_) | View::Real(_) | View::Date(_),
View::Int(_) | View::Real(_) | View::Date(_),
) => {
let a = numeric_value(self);
let b = numeric_value(other);
a.total_cmp(&b)
}
(View::Var(a), View::Var(b)) => a.cmp(b),
(View::Atom(a), View::Atom(b)) => a.cmp(b),
(View::Str(a), View::Str(b)) => a.cmp(b),
(View::Bin(a), View::Bin(b)) => a.cmp(b),
(View::Func(arena_a, functor_a, args_a), View::Func(arena_b, functor_b, args_b)) => {
let ord = args_a.len().cmp(&args_b.len());
if ord != Ordering::Equal {
return ord;
}
let ord = functor_a
.view(arena_a)
.expect("arena mismatch")
.cmp(&functor_b.view(arena_b).expect("arena mismatch"));
if ord != Ordering::Equal {
return ord;
}
for (arg_a, arg_b) in args_a.iter().zip(args_b.iter()).map(|(a, b)| {
(
a.view(arena_a).expect("arena mismatch"),
b.view(arena_b).expect("arena mismatch"),
)
}) {
let ord = arg_a.cmp(&arg_b);
if ord != Ordering::Equal {
return ord;
}
}
Ordering::Equal
}
(View::List(arena_a, args_a, tail_a), View::List(arena_b, args_b, tail_b)) => {
let ord = args_a.len().cmp(&args_b.len());
if ord != Ordering::Equal {
return ord;
}
for (arg_a, arg_b) in args_a.iter().zip(args_b.iter()).map(|(a, b)| {
(
a.view(arena_a).expect("arena mismatch"),
b.view(arena_b).expect("arena mismatch"),
)
}) {
let ord = arg_a.cmp(&arg_b);
if ord != Ordering::Equal {
return ord;
}
}
tail_a
.view(arena_a)
.expect("arena mismatch")
.cmp(&tail_b.view(arena_b).expect("arena mismatch"))
}
(View::Tuple(arena_a, args_a), View::Tuple(arena_b, args_b)) => {
let ord = args_a.len().cmp(&args_b.len());
if ord != Ordering::Equal {
return ord;
}
for (arg_a, arg_b) in args_a.iter().zip(args_b.iter()).map(|(a, b)| {
(
a.view(arena_a).expect("arena mismatch"),
b.view(arena_b).expect("arena mismatch"),
)
}) {
let ord = arg_a.cmp(&arg_b);
if ord != Ordering::Equal {
return ord;
}
}
Ordering::Equal
}
_ => unreachable!(),
}
}
}
fn kind_order(t: &View) -> u8 {
match t {
View::Var(_) => 0,
View::Int(_) => 1,
View::Date(_) => 2,
View::Real(_) => 3,
View::Atom(_) => 4,
View::Str(_) => 5,
View::Func(_, _, _) => 6,
View::Tuple(_, _) => 7,
View::List(_, _, _) => 8,
View::Bin(_) => 9,
}
}
fn numeric_value(t: &View) -> f64 {
match t {
View::Int(i) => *i as f64,
View::Real(f) => *f,
View::Date(d) => *d as f64,
_ => unreachable!(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn view_size_is_40_bytes() {
assert_eq!(core::mem::size_of::<View>(), 40);
}
#[test]
fn option_view_size_is_40_bytes() {
assert_eq!(core::mem::size_of::<Option<View>>(), 40);
}
}