#![warn(
unknown_lints,
// ---------- Stylistic
absolute_paths_not_starting_with_crate,
elided_lifetimes_in_paths,
explicit_outlives_requirements,
macro_use_extern_crate,
nonstandard_style, /* group */
noop_method_call,
rust_2018_idioms,
single_use_lifetimes,
trivial_casts,
trivial_numeric_casts,
// ---------- Future
future_incompatible, /* group */
rust_2021_compatibility, /* group */
// ---------- Public
missing_debug_implementations,
// missing_docs,
unreachable_pub,
// ---------- Unsafe
unsafe_code,
unsafe_op_in_unsafe_fn,
// ---------- Unused
unused, /* group */
)]
#![deny(
// ---------- Public
exported_private_dependencies,
private_in_public,
// ---------- Deprecated
anonymous_parameters,
bare_trait_objects,
ellipsis_inclusive_range_patterns,
// ---------- Unsafe
deref_nullptr,
drop_bounds,
dyn_drop,
)]
use crate::edb::{Attribute, Predicate, PredicateRef, Relation, RelationSet, Schema};
use crate::error::{
extensional_predicate_in_rule_head, language_feature_unsupported, relation_does_not_exist,
Result,
};
use crate::features::{FeatureSet, FEATURE_CONSTRAINTS, FEATURE_DISJUNCTION, FEATURE_NEGATION};
use crate::idb::eval::{strata::PrecedenceGraph, Evaluator};
use crate::idb::query::{Query, QuerySet, Queryable, View};
use crate::idb::{Atom, Literal, Rule, RuleForm, RuleSet, Term, Variable, VariableRef};
use crate::visitor::{make_native_writer, write_program};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::fmt::{Debug, Display, Formatter};
use std::hash::Hash;
use std::path::PathBuf;
use std::rc::Rc;
use std::str::FromStr;
pub const MIME_TYPE_BASE: &str = "text/vnd.datalog";
pub trait ProgramCore {
fn features(&self) -> &FeatureSet;
fn extensional(&self) -> &RelationSet;
fn intensional(&self) -> &RelationSet;
fn rules(&self) -> &RuleSet;
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Program {
from_file: Option<PathBuf>,
features: FeatureSet,
predicate_cache: NameReferenceSet<Predicate>,
variable_cache: NameReferenceSet<Variable>,
extensional: RelationSet,
intensional: RelationSet,
rules: RuleSet,
queries: QuerySet,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NameReferenceSet<T>(RefCell<BTreeMap<String, AttributeNameRef<T>>>)
where
T: AttributeName;
pub trait AttributeName:
AsRef<str> + Clone + Debug + Display + FromStr + PartialEq + Eq + PartialOrd + Ord + Hash
{
fn is_valid(s: &str) -> bool;
fn type_name() -> &'static str;
}
#[allow(type_alias_bounds)]
pub type AttributeNameRef<T: AttributeName> = Rc<T>;
pub trait Collection<T> {
fn is_empty(&self) -> bool;
fn len(&self) -> usize;
fn iter(&self) -> Box<dyn Iterator<Item = &'_ T> + '_>;
fn contains(&self, value: &T) -> bool;
}
pub trait IndexedCollection<K, V>: Collection<V> {
fn get(&self, index: &K) -> Option<&V>;
fn contains_index(&self, index: &K) -> bool;
}
pub trait Labeled {
fn label(&self) -> &Predicate;
fn label_ref(&self) -> PredicateRef;
}
pub trait MaybeAnonymous {
fn anonymous() -> Self
where
Self: Sized;
fn is_anonymous(&self) -> bool;
}
pub trait MaybePositive {
fn is_positive(&self) -> bool;
fn is_negative(&self) -> bool {
!self.is_positive()
}
}
impl Display for Program {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
use std::io::BufWriter;
let mut buffer = BufWriter::new(Vec::new());
let visitor = make_native_writer(&mut buffer);
write_program(self, &visitor).unwrap();
let s = String::from_utf8(buffer.into_inner().unwrap()).unwrap();
write!(f, "{}", s)
}
}
impl MaybePositive for Program {
fn is_positive(&self) -> bool {
self.rules().iter().all(|rule| rule.is_positive())
}
}
impl ProgramCore for Program {
fn features(&self) -> &FeatureSet {
&self.features
}
fn extensional(&self) -> &RelationSet {
&self.extensional
}
fn intensional(&self) -> &RelationSet {
&self.intensional
}
fn rules(&self) -> &RuleSet {
&self.rules
}
}
impl Program {
pub fn new_with_features(features: FeatureSet) -> Self {
Self {
from_file: None,
features,
predicate_cache: Default::default(),
variable_cache: Default::default(),
extensional: Default::default(),
intensional: Default::default(),
queries: Default::default(),
rules: Default::default(),
}
}
pub fn source_file_path(&self) -> Option<&PathBuf> {
self.from_file.as_ref()
}
pub(crate) fn set_source_file_path(&mut self, file_name: PathBuf) {
self.from_file = Some(file_name);
}
pub(crate) fn features_mut(&mut self) -> &mut FeatureSet {
&mut self.features
}
pub fn predicates(&self) -> &NameReferenceSet<Predicate> {
&self.predicate_cache
}
pub fn variables(&self) -> &NameReferenceSet<Variable> {
&self.variable_cache
}
pub fn extensional_mut(&mut self) -> &mut RelationSet {
&mut self.extensional
}
pub fn add_new_extensional_relation<V: Into<Schema<Predicate>>>(
&mut self,
label: PredicateRef,
schema: V,
) -> Result<&mut Relation> {
let label = self.predicate_cache.canonical(label);
self.extensional_mut()
.add_new_relation(label, schema.into())
}
pub fn add_extensional_relation(&mut self, relation: Relation) {
self.extensional_mut().add(relation)
}
pub fn intensional_mut(&mut self) -> &mut RelationSet {
&mut self.intensional
}
pub fn add_new_intensional_relation<V: Into<Schema<Predicate>>>(
&mut self,
label: PredicateRef,
schema: V,
) -> Result<&mut Relation> {
let label = self.predicate_cache.canonical(label);
self.intensional_mut()
.add_new_relation(label, schema.into())
}
pub fn add_intensional_relation(&mut self, relation: Relation) {
self.intensional_mut().add(relation)
}
pub fn add_new_pure_rule<H: Into<Vec<Term>>, B: Into<Vec<Literal>>>(
&mut self,
head_label: PredicateRef,
head_terms: H,
body: B,
) -> Result<bool> {
let head_label = self.predicate_cache.canonical(head_label);
let rule = Rule::new_pure(Atom::new(head_label, head_terms), body);
self.add_rule(rule)
}
pub fn add_new_constraint_rule<B: Into<Vec<Literal>>>(&mut self, body: B) -> Result<bool> {
let rule = Rule::new_constraint(body);
self.add_rule(rule)
}
pub fn add_new_disjunctive_rule<A: Into<Vec<Atom>>, B: Into<Vec<Literal>>>(
&mut self,
head: A,
body: B,
) -> Result<bool> {
let rule = Rule::new_disjunctive(head, body);
self.add_rule(rule)
}
pub fn add_rule(&mut self, rule: Rule) -> Result<bool> {
rule.safety_check(self.features())?;
if rule.form() == RuleForm::Constraint && !self.features().supports(&FEATURE_CONSTRAINTS) {
return Err(language_feature_unsupported(FEATURE_CONSTRAINTS));
}
if rule.form() == RuleForm::Disjunctive && !self.features().supports(&FEATURE_DISJUNCTION) {
return Err(language_feature_unsupported(FEATURE_DISJUNCTION));
}
for atom in rule.head() {
if self.extensional.contains(atom.label()) {
return Err(extensional_predicate_in_rule_head(
atom.label_ref(),
atom.source_location().cloned(),
));
} else if !self.intensional.contains(atom.label()) {
let mut schema = Vec::with_capacity(atom.len());
for term in atom.iter() {
match term {
Term::Anonymous => {}
Term::Variable(v) => schema.push(self.infer_attribute(v, &rule)),
Term::Constant(c) => schema.push(Attribute::from(c.kind())),
}
}
self.intensional_mut()
.add_new_relation(atom.label_ref(), schema)?;
}
}
Ok(self.rules.add(rule))
}
fn infer_attribute(&self, variable: &VariableRef, rule: &Rule) -> Attribute<Predicate> {
let candidates: Vec<(&Predicate, usize)> = rule
.literals()
.filter_map(Literal::as_relational)
.filter_map(|a| {
a.iter()
.enumerate()
.filter_map(|(i, term)| term.as_variable().map(|var| (i, var)))
.find(|(_, var)| var == &variable)
.map(|(i, _)| (a.label(), i))
})
.collect();
for (predicate, i) in candidates {
if let Some(relation) = self.extensional().get(predicate) {
return relation.schema().get(&(i.into())).unwrap().clone();
}
}
Attribute::anonymous()
}
pub fn is_recursive(&self) -> bool {
PrecedenceGraph::from(self).is_recursive()
}
pub fn is_semi_positive(&self) -> bool {
if self.features().supports(&FEATURE_NEGATION) {
PrecedenceGraph::from(self).is_semi_positive()
} else {
false
}
}
pub fn is_linear(&self) -> bool {
self.rules().iter().all(|rule| {
rule.literals()
.filter_map(|literal| literal.as_relational())
.filter(|atom| self.intensional().contains(atom.label()))
.count()
<= 1
})
}
pub fn is_guarded(&self) -> bool {
self.rules().iter().all(|rule| rule.is_guarded())
}
pub fn is_frontier_guarded(&self) -> bool {
self.rules().iter().all(|rule| rule.is_frontier_guarded())
}
pub fn queries(&self) -> &QuerySet {
&self.queries
}
pub fn add_new_query<T: Into<Vec<Term>>>(
&mut self,
label: PredicateRef,
terms: T,
) -> Result<bool> {
let label = self.predicate_cache.canonical(label);
let query = Query::new(label, terms);
self.add_query(query)
}
pub fn add_query(&mut self, query: Query) -> Result<bool> {
let predicate = query.as_ref().label_ref();
if !self.extensional().contains(&predicate) && !self.intensional().contains(&predicate) {
Err(relation_does_not_exist(predicate))
} else {
Ok(self.queries.add(query))
}
}
pub fn load_extensional_data(&mut self) -> Result<()> {
for relation in self.extensional_mut().iter_mut() {
relation.load_from_file()?;
}
Ok(())
}
pub fn store_intensional_data(&mut self) -> Result<()> {
for relation in self.intensional_mut().iter_mut() {
relation.store_to_file()?;
}
Ok(())
}
pub fn run(&mut self, evaluator: impl Evaluator, load_extensional: bool) -> Result<()> {
if load_extensional {
self.load_extensional_data()?;
}
let new_idb = evaluator.inference(self)?;
println!("{:?}", new_idb);
self.intensional_mut().merge_from(new_idb)?;
self.store_intensional_data()?;
let results = self.eval_queries()?;
for (query, view) in results {
println!("{:?}", query);
if let Some(view) = view {
println!("{}", view);
}
}
Ok(())
}
pub fn eval_query(&self, query: &Query) -> Result<Option<View>> {
self.inner_eval_query(query, self.intensional())
}
pub fn eval_query_with(
&self,
query: &Query,
_evaluator: impl Evaluator,
) -> Result<Option<View>> {
let new_idb = _evaluator.inference(self)?;
self.inner_eval_query(query, &new_idb)
}
pub fn eval_queries(&self) -> Result<Vec<(&Query, Option<View>)>> {
let results: Result<Vec<Option<View>>> = self
.queries()
.iter()
.map(|q| self.inner_eval_query(q, self.intensional()))
.collect();
match results {
Ok(results) => Ok(self.queries.iter().zip(results.into_iter()).collect()),
Err(e) => Err(e),
}
}
pub fn eval_queries_with(
&self,
_evaluator: impl Evaluator,
) -> Result<Vec<(&Query, Option<View>)>> {
let new_idb = _evaluator.inference(self)?;
let results: Result<Vec<Option<View>>> = self
.queries()
.iter()
.map(|q| self.inner_eval_query(q, &new_idb))
.collect();
match results {
Ok(results) => Ok(self.queries.iter().zip(results.into_iter()).collect()),
Err(e) => Err(e),
}
}
fn inner_eval_query(&self, query: &Query, intensional: &RelationSet) -> Result<Option<View>> {
let label = query.as_ref().label_ref();
if intensional.contains(&label) {
intensional.query(query)
} else if self.extensional().contains(&label) {
self.extensional().query(query)
} else {
Err(relation_does_not_exist(label))
}
}
}
impl<T> Default for NameReferenceSet<T>
where
T: AttributeName,
{
fn default() -> Self {
Self(Default::default())
}
}
impl<T> NameReferenceSet<T>
where
T: AttributeName,
{
pub fn add<S: AsRef<str>>(&self, s: S) -> Result<()> {
self.fetch(s.as_ref()).map(|_| ())
}
pub fn add_all<S: AsRef<str>>(&self, all: impl Iterator<Item = S>) -> Result<()> {
for s in all {
self.fetch(s.as_ref()).map(|_| ())?;
}
Ok(())
}
pub fn contains<S: AsRef<str>>(&self, s: S) -> bool {
self.0.borrow().contains_key(s.as_ref())
}
pub fn fetch<S: Into<String>>(&self, s: S) -> Result<AttributeNameRef<T>> {
let s = s.into();
let found = { self.0.borrow().get(&s).cloned() };
match found {
None => {
let predicate: AttributeNameRef<T> = T::from_str(&s)
.map_err(|_| error::invalid_value(T::type_name(), &s))?
.into();
let _ = self.0.borrow_mut().insert(s, predicate.clone());
Ok(predicate)
}
Some(p) => Ok(p),
}
}
#[inline]
pub fn canonical(&self, p: AttributeNameRef<T>) -> AttributeNameRef<T> {
let s: &str = p.as_ref().as_ref();
let found = { self.0.borrow().get(s).cloned() };
match found {
None => {
let _ = self.0.borrow_mut().insert(p.to_string(), p.clone());
p
}
Some(p) => p,
}
}
}
#[macro_use]
mod macros;
mod syntax;
pub mod error;
pub mod features;
pub mod edb;
pub mod idb;
#[cfg(feature = "parser")]
pub mod parse;
pub mod visitor;