use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::str::FromStr;
use std::sync::Arc;
use regex::Regex;
use cst;
use errors::LoadError;
use util::gensym;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Rules(pub Vec<Clause>);
impl Rules {
pub fn load_from(path: impl AsRef<Path>) -> Result<Rules, LoadError> {
let src = {
let mut f = File::open(path).map_err(LoadError::Io)?;
let mut buf = String::new();
f.read_to_string(&mut buf).map_err(LoadError::Io)?;
buf
};
src.parse().map_err(LoadError::Parse)
}
}
impl FromStr for Rules {
type Err = <cst::Rules as FromStr>::Err;
fn from_str(src: &str) -> Result<Rules, Self::Err> {
let cst = src.parse::<cst::Rules>()?;
let mut atoms = HashSet::new();
Ok(cst.to_ast(&mut atoms))
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Clause(pub Lit, pub Vec<Lit>);
impl Clause {
pub fn freshen(&self) -> Clause {
let Clause(ref head, ref body) = *self;
let mut vars = HashMap::new();
let head = head.freshen_helper(&mut vars);
let body = body
.iter()
.map(|lit| lit.freshen_helper(&mut vars))
.collect::<Vec<_>>();
Clause(head, body)
}
}
impl Display for Clause {
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
let Clause(ref head, ref body) = *self;
write!(fmt, "{}", head)?;
let mut first = true;
for lit in body {
let prefix = if first {
first = false;
" :- "
} else {
", "
};
write!(fmt, "{}{}", prefix, lit)?;
}
write!(fmt, ".")
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Term {
Lit(Lit),
Num(u32),
Var(usize),
}
impl Term {
fn freshen_helper(&self, vars: &mut HashMap<usize, usize>) -> Arc<Term> {
Arc::new(match *self {
Term::Lit(ref l) => Term::Lit(l.freshen_helper(vars)),
Term::Num(n) => Term::Num(n),
Term::Var(v) => Term::Var(*vars.entry(v).or_insert_with(gensym)),
})
}
pub fn gensym() -> Arc<Term> {
Arc::new(Term::Var(gensym()))
}
}
impl Display for Term {
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
match *self {
Term::Lit(ref l) => write!(fmt, "{}", l),
Term::Num(n) => write!(fmt, "{}", n),
Term::Var(v) => write!(fmt, "_{}", v),
}
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Lit(pub Arc<str>, pub Vec<Arc<Term>>);
impl Lit {
fn freshen_helper(&self, vars: &mut HashMap<usize, usize>) -> Lit {
let Lit(ref name, ref args) = *self;
let args = args
.iter()
.map(|a| a.freshen_helper(vars))
.collect::<Vec<_>>();
Lit(name.clone(), args)
}
pub fn functor(&self) -> (Arc<str>, usize) {
(self.0.clone(), self.1.len())
}
pub fn functor_b(&self) -> (&str, usize) {
(&*self.0, self.1.len())
}
}
impl Display for Lit {
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
lazy_static! {
static ref UNQUOTED_ATOM: Regex = Regex::new("[a-z.][A-Za-z_.]*").unwrap();
}
let Lit(ref name, ref args) = *self;
if UNQUOTED_ATOM.is_match(name) {
write!(fmt, "{}", name)?;
} else if name.contains('\'') {
write!(fmt, "\"{}\"", name)?;
} else {
write!(fmt, "'{}'", name)?;
}
if !args.is_empty() {
let mut first = true;
for arg in args {
let prefix = if first {
first = false;
"("
} else {
", "
};
write!(fmt, "{}{}", prefix, arg)?;
}
write!(fmt, ")")?;
}
Ok(())
}
}