use ir;
use syn::{Ident, IntTy, Lit};
use quote;
use super::typed;
use std::collections::BTreeMap;
pub mod names {
use ir;
use syn::Ident;
use codegen::camelize;
pub fn fields(pred: &ir::Predicate) -> Vec<Ident> {
match pred.names {
Some(ref names) => names.iter().cloned().map(Ident::new).collect(),
None => pred.types
.iter()
.enumerate()
.map(|x| Ident::new(format!("arg{}", x.0)))
.collect(),
}
}
pub fn fact(pred: &ir::Predicate) -> Ident {
Ident::new(camelize(&pred.name))
}
pub fn insert(pred: &ir::Predicate) -> Ident {
Ident::new(format!("insert_{}", pred.name.to_lowercase()))
}
pub fn tuple(pred_name: &str) -> Ident {
Ident::new(format!("pred_{}", pred_name.to_lowercase()))
}
pub fn id(pred_name: &str) -> Ident {
Ident::new(format!("PRED_ID_{}", pred_name.to_uppercase()))
}
}
pub fn fact(pred_id: usize, pred: &ir::Predicate) -> quote::Tokens {
let fact_name = names::fact(pred);
let fact_name2 = fact_name.clone();
let pred_id_name = names::id(&pred.name);
let pred_id_k = Lit::Int(pred_id as u64, IntTy::Usize);
let field_types = pred.types
.iter()
.cloned()
.map(Ident::new)
.collect::<Vec<_>>();
let field_names = names::fields(pred);
let field_names2 = field_names.clone();
let arity = Lit::Int(pred.types.len() as u64, IntTy::Usize);
let arity2 = arity.clone();
let mut type_stores = Vec::new();
{
for (index, (type_, field_name)) in pred.types.iter().zip(field_names.clone()).enumerate() {
let store = typed::store(type_, "e! {self.#field_name});
let index_lit = Lit::Int(index as u64, IntTy::Usize);
type_stores.push(quote! {
out[#index_lit] = #store;
});
}
}
let type_loads = pred.types
.iter()
.enumerate()
.map(|(idx, type_)| typed::load(type_, idx))
.collect::<Vec<_>>();
let mut type_counts = BTreeMap::new();
for type_ in pred.types.iter() {
if !typed::is_small(type_) {
*type_counts.entry(type_.to_string()).or_insert(0usize) += 1;
}
}
let mut type_releases = Vec::new();
for (type_, count) in type_counts {
let data_name = typed::name(&type_);
type_releases.push(quote! {
db.#data_name.read_exit(#count);
});
}
quote! {
const #pred_id_name: usize = #pred_id_k;
#[derive(Debug)]
pub struct #fact_name {
#(pub #field_names: #field_types),*
}
impl #fact_name2 {
fn from_tuple(db: &Database, tuple: &[usize]) -> Self {
let out = Self {
#(#field_names2: (#type_loads).clone()),*
};
unsafe {
#(#type_releases;)*
}
out
}
fn to_tuple(self, db: &mut Database) -> [usize; #arity] {
let mut out = [0; #arity2];
#(#type_stores)*
out
}
}
}
}
pub fn insert(pred: &ir::Predicate) -> quote::Tokens {
let insert_name = names::insert(pred);
let fact_name = names::fact(pred);
let tuple_name = names::tuple(&pred.name);
let pid = names::id(&pred.name);
quote! {
pub fn #insert_name(&mut self, fact: #fact_name) -> usize {
let tuple = fact.to_tuple(self);
let res = self.#tuple_name.insert(&tuple, Provenance::Base);
self.purge_mid(#pid, res.2, res.0);
res.0
}
}
}