#[cfg(feature = "mem_dbg")]
use mem_dbg::{MemDbg, MemSize};
use crate::{BoxSlice, Error, FxHashMap, IString, NonMaxU32, Result, StringTable};
use super::{QuantIdx, TermIdx};
#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Term {
pub id: TermId,
kind: TermKindInner,
pub child_ids: BoxSlice<TermIdx>,
}
impl Term {
#[allow(clippy::no_effect)]
pub(crate) const CHECK_SIZE: bool = {
#[allow(dead_code)]
struct SmallTerm {
id: TermId,
kind: TermKind,
child_ids: BoxSlice<TermIdx>,
}
let sizeof_eq = core::mem::size_of::<Term>() == core::mem::size_of::<SmallTerm>();
[(); 1][!sizeof_eq as usize];
true
};
pub fn new<'a>(
id: TermId,
kind: TermKind,
child_ids: BoxSlice<TermIdx>,
t: impl Fn(TermIdx) -> &'a Term,
) -> Self {
let _ = Self::CHECK_SIZE;
let kind = match kind {
TermKind::Var(var) => TermKindInner::Var(var),
TermKind::App(app) => {
fn get_has_var<'a>(
children: &[TermIdx],
t: impl Fn(TermIdx) -> &'a Term,
) -> Option<bool> {
let mut has_var = false;
for &c in children.iter() {
has_var |= t(c).has_var()?;
}
Some(has_var)
}
TermKindInner::App(get_has_var(&child_ids, t), app)
}
TermKind::Quant(quant) => TermKindInner::Quant(quant),
};
Self {
id,
kind,
child_ids,
}
}
pub fn kind(&self) -> TermKind {
use TermKindInner::*;
match self.kind {
Var(.., var) => TermKind::Var(var),
App(.., app) => TermKind::App(app),
Quant(.., quant) => TermKind::Quant(quant),
}
}
pub fn var_idx(&self) -> Option<NonMaxU32> {
self.kind().var()
}
pub fn app_name(&self) -> Option<IString> {
self.kind().app()
}
pub fn quant_idx(&self) -> Option<QuantIdx> {
self.kind().quant()
}
pub fn has_var(&self) -> Option<bool> {
match self.kind {
TermKindInner::Var(_) => Some(true),
TermKindInner::App(has_var, _) => has_var,
TermKindInner::Quant(_) => None,
}
}
pub fn can_match(&self, other: &Self) -> bool {
if self.kind() != other.kind() {
return false;
}
if self.has_var().unwrap_or(true) || other.has_var().unwrap_or(true) {
self.child_ids.len() == other.child_ids.len()
} else {
self.child_ids == other.child_ids
}
}
}
#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
#[cfg_attr(feature = "mem_dbg", copy_type)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum TermKindInner {
Var(NonMaxU32),
App(Option<bool>, IString),
Quant(QuantIdx),
}
#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
#[cfg_attr(feature = "mem_dbg", copy_type)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum TermKind {
Var(NonMaxU32),
App(IString),
Quant(QuantIdx),
}
impl TermKind {
pub(crate) fn parse_var(value: &str) -> Result<TermKind> {
value
.parse::<NonMaxU32>()
.map(TermKind::Var)
.map_err(Error::InvalidVar)
}
fn var(&self) -> Option<NonMaxU32> {
match self {
Self::Var(var) => Some(*var),
_ => None,
}
}
fn app(&self) -> Option<IString> {
match self {
Self::App(name) => Some(*name),
_ => None,
}
}
fn quant(&self) -> Option<QuantIdx> {
match self {
Self::Quant(idx) => Some(*idx),
_ => None,
}
}
}
#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
#[cfg_attr(feature = "mem_dbg", copy_type)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq)]
pub struct TermId {
pub namespace: Option<IString>,
pub id: Option<NonMaxU32>,
}
impl TermId {
pub fn parse(strings: &mut StringTable, value: &str) -> Result<Self> {
let hash_idx = value.bytes().position(|b| b == b'#');
let hash_idx = hash_idx.ok_or_else(|| Error::InvalidIdHash(value.to_string()))?;
let (namespace, id) = value.split_at(hash_idx);
let id = match &id[1..] {
"" => None,
id => Some(id.parse::<NonMaxU32>().map_err(Error::InvalidIdNumber)?),
};
let namespace = (!namespace.is_empty()).then(|| IString(strings.get_or_intern(namespace)));
Ok(Self { namespace, id })
}
pub fn order(&self) -> u32 {
self.id.map(|id| id.get() + 1).unwrap_or_default()
}
pub fn to_string_components<'a>(
&self,
strings: &'a StringTable,
) -> (&'a str, Option<NonMaxU32>) {
let namespace = self.namespace.map(|ns| &strings[*ns]).unwrap_or_default();
(namespace, self.id)
}
pub fn to_string(&self, strings: &StringTable) -> String {
let (namespace, id) = self.to_string_components(strings);
let id = id.map(|id| id.to_string()).unwrap_or_default();
format!("{namespace}#{id}")
}
}
#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
#[derive(Debug)]
pub struct TermIdToIdxMap<K: Copy> {
empty_namespace: Vec<Option<K>>,
namespace_map: FxHashMap<IString, Vec<Option<K>>>,
}
impl<K: Copy> Default for TermIdToIdxMap<K> {
fn default() -> Self {
Self {
empty_namespace: Vec::new(),
namespace_map: FxHashMap::default(),
}
}
}
impl<K: Copy> TermIdToIdxMap<K> {
fn get_vec_mut(&mut self, namespace: Option<IString>) -> Result<&mut Vec<Option<K>>> {
if let Some(namespace) = namespace {
self.namespace_map.try_reserve(1)?;
Ok(self.namespace_map.entry(namespace).or_default())
} else {
Ok(&mut self.empty_namespace)
}
}
pub fn register_term(&mut self, id: TermId, idx: K) -> Result<Option<K>> {
let id_idx = id.order() as usize;
let vec = self.get_vec_mut(id.namespace)?;
if id_idx >= vec.len() {
let new_len = id_idx + 1;
vec.try_reserve(new_len - vec.len())?;
vec.resize(new_len, None);
}
let old = vec[id_idx].replace(idx);
Ok(old)
}
fn get_vec(&self, namespace: Option<IString>) -> Option<&Vec<Option<K>>> {
if let Some(namespace) = namespace {
self.namespace_map.get(&namespace)
} else {
Some(&self.empty_namespace)
}
}
pub fn get_term(&self, id: &TermId) -> Option<K> {
self.get_vec(id.namespace)
.and_then(|vec| vec.get(id.order() as usize).and_then(|x| x.as_ref()))
.copied()
}
}