use alloc::vec;
use alloc::vec::Vec;
use crate::ast::node::{
AppData, AstNode, DeclInfo, FuncDeclData, FuncDeclFlags, QuantifierData, QuantifierKind,
SortData, VarData,
};
use crate::ast::{AstId, AstKind, FamilyId, SortSize};
use crate::util::hash::fnv_hash;
use crate::util::symbol::Symbol;
struct Stored {
node: AstNode,
hash: u32,
}
pub struct AstManager {
nodes: Vec<Stored>,
buckets: Vec<Vec<u32>>,
mask: usize,
families: Vec<Symbol>,
}
impl Default for AstManager {
fn default() -> AstManager {
AstManager::new()
}
}
impl AstManager {
pub fn new() -> AstManager {
const INIT_BUCKETS: usize = 16; AstManager {
nodes: Vec::new(),
buckets: vec![Vec::new(); INIT_BUCKETS],
mask: INIT_BUCKETS - 1,
families: vec![Symbol::new("basic")],
}
}
pub fn mk_family_id(&mut self, name: Symbol) -> FamilyId {
if let Some(fid) = self.get_family_id(name) {
return fid;
}
let fid = self.families.len() as FamilyId;
self.families.push(name);
fid
}
pub fn get_family_id(&self, name: Symbol) -> Option<FamilyId> {
self.families
.iter()
.position(|&s| s == name)
.map(|i| i as FamilyId)
}
pub fn family_name(&self, fid: FamilyId) -> Option<Symbol> {
self.families.get(fid as usize).copied()
}
#[inline]
pub fn len(&self) -> usize {
self.nodes.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
fn intern(&mut self, node: AstNode) -> AstId {
let hash = fnv_hash(&node);
let b = (hash as usize) & self.mask;
for &id in &self.buckets[b] {
let stored = &self.nodes[id as usize];
if stored.hash == hash && stored.node == node {
return AstId(id);
}
}
let id = self.nodes.len() as u32;
self.nodes.push(Stored { node, hash });
self.buckets[b].push(id);
if self.nodes.len() > self.buckets.len() * 2 {
self.grow();
}
AstId(id)
}
fn grow(&mut self) {
let new_len = self.buckets.len() * 2;
let new_mask = new_len - 1;
let mut buckets: Vec<Vec<u32>> = vec![Vec::new(); new_len];
for (id, stored) in self.nodes.iter().enumerate() {
buckets[(stored.hash as usize) & new_mask].push(id as u32);
}
self.buckets = buckets;
self.mask = new_mask;
}
pub fn mk_sort(&mut self, name: Symbol, info: DeclInfo, num_elements: SortSize) -> AstId {
self.intern(AstNode::Sort(SortData {
name,
info,
num_elements,
}))
}
pub fn mk_uninterpreted_sort(&mut self, name: Symbol) -> AstId {
self.mk_sort(name, DeclInfo::null(), SortSize::Infinite)
}
pub fn mk_func_decl(&mut self, name: Symbol, domain: &[AstId], range: AstId) -> AstId {
self.mk_func_decl_full(
name,
domain,
range,
DeclInfo::null(),
FuncDeclFlags::default(),
)
}
pub fn mk_func_decl_full(
&mut self,
name: Symbol,
domain: &[AstId],
range: AstId,
info: DeclInfo,
flags: FuncDeclFlags,
) -> AstId {
debug_assert!(domain.iter().all(|&s| self.is_sort(s)));
debug_assert!(self.is_sort(range));
self.intern(AstNode::FuncDecl(FuncDeclData {
name,
info,
flags,
domain: domain.to_vec(),
range,
}))
}
pub fn mk_app(&mut self, decl: AstId, args: &[AstId]) -> AstId {
debug_assert!(
self.is_func_decl(decl),
"mk_app: decl is not a function declaration"
);
debug_assert_eq!(
self.func_decl(decl).unwrap().arity(),
args.len(),
"mk_app: arity mismatch"
);
self.intern(AstNode::App(AppData {
decl,
args: args.to_vec(),
}))
}
#[inline]
pub fn mk_const(&mut self, decl: AstId) -> AstId {
self.mk_app(decl, &[])
}
pub fn mk_var(&mut self, index: u32, sort: AstId) -> AstId {
debug_assert!(self.is_sort(sort));
self.intern(AstNode::Var(VarData { index, sort }))
}
pub fn mk_quantifier(
&mut self,
kind: QuantifierKind,
var_sorts: &[AstId],
var_names: &[Symbol],
body: AstId,
patterns: &[AstId],
weight: i32,
) -> AstId {
debug_assert_eq!(var_sorts.len(), var_names.len());
debug_assert!(
!var_sorts.is_empty(),
"quantifier needs at least one binder"
);
let sort = match kind {
QuantifierKind::Forall | QuantifierKind::Exists => self.mk_bool_sort(),
QuantifierKind::Lambda => {
let mut s = self.get_sort(body);
for &vs in var_sorts.iter().rev() {
s = self.mk_array_sort(vs, s);
}
s
}
};
self.intern(AstNode::Quantifier(QuantifierData {
kind,
var_sorts: var_sorts.to_vec(),
var_names: var_names.to_vec(),
body,
patterns: patterns.to_vec(),
weight,
sort,
}))
}
pub fn mk_forall(&mut self, var_sorts: &[AstId], body: AstId) -> AstId {
let names = default_binder_names(var_sorts.len());
self.mk_quantifier(QuantifierKind::Forall, var_sorts, &names, body, &[], 0)
}
pub fn mk_exists(&mut self, var_sorts: &[AstId], body: AstId) -> AstId {
let names = default_binder_names(var_sorts.len());
self.mk_quantifier(QuantifierKind::Exists, var_sorts, &names, body, &[], 0)
}
#[inline]
pub fn node(&self, id: AstId) -> &AstNode {
&self.nodes[id.0 as usize].node
}
#[inline]
pub fn kind(&self, id: AstId) -> AstKind {
self.node(id).kind()
}
#[inline]
pub fn is_sort(&self, id: AstId) -> bool {
matches!(self.node(id), AstNode::Sort(_))
}
#[inline]
pub fn is_func_decl(&self, id: AstId) -> bool {
matches!(self.node(id), AstNode::FuncDecl(_))
}
#[inline]
pub fn is_app(&self, id: AstId) -> bool {
matches!(self.node(id), AstNode::App(_))
}
#[inline]
pub fn is_var(&self, id: AstId) -> bool {
matches!(self.node(id), AstNode::Var(_))
}
pub fn sort(&self, id: AstId) -> Option<&SortData> {
match self.node(id) {
AstNode::Sort(s) => Some(s),
_ => None,
}
}
pub fn func_decl(&self, id: AstId) -> Option<&FuncDeclData> {
match self.node(id) {
AstNode::FuncDecl(f) => Some(f),
_ => None,
}
}
pub fn app(&self, id: AstId) -> Option<&AppData> {
match self.node(id) {
AstNode::App(a) => Some(a),
_ => None,
}
}
pub fn var(&self, id: AstId) -> Option<VarData> {
match self.node(id) {
AstNode::Var(v) => Some(*v),
_ => None,
}
}
pub fn app_decl(&self, id: AstId) -> AstId {
self.app(id).expect("app_decl: not an application").decl
}
pub fn app_args(&self, id: AstId) -> &[AstId] {
&self.app(id).expect("app_args: not an application").args
}
pub fn get_sort(&self, expr: AstId) -> AstId {
match self.node(expr) {
AstNode::App(a) => {
self.func_decl(a.decl)
.expect("app decl must be a func_decl")
.range
}
AstNode::Var(v) => v.sort,
AstNode::Quantifier(q) => q.sort,
_ => panic!("get_sort: node is not an expression"),
}
}
pub fn quantifier(&self, id: AstId) -> Option<&QuantifierData> {
match self.node(id) {
AstNode::Quantifier(q) => Some(q),
_ => None,
}
}
#[inline]
pub fn is_quantifier(&self, id: AstId) -> bool {
matches!(self.node(id), AstNode::Quantifier(_))
}
}
fn default_binder_names(n: usize) -> alloc::vec::Vec<Symbol> {
(0..n)
.map(|i| Symbol::new(&alloc::format!("x{i}")))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sorts_are_hash_consed() {
let mut m = AstManager::new();
let a1 = m.mk_uninterpreted_sort(Symbol::new("A"));
let a2 = m.mk_uninterpreted_sort(Symbol::new("A"));
let b = m.mk_uninterpreted_sort(Symbol::new("B"));
assert_eq!(a1, a2, "identical sorts share one id");
assert_ne!(a1, b);
assert_eq!(m.len(), 2);
assert!(m.is_sort(a1));
assert_eq!(m.sort(a1).unwrap().name, Symbol::new("A"));
}
#[test]
fn applications_dedupe_and_carry_sorts() {
let mut m = AstManager::new();
let a = m.mk_uninterpreted_sort(Symbol::new("A"));
let f = m.mk_func_decl(Symbol::new("f"), &[a], a);
let x_decl = m.mk_func_decl(Symbol::new("x"), &[], a);
let x = m.mk_const(x_decl);
let fx1 = m.mk_app(f, &[x]);
let fx2 = m.mk_app(f, &[x]);
assert_eq!(fx1, fx2);
let ffx = m.mk_app(f, &[fx1]);
assert_ne!(fx1, ffx);
assert_eq!(m.get_sort(x), a);
assert_eq!(m.get_sort(fx1), a);
assert_eq!(m.app_args(fx1), &[x]);
assert_eq!(m.app_decl(fx1), f);
}
#[test]
fn variables_hash_cons_by_index_and_sort() {
let mut m = AstManager::new();
let a = m.mk_uninterpreted_sort(Symbol::new("A"));
let b = m.mk_uninterpreted_sort(Symbol::new("B"));
let v0a = m.mk_var(0, a);
let v0a2 = m.mk_var(0, a);
let v0b = m.mk_var(0, b);
let v1a = m.mk_var(1, a);
assert_eq!(v0a, v0a2);
assert_ne!(v0a, v0b);
assert_ne!(v0a, v1a);
assert_eq!(m.get_sort(v0a), a);
assert_eq!(m.var(v0a).unwrap().index, 0);
}
#[test]
fn hash_cons_survives_table_growth() {
let mut m = AstManager::new();
let a = m.mk_uninterpreted_sort(Symbol::new("A"));
let mut decls = alloc::vec::Vec::new();
for i in 0..200 {
let name = alloc::format!("c{i}");
decls.push(m.mk_func_decl(Symbol::new(&name), &[], a));
}
let n_after = m.len();
let again = m.mk_func_decl(Symbol::new("c0"), &[], a);
assert_eq!(again, decls[0]);
assert_eq!(m.len(), n_after);
}
}