pub mod physical;
use std::collections::HashMap;
use std::hash::Hash;
use std::num::NonZeroU32;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct InstId(pub NonZeroU32);
impl InstId {
pub fn new(index: usize) -> Self {
InstId(NonZeroU32::new((index + 1) as u32).expect("index overflow"))
}
pub fn index(&self) -> usize {
(self.0.get() - 1) as usize
}
}
pub trait InternKey: Copy + Eq + Hash {
fn new(index: usize) -> Self;
fn index(&self) -> usize;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct NameId(pub NonZeroU32);
impl InternKey for NameId {
fn new(index: usize) -> Self {
NameId(NonZeroU32::new((index + 1) as u32).expect("index overflow"))
}
fn index(&self) -> usize {
(self.0.get() - 1) as usize
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct StringId(pub NonZeroU32);
impl InternKey for StringId {
fn new(index: usize) -> Self {
StringId(NonZeroU32::new((index + 1) as u32).expect("index overflow"))
}
fn index(&self) -> usize {
(self.0.get() - 1) as usize
}
}
#[derive(Debug)]
pub struct Store<K: InternKey> {
map: HashMap<String, K>,
vec: Vec<String>,
}
impl<K: InternKey> Default for Store<K> {
fn default() -> Self {
Self {
map: HashMap::default(),
vec: Vec::new(),
}
}
}
impl<K: InternKey> Store<K> {
pub fn new() -> Self {
Self::default()
}
pub fn intern(&mut self, name: String) -> K {
if let Some(&id) = self.map.get(&name) {
return id;
}
let id = K::new(self.vec.len());
self.vec.push(name.clone());
self.map.insert(name, id);
id
}
pub fn get(&self, id: K) -> &str {
&self.vec[id.index()]
}
pub fn lookup(&self, name: &str) -> Option<K> {
self.map.get(name).copied()
}
pub fn values(&self) -> &[String] {
&self.vec
}
}
#[derive(Default, Debug)]
pub struct Ir {
pub insts: Vec<Inst>,
pub name_store: Store<NameId>,
pub string_store: Store<StringId>,
pub temporal_predicates: std::collections::HashSet<NameId>,
}
impl Ir {
pub fn new() -> Self {
Self::default()
}
pub fn add_inst(&mut self, inst: Inst) -> InstId {
let id = InstId::new(self.insts.len());
self.insts.push(inst);
id
}
pub fn get(&self, id: InstId) -> &Inst {
&self.insts[id.index()]
}
pub fn intern_name(&mut self, name: impl Into<String>) -> NameId {
self.name_store.intern(name.into())
}
pub fn resolve_name(&self, id: NameId) -> &str {
self.name_store.get(id)
}
pub fn intern_string(&mut self, s: impl Into<String>) -> StringId {
self.string_store.intern(s.into())
}
pub fn resolve_string(&self, id: StringId) -> &str {
self.string_store.get(id)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Inst {
Bool(bool),
Number(i64),
Float(f64),
String(StringId),
Bytes(Vec<u8>),
Name(NameId),
Time(i64),
Duration(i64),
List(Vec<InstId>),
Map {
keys: Vec<InstId>,
values: Vec<InstId>,
},
Struct {
fields: Vec<NameId>,
values: Vec<InstId>,
},
Var(NameId),
ApplyFn {
function: NameId,
args: Vec<InstId>,
},
Atom {
predicate: NameId,
args: Vec<InstId>,
},
NegAtom(InstId),
Eq(InstId, InstId),
Ineq(InstId, InstId),
Transform {
var: Option<NameId>,
app: InstId,
},
Rule {
head: InstId, premises: Vec<InstId>, transform: Vec<InstId>, },
Decl {
atom: InstId,
descr: Vec<InstId>, bounds: Vec<InstId>, constraints: Option<InstId>, },
BoundDecl {
base_terms: Vec<InstId>,
},
Constraints {
consequences: Vec<InstId>, alternatives: Vec<Vec<InstId>>, },
}
#[cfg(test)]
mod compat_test;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_ir_construction() {
let mut ir = Ir::new();
let x_name = ir.intern_name("X");
let var_x = ir.add_inst(Inst::Var(x_name));
let p_name = ir.intern_name("p");
let atom_head = ir.add_inst(Inst::Atom {
predicate: p_name,
args: vec![var_x],
});
let q_name = ir.intern_name("q");
let atom_body = ir.add_inst(Inst::Atom {
predicate: q_name,
args: vec![var_x],
});
let rule = ir.add_inst(Inst::Rule {
head: atom_head,
premises: vec![atom_body],
transform: vec![],
});
assert_eq!(ir.insts.len(), 4);
if let Inst::Rule { head, .. } = ir.get(rule) {
assert_eq!(*head, atom_head);
} else {
panic!("Expected Rule");
}
}
#[test]
fn complex_types() {
let mut ir = Ir::new();
let n_string = ir.intern_name("/string");
let s_string = ir.add_inst(Inst::Name(n_string));
let n_a = ir.intern_name("/a");
let s_a = ir.add_inst(Inst::Name(n_a));
let fn_opt_name = ir.intern_name("fn:opt");
let fn_opt = ir.add_inst(Inst::ApplyFn {
function: fn_opt_name,
args: vec![s_a, s_string],
});
let fn_struct_name = ir.intern_name("fn:Struct");
let fn_struct = ir.add_inst(Inst::ApplyFn {
function: fn_struct_name,
args: vec![fn_opt],
});
let fn_pair_name = ir.intern_name("fn:Pair");
let fn_pair = ir.add_inst(Inst::ApplyFn {
function: fn_pair_name,
args: vec![s_string, fn_struct],
});
if let Inst::ApplyFn { function, args } = ir.get(fn_pair) {
assert_eq!(ir.resolve_name(*function), "fn:Pair");
assert_eq!(args.len(), 2);
assert_eq!(args[0], s_string);
assert_eq!(args[1], fn_struct);
} else {
panic!("Expected ApplyFn");
}
}
}