1use std::collections::{HashMap, HashSet};
2use std::fmt::{Display, Formatter, Result as FmtResult};
3use std::fs::File;
4use std::io::Read;
5use std::path::Path;
6use std::str::FromStr;
7use std::sync::Arc;
8
9use regex::Regex;
10
11use cst;
12use errors::LoadError;
13use util::gensym;
14
15#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
17pub struct Rules(pub Vec<Clause>);
18
19impl Rules {
20 pub fn load_from(path: impl AsRef<Path>) -> Result<Rules, LoadError> {
22 let src = {
23 let mut f = File::open(path).map_err(LoadError::Io)?;
24 let mut buf = String::new();
25 f.read_to_string(&mut buf).map_err(LoadError::Io)?;
26 buf
27 };
28 src.parse().map_err(LoadError::Parse)
29 }
30}
31
32impl FromStr for Rules {
33 type Err = <cst::Rules as FromStr>::Err;
34 fn from_str(src: &str) -> Result<Rules, Self::Err> {
35 let cst = src.parse::<cst::Rules>()?;
36 let mut atoms = HashSet::new();
37 Ok(cst.to_ast(&mut atoms))
38 }
39}
40
41#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
43pub struct Clause(pub Lit, pub Vec<Lit>);
44
45impl Clause {
46 pub fn freshen(&self) -> Clause {
48 let Clause(ref head, ref body) = *self;
49 let mut vars = HashMap::new();
50 let head = head.freshen_helper(&mut vars);
51 let body = body
52 .iter()
53 .map(|lit| lit.freshen_helper(&mut vars))
54 .collect::<Vec<_>>();
55 Clause(head, body)
56 }
57}
58
59impl Display for Clause {
60 fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
61 let Clause(ref head, ref body) = *self;
62
63 write!(fmt, "{}", head)?;
64 let mut first = true;
65 for lit in body {
66 let prefix = if first {
67 first = false;
68 " :- "
69 } else {
70 ", "
71 };
72 write!(fmt, "{}{}", prefix, lit)?;
73 }
74 write!(fmt, ".")
75 }
76}
77
78#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
80pub enum Term {
81 Lit(Lit),
83
84 Num(u32),
86
87 Var(usize),
90}
91
92impl Term {
93 fn freshen_helper(&self, vars: &mut HashMap<usize, usize>) -> Arc<Term> {
94 Arc::new(match *self {
95 Term::Lit(ref l) => Term::Lit(l.freshen_helper(vars)),
96 Term::Num(n) => Term::Num(n),
97 Term::Var(v) => Term::Var(*vars.entry(v).or_insert_with(gensym)),
98 })
99 }
100
101 pub fn gensym() -> Arc<Term> {
103 Arc::new(Term::Var(gensym()))
104 }
105}
106
107impl Display for Term {
108 fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
109 match *self {
110 Term::Lit(ref l) => write!(fmt, "{}", l),
111 Term::Num(n) => write!(fmt, "{}", n),
112 Term::Var(v) => write!(fmt, "_{}", v),
113 }
114 }
115}
116
117#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
119pub struct Lit(pub Arc<str>, pub Vec<Arc<Term>>);
120
121impl Lit {
122 fn freshen_helper(&self, vars: &mut HashMap<usize, usize>) -> Lit {
123 let Lit(ref name, ref args) = *self;
124 let args = args
125 .iter()
126 .map(|a| a.freshen_helper(vars))
127 .collect::<Vec<_>>();
128 Lit(name.clone(), args)
129 }
130
131 pub fn functor(&self) -> (Arc<str>, usize) {
133 (self.0.clone(), self.1.len())
134 }
135
136 pub fn functor_b(&self) -> (&str, usize) {
138 (&*self.0, self.1.len())
139 }
140}
141
142impl Display for Lit {
143 fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
144 lazy_static! {
145 static ref UNQUOTED_ATOM: Regex = Regex::new("[a-z.][A-Za-z_.]*").unwrap();
146 }
147
148 let Lit(ref name, ref args) = *self;
149 if UNQUOTED_ATOM.is_match(name) {
150 write!(fmt, "{}", name)?;
151 } else if name.contains('\'') {
152 write!(fmt, "\"{}\"", name)?;
153 } else {
154 write!(fmt, "'{}'", name)?;
155 }
156
157 if !args.is_empty() {
158 let mut first = true;
159 for arg in args {
160 let prefix = if first {
161 first = false;
162 "("
163 } else {
164 ", "
165 };
166 write!(fmt, "{}{}", prefix, arg)?;
167 }
168 write!(fmt, ")")?;
169 }
170
171 Ok(())
172 }
173}