This commit is contained in:
2026-02-08 22:48:51 +01:00
parent ced43b76bf
commit 134ebc6dc6
11 changed files with 1278 additions and 1 deletions

183
src/prover/constraints.rs Normal file
View File

@ -0,0 +1,183 @@
use std::fmt::Display;
use litemap::LiteMap;
use winnow::Str;
use crate::ast::Predicate;
use crate::ast::Variable;
use crate::prover::constraints;
#[derive(Clone, Debug)]
pub struct Constraints
{
set: LiteMap<Variable, Predicate>,
}
impl Constraints
{
pub fn none() -> Self
{
Constraints {
set: LiteMap::new(),
}
}
pub fn with(variable: Variable, predicate: Predicate) -> Self
{
let mut c = Constraints::none();
c.set.insert(variable, predicate);
c
}
pub fn try_append(&mut self, variable: &Variable, predicate: &Predicate) -> bool
{
if let Some(other_predicate) = self.set.get(variable)
{
// If variable is already contrained, we need to check if both contraints are
// contradictory
// Try to unify both predicates
let unification = predicate.matches(other_predicate);
if let Some(unification_contraints) = unification
{
// Instead of adding the variable = predicates contraint,
// We can try adding the unification contraints which is implicitely the same
if self.try_merge(&unification_contraints)
{
self.set.insert(variable.clone(), predicate.clone());
true
}
else
{
false
}
}
else
{
// Unification is impossible, variable = predicates is contradictory under self
false
}
}
else
{
// No constraint
self.set.insert(variable.clone(), predicate.clone());
true
}
}
pub fn try_merge(&mut self, constraints: &Constraints) -> bool
{
// Trying to merge, is just trying to add all of the constraints into self
let mut ok = self.clone();
for (var, pred) in constraints.set.iter()
{
if !ok.try_append(var, pred)
{
return false;
}
}
*self = ok;
true
}
pub fn and(&self, constraints: &Constraints) -> Option<Constraints>
{
let mut new_contraints = self.clone();
let valid = new_contraints.try_merge(constraints);
if valid { Some(new_contraints) } else { None }
}
pub fn simplified(&self) -> Constraints
{
let mut max_sub = Constraints::none();
for (var, pred) in self.set.iter()
{
max_sub.set.insert(var.clone(), pred.substitute(self));
}
let mut stripped = max_sub.clone();
'outer: for (var, _) in max_sub.set.iter()
{
if var.chars().next().is_some_and(|x| x == '_')
{
for (_, other_pred) in max_sub.set.iter()
{
if other_pred.contains_variable(var)
{
continue 'outer;
}
}
stripped.set.remove(var);
}
}
stripped
}
}
impl Predicate
{
pub fn substitute(&self, constraints: &Constraints) -> Predicate
{
match self
{
Predicate::Variable(name) =>
{
if let Some(pred) = constraints.set.get(name)
{
let max_sub = pred.substitute(constraints);
max_sub
}
else
{
Predicate::Variable(name.clone())
}
}
Predicate::Fixed(name, predicates) => Predicate::Fixed(
name.clone(),
predicates
.iter()
.map(|x| x.substitute(constraints))
.collect(),
),
}
}
pub fn contains_variable(&self, name: &String) -> bool
{
match self
{
Predicate::Variable(var_name) => name == var_name,
Predicate::Fixed(_, predicates) => predicates
.iter()
.find(|x| x.contains_variable(name))
.is_some(),
}
}
}
impl Default for Constraints
{
fn default() -> Self
{
Self::none()
}
}
impl Display for Constraints
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{
let len = self.set.len();
for (i, (var, pred)) in self.set.iter().enumerate()
{
write!(f, "{} = {}", var, pred)?;
if i != len - 1
{
write!(f, ", ")?;
}
}
Ok(())
}
}

0
src/prover/trace.rs Normal file
View File

65
src/prover/unification.rs Normal file
View File

@ -0,0 +1,65 @@
use std::process::Output;
use log::debug;
use crate::ast::Predicate;
use crate::prover::constraints::Constraints;
impl Predicate
{
/// Checks if `self` can be unified with `other`
///
/// returns `Some(constraints)` if the unification is valid under `constraints`
/// returns `None` if the predicate cannot be unified against the other
pub fn matches(&self, other: &Predicate) -> Option<Constraints>
{
debug!("Unifying {} against {}", self, other);
match self
{
Predicate::Variable(variable) =>
{
// We are trying to see if X (any) matches the other Predicate.
// This is always true if X = other_predicate
Some(Constraints::with(variable.clone(), other.clone()))
}
Predicate::Fixed(name, arguments) =>
{
match other
{
Predicate::Variable(var) =>
{
// We are trying to see if something like "predicate(..., ...)" matches X
// (any)
// This is always true
Some(Constraints::with(var.clone(), self.clone()))
}
// Match pred(X, Y, Z, ...) with pred(_X, _Y, _Z, ...)
Predicate::Fixed(other_name, other_arguments)
if other_name == name && other_arguments.len() == arguments.len() =>
{
// If there is no arguments, no constraints
if arguments.is_empty()
{
return Some(Constraints::none());
}
// We unify with other only if our arguments unify with theirs
arguments
.iter()
.zip(other_arguments.iter())
.map(|(ours, theirs)| ours.matches(theirs))
.reduce(|a, b| match (a, b)
{
(Some(c1), Some(c2)) => c1.and(&c2),
_ => None,
})
.unwrap_or(None)
}
_ => None,
}
}
}
}
}