mod handler;
pub(crate) mod user;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote};
use crate::parser::{Program, Relation};
use crate::codegen::CodegenError;
use crate::codegen::Features;
pub(crate) fn gen_input_module(
program: &Program,
features: &Features,
) -> Result<TokenStream, CodegenError> {
let edbs = program.edbs();
let string_intern = features.string_intern();
let preamble = gen_preamble(program, features);
let input_structs = edbs
.iter()
.map(|rel| handler::gen_input_struct(rel, program.facts().get(rel.name()), string_intern))
.collect::<Result<Vec<_>, _>>()?;
let inputs_container = gen_inputs_container(&edbs);
Ok(quote! {
#preamble
#(#input_structs)*
#inputs_container
})
}
pub(crate) fn pascal_case(name: &str) -> String {
let mut out = String::with_capacity(name.len());
let mut capitalize = true;
for c in name.chars() {
if c == '_' || c == '-' {
capitalize = true;
continue;
}
if capitalize {
out.extend(c.to_uppercase());
capitalize = false;
} else {
out.push(c);
}
}
out
}
pub(crate) fn rust_ident(name: &str) -> Ident {
let is_keyword = matches!(
name,
"as" | "break"
| "const"
| "continue"
| "crate"
| "else"
| "enum"
| "extern"
| "false"
| "fn"
| "for"
| "if"
| "impl"
| "in"
| "let"
| "loop"
| "match"
| "mod"
| "move"
| "mut"
| "pub"
| "ref"
| "return"
| "self"
| "Self"
| "static"
| "struct"
| "super"
| "trait"
| "true"
| "type"
| "unsafe"
| "use"
| "where"
| "while"
| "async"
| "await"
| "dyn"
| "abstract"
| "become"
| "box"
| "do"
| "final"
| "macro"
| "override"
| "priv"
| "typeof"
| "unsized"
| "virtual"
| "yield"
| "try"
);
if is_keyword {
Ident::new_raw(name, Span::call_site())
} else {
Ident::new(name, Span::call_site())
}
}
pub(crate) fn user_struct_ident(rel: &Relation) -> Ident {
format_ident!("{}", pascal_case(rel.name()))
}
pub(crate) fn input_struct_ident(rel: &Relation) -> Ident {
format_ident!("{}Input", pascal_case(rel.name()))
}
fn gen_preamble(program: &Program, features: &Features) -> TokenStream {
let facts = program.facts();
let edbs = program.edbs();
let has_any_inline = edbs.iter().any(|rel| facts.contains_key(rel.name()));
let needs_ordered_float = edbs
.iter()
.any(|rel| rel.data_type().iter().any(|dt| dt.is_float()));
let intern_import = if features.string_intern() {
quote! {
use super::intern;
use lasso::Spur;
}
} else {
quote! {}
};
let ordered_float_import = if needs_ordered_float {
quote! { use ordered_float::OrderedFloat; }
} else {
quote! {}
};
let semiring_one_import = if has_any_inline {
quote! { use super::SEMIRING_ONE; }
} else {
quote! {}
};
quote! {
use differential_dataflow::input::InputSession;
use super::{Diff, Ts};
#semiring_one_import
#intern_import
#ordered_float_import
}
}
fn gen_inputs_container(edbs: &[&Relation]) -> TokenStream {
if edbs.is_empty() {
return quote! {};
}
let fields: Vec<TokenStream> = edbs
.iter()
.map(|rel| {
let f = rust_ident(rel.name());
let ty = input_struct_ident(rel);
quote! { pub #f: #ty }
})
.collect();
let fn_params: Vec<TokenStream> = edbs
.iter()
.map(|rel| {
let p = format_ident!("h_{}", rel.name());
let ty = input_struct_ident(rel);
quote! { #p: #ty }
})
.collect();
let inits: Vec<TokenStream> = edbs
.iter()
.map(|rel| {
let f = rust_ident(rel.name());
let p = format_ident!("h_{}", rel.name());
quote! { #f: #p }
})
.collect();
let per_field = |method: TokenStream| -> Vec<TokenStream> {
edbs.iter()
.map(|rel| {
let f = rust_ident(rel.name());
quote! { self.#f.#method; }
})
.collect()
};
let apply_inline = per_field(quote! { apply_inline(index) });
let close = per_field(quote! { close() });
let advance = per_field(quote! { advance_to(t) });
let flush = per_field(quote! { flush() });
quote! {
pub(crate) struct Inputs {
#(#fields,)*
}
impl Inputs {
pub fn new(#(#fn_params),*) -> Self {
Self { #(#inits,)* }
}
pub fn apply_inline_all(&mut self, index: usize) {
#(#apply_inline)*
}
pub fn close_all(&mut self) {
#(#close)*
}
pub fn advance_to_all(&mut self, t: Ts) {
#(#advance)*
}
pub fn flush_all(&mut self) {
#(#flush)*
}
}
}
}