#[derive(Debug, Clone)]
pub struct Program {
pub clauses: Vec<Clause>,
pub query: Option<Vec<Goal>>,
}
impl Program {
pub fn new() -> Self {
Self {
clauses: Vec::new(),
query: None,
}
}
}
impl Default for Program {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct Clause {
pub head: Term,
pub body: Vec<Goal>,
}
impl Clause {
pub fn fact(head: Term) -> Self {
Self {
head,
body: Vec::new(),
}
}
pub fn rule(head: Term, body: Vec<Goal>) -> Self {
Self { head, body }
}
pub fn is_fact(&self) -> bool {
self.body.is_empty()
}
pub fn predicate_key(&self) -> (String, usize) {
match &self.head {
Term::Atom(name) => (name.clone(), 0),
Term::Compound { functor, args } => (functor.clone(), args.len()),
_ => ("_".to_string(), 0),
}
}
}
#[derive(Debug, Clone)]
pub enum Goal {
Call(Term),
Is(String, ArithExpr),
Compare(CompareOp, ArithExpr, ArithExpr),
Unify(Term, Term),
NotUnify(Term, Term),
Cut,
Write(Term),
Nl,
Fail,
True,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Term {
Integer(i32),
Atom(String),
Variable(String),
Anonymous,
Compound { functor: String, args: Vec<Term> },
Cons(Box<Term>, Box<Term>),
Nil,
}
impl Term {
pub fn compound(functor: impl Into<String>, args: Vec<Term>) -> Self {
Term::Compound {
functor: functor.into(),
args,
}
}
pub fn atom(name: impl Into<String>) -> Self {
Term::Atom(name.into())
}
pub fn var(name: impl Into<String>) -> Self {
Term::Variable(name.into())
}
pub fn int(n: i32) -> Self {
Term::Integer(n)
}
pub fn list(terms: Vec<Term>) -> Self {
let mut result = Term::Nil;
for term in terms.into_iter().rev() {
result = Term::Cons(Box::new(term), Box::new(result));
}
result
}
pub fn list_with_tail(heads: Vec<Term>, tail: Term) -> Self {
let mut result = tail;
for term in heads.into_iter().rev() {
result = Term::Cons(Box::new(term), Box::new(result));
}
result
}
pub fn is_variable(&self) -> bool {
matches!(self, Term::Variable(_) | Term::Anonymous)
}
pub fn is_ground(&self) -> bool {
match self {
Term::Integer(_) | Term::Atom(_) | Term::Nil => true,
Term::Variable(_) | Term::Anonymous => false,
Term::Compound { args, .. } => args.iter().all(|a| a.is_ground()),
Term::Cons(h, t) => h.is_ground() && t.is_ground(),
}
}
pub fn functor_arity(&self) -> Option<(&str, usize)> {
match self {
Term::Atom(name) => Some((name.as_str(), 0)),
Term::Compound { functor, args } => Some((functor.as_str(), args.len())),
Term::Cons(_, _) => Some((".", 2)),
Term::Nil => Some(("[]", 0)),
_ => None,
}
}
pub fn variables(&self) -> Vec<String> {
let mut vars = Vec::new();
self.collect_variables(&mut vars);
vars
}
fn collect_variables(&self, vars: &mut Vec<String>) {
match self {
Term::Variable(name) => {
if !vars.contains(name) {
vars.push(name.clone());
}
}
Term::Compound { args, .. } => {
for arg in args {
arg.collect_variables(vars);
}
}
Term::Cons(h, t) => {
h.collect_variables(vars);
t.collect_variables(vars);
}
_ => {}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ArithExpr {
Integer(i32),
Variable(String),
BinOp(ArithOp, Box<ArithExpr>, Box<ArithExpr>),
Neg(Box<ArithExpr>),
}
impl ArithExpr {
pub fn variables(&self) -> Vec<String> {
let mut vars = Vec::new();
self.collect_variables(&mut vars);
vars
}
fn collect_variables(&self, vars: &mut Vec<String>) {
match self {
ArithExpr::Variable(name) => {
if !vars.contains(name) {
vars.push(name.clone());
}
}
ArithExpr::BinOp(_, left, right) => {
left.collect_variables(vars);
right.collect_variables(vars);
}
ArithExpr::Neg(inner) => inner.collect_variables(vars),
ArithExpr::Integer(_) => {}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArithOp {
Add,
Sub,
Mul,
Div,
Mod,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompareOp {
Lt,
Gt,
Le,
Ge,
ArithEq,
ArithNe,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PredicateKey {
pub name: String,
pub arity: usize,
}
impl PredicateKey {
pub fn new(name: impl Into<String>, arity: usize) -> Self {
Self {
name: name.into(),
arity,
}
}
}
impl std::fmt::Display for PredicateKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}/{}", self.name, self.arity)
}
}