use crate::domain::DomainCtx;
use crate::error::CompileError;
use crate::ir::{AtomKey, Origin, Value};
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt::Write as _;
use elenchus_parser::{Body, Conn, ListOp, Literal, Quant, kw};
#[derive(Clone)]
pub(crate) struct RawLit {
pub(crate) key: AtomKey,
pub(crate) negated: bool,
}
pub(crate) struct RawFact {
pub(crate) key: AtomKey,
pub(crate) value: Value,
pub(crate) origin: Origin,
pub(crate) soft: bool,
}
pub(crate) struct RawClause {
pub(crate) lits: Vec<RawLit>,
pub(crate) origin: Origin,
}
pub(crate) struct RawRule {
pub(crate) antecedent: Vec<RawLit>,
pub(crate) consequent: Vec<RawLit>,
pub(crate) origin: Origin,
}
pub(crate) fn quant_sig(q: &Quant) -> String {
match q {
Quant::InSet { binder, set } => alloc::format!("|FOREACH {} IN {}", binder.data, set.data),
Quant::Relation {
left,
predicate,
right,
} => alloc::format!("|FOREACH {} {} {}", left.data, predicate.data, right.data),
}
}
pub(crate) fn raw_lits(
lits: &[elenchus_parser::Located<Literal>],
ctx: &DomainCtx,
) -> Result<Vec<RawLit>, CompileError> {
lits.iter()
.map(|l| {
Ok(RawLit {
key: ctx.key(&l.data.atom)?,
negated: l.data.negated,
})
})
.collect()
}
pub(crate) fn list_kind(op: ListOp) -> &'static str {
match op {
ListOp::Exclusive => kw::EXCLUSIVE,
ListOp::Forbids => kw::FORBIDS,
ListOp::OneOf => kw::ONEOF,
ListOp::AtLeast => kw::ATLEAST,
}
}
pub(crate) fn key_sig(k: &AtomKey) -> String {
alloc::format!(
"{}|{}|{}|{}",
k.domain,
k.subject,
k.predicate.as_deref().unwrap_or(""),
k.object.as_deref().unwrap_or("")
)
}
pub(crate) fn clause_sig(lits: &[RawLit]) -> String {
let mut parts: Vec<String> = lits
.iter()
.map(|l| alloc::format!("{}|{}", key_sig(&l.key), l.negated as u8))
.collect();
parts.sort();
parts.dedup();
parts.join(";")
}
pub(crate) fn canonical_body(
name: &str,
body: &Body,
is_rule: bool,
ctx: &DomainCtx,
) -> Result<String, CompileError> {
let mut s = String::new();
let _ = write!(
s,
"{}|{}|",
if is_rule { kw::RULE } else { kw::PREMISE },
name
);
match body {
Body::List { op, atoms } => {
let _ = write!(s, "LIST|{}|", list_kind(*op));
let mut keys: Vec<String> = atoms
.iter()
.map(|a| Ok(key_sig(&ctx.key(&a.data)?)))
.collect::<Result<_, CompileError>>()?;
keys.sort();
s.push_str(&keys.join(";"));
}
Body::Impl {
antecedent,
ante_conn,
consequent,
cons_conn,
} => {
let conn = |c: &Conn| if *c == Conn::Or { "OR" } else { "AND" };
s.push_str("IMPL|ANTE|");
s.push_str(conn(ante_conn));
s.push('|');
s.push_str(&lit_sigs(antecedent, ctx)?);
s.push_str("|CONS|");
s.push_str(conn(cons_conn));
s.push('|');
s.push_str(&lit_sigs(consequent, ctx)?);
}
Body::Exists { binder, set, atom } => {
let _ = write!(s, "EXISTS|{}|{}|", binder.data, set.data);
s.push_str(&key_sig(&ctx.key(&atom.data)?));
}
}
Ok(s)
}
pub(crate) fn lit_sigs(
lits: &[elenchus_parser::Located<Literal>],
ctx: &DomainCtx,
) -> Result<String, CompileError> {
let mut parts: Vec<String> = lits
.iter()
.map(|l| {
Ok(alloc::format!(
"{}|{}",
key_sig(&ctx.key(&l.data.atom)?),
l.data.negated as u8
))
})
.collect::<Result<_, CompileError>>()?;
parts.sort();
Ok(parts.join(";"))
}