Skip to content

Commit

Permalink
Implement an initial version of datapond macro.
Browse files Browse the repository at this point in the history
  • Loading branch information
vakaras committed Jan 11, 2020
1 parent 1848804 commit 4a01ddc
Show file tree
Hide file tree
Showing 14 changed files with 268 additions and 58 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/target
target
**/*.rs.bk
Cargo.lock
13 changes: 13 additions & 0 deletions datapond-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "datapond-derive"
version = "0.1.0"
authors = ["Vytautas Astrauskas <[email protected]>"]
edition = "2018"

[dependencies]
proc-macro-hack = "0.5"
datapond-macro = { path = "../datapond-macro" }

[dev-dependencies]
trybuild = "1.0"
datafrog = "2"
4 changes: 4 additions & 0 deletions datapond-derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
use proc_macro_hack::proc_macro_hack;

#[proc_macro_hack]
pub use datapond_macro::datapond;
14 changes: 14 additions & 0 deletions datapond-derive/tests/pass/simple1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use datapond_derive::datapond;

fn main() {
let inp = vec![(1, 2), (2, 3)];
let out;
datapond! {
input inp(x: u32, y: u32)
output out(x: u32, y: u32)
out(x, y) :- inp(y, x).
};
assert!(out.len() == 2);
assert!(out[0] == (2, 1));
assert!(out[1] == (3, 2));
}
5 changes: 5 additions & 0 deletions datapond-derive/tests/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#[test]
fn tests() {
let runner = trybuild::TestCases::new();
runner.pass("tests/pass/*.rs");
}
12 changes: 12 additions & 0 deletions datapond-macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "datapond-macro"
version = "0.1.0"
authors = ["Vytautas Astrauskas <[email protected]>"]
edition = "2018"

[lib]
proc-macro = true

[dependencies]
datapond = { path = ".." }
proc-macro-hack = "0.5"
7 changes: 7 additions & 0 deletions datapond-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use proc_macro::TokenStream;
use proc_macro_hack::proc_macro_hack;

#[proc_macro_hack]
pub fn datapond(input: TokenStream) -> TokenStream {
datapond::generate_datafrog(input.into()).into()
}
48 changes: 48 additions & 0 deletions src/data_structures.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use std::collections::HashMap;

/// A map that tracks insertion order.
#[derive(Debug)]
pub struct OrderedMap<K, V>
where
K: Eq + std::hash::Hash,
{
key_order: Vec<K>,
map: HashMap<K, V>,
}

impl<K: Eq + std::hash::Hash + Clone, V> OrderedMap<K, V> {
pub fn len(&self) -> usize {
self.map.len()
}
pub fn insert(&mut self, k: K, v: V) {
assert!(self.map.insert(k.clone(), v).is_none());
self.key_order.push(k);
}
pub fn get(&self, k: &K) -> Option<&V> {
self.map.get(k)
}
pub fn values<'a>(&'a self) -> Vec<&'a V> {
self.key_order.iter().map(|k| &self.map[k]).collect()
}
}

impl<K: Eq + std::hash::Hash + Clone, V> std::iter::FromIterator<(K, V)> for OrderedMap<K, V> {
fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
let mut s = Self {
key_order: Vec::new(),
map: HashMap::new(),
};
for (k, v) in iter {
s.insert(k, v);
}
s
}
}

impl<K: Eq + std::hash::Hash, V> std::ops::Index<&K> for OrderedMap<K, V> {
type Output = V;

fn index(&self, key: &K) -> &Self::Output {
&self.map[key]
}
}
82 changes: 71 additions & 11 deletions src/generator_new/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
//! let r = in.iter().map(|(y, x)| {(x, y)});
//! ```
use std::collections::HashMap;
use crate::data_structures::OrderedMap;

/// A Datalog variable.
///
Expand Down Expand Up @@ -91,6 +91,12 @@ pub(crate) enum DVarTypes {
},
}

impl std::convert::From<Vec<syn::Type>> for DVarTypes {
fn from(types: Vec<syn::Type>) -> Self {
DVarTypes::Tuple(types)
}
}

/// A Datafrog relation.
#[derive(Debug)]
pub(crate) struct RelationDecl {
Expand Down Expand Up @@ -118,6 +124,25 @@ pub(crate) struct Variable {
pub name: syn::Ident,
}

impl Variable {
pub fn with_suffix(&self, suffix: &str) -> Self {
Self {
name: syn::Ident::new(
&format!("{}{}", self.name, suffix),
proc_macro2::Span::call_site(),
),
}
}
pub fn with_counter(&self, counter: usize) -> Self {
Self {
name: syn::Ident::new(
&format!("{}{}", self.name, counter),
proc_macro2::Span::call_site(),
),
}
}
}

/// An operation that reorders and potentially drops Datalog variables.
///
/// It is encoded as a Datafrog `from_map`.
Expand Down Expand Up @@ -171,20 +196,35 @@ pub(crate) struct FilterOp {
expr: syn::Expr,
}

/// An operation that inserts the relation into a variable.
#[derive(Debug)]
pub(crate) struct InsertOp {
/// The variable into which we want to insert the relation.
pub variable: Variable,
/// The relation to be inserted.
pub relation: Variable,
}

#[derive(Debug)]
pub(crate) enum Operation {
Reorder(ReorderOp),
BindVar(BindVarOp),
Join(JoinOp),
Filter(FilterOp),
Insert(InsertOp),
}

/// A Datafrog iteration.
#[derive(Debug)]
pub(crate) struct Iteration {
pub relations: HashMap<syn::Ident, RelationDecl>,
pub variables: HashMap<syn::Ident, VariableDecl>,
pub operations: Vec<Operation>,
pub relations: OrderedMap<syn::Ident, RelationDecl>,
pub variables: OrderedMap<syn::Ident, VariableDecl>,
/// Operations performed before entering the iteration.
pub pre_operations: Vec<Operation>,
/// Operations performed in the body of the iteration.
pub body_operations: Vec<Operation>,
/// Operations performed after exiting the iteration.
pub post_operations: Vec<Operation>,
}

impl Iteration {
Expand All @@ -198,17 +238,37 @@ impl Iteration {
.into_iter()
.map(|decl| (decl.var.name.clone(), decl))
.collect(),
operations: Vec::new(),
pre_operations: Vec::new(),
body_operations: Vec::new(),
post_operations: Vec::new(),
}
}
/// Convert a Datafrog relation to a Datafrog variable and return its identifier.
pub fn convert_relation_to_variable(&mut self, variable: &Variable) -> Variable {
let decl = &self.relations[&variable.name];
let variable_decl = VariableDecl {
var: decl.var.with_counter(self.variables.len()),
typ: decl.typ.clone().into(),
is_output: false,
};
let new_variable = variable_decl.var.clone();
self.variables
.insert(new_variable.name.clone(), variable_decl);
self.pre_operations.push(Operation::Insert(InsertOp {
variable: new_variable.clone(),
relation: decl.var.clone(),
}));
new_variable
}
pub fn get_relation_var(&self, variable_name: &syn::Ident) -> Option<Variable> {
self.relations
.get(variable_name)
.map(|decl| decl.var.clone())
}
pub fn get_variable(&self, variable_name: &syn::Ident) -> Variable {
if let Some(decl) = self.variables.get(variable_name) {
decl.var.clone()
} else {
self.relations[variable_name].var.clone()
}
self.variables[variable_name].var.clone()
}
pub fn add_operation(&mut self, operation: Operation) {
self.operations.push(operation);
self.body_operations.push(operation);
}
}
24 changes: 16 additions & 8 deletions src/generator_new/encode.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::ast;
use crate::generator_new::ast as gen;

fn encode(program: ast::Program) -> gen::Iteration {
pub(crate) fn encode(program: ast::Program) -> gen::Iteration {
let mut relations = Vec::new();
let mut variables = Vec::new();
for decl in program.decls.values() {
Expand Down Expand Up @@ -40,7 +40,11 @@ fn encode(program: ast::Program) -> gen::Iteration {
if literal1.is_negated {
unimplemented!();
}
let variable = iteration.get_variable(&literal1.predicate);
let variable = if let Some(variable) = iteration.get_relation_var(&literal1.predicate) {
iteration.convert_relation_to_variable(&variable)
} else {
iteration.get_variable(&literal1.predicate)
};
let args = literal1.args.clone();

// Retrieve the main variable for the head.
Expand Down Expand Up @@ -111,13 +115,17 @@ mod tests {
eprintln!("{}", tokens);
let expected_tokens = TokenStream::from_str(
r##"
let mut iteration = datafrog::Iteration::new();
let inp = datafrog::Relation::from_vec:: <(u32, u32,)>(inp);
let out = iteration.variable:: <(u32, u32,)>("out");
while iteration.changed() {
out.from_map(&inp, | &(y, x,)| (x, y,));
{
let mut iteration = datafrog::Iteration::new();
let var_inp = datafrog::Relation::from_vec(inp);
let var_out = iteration.variable:: <(u32, u32,)>("out");
let var_inp1 = iteration.variable:: <(u32, u32,)>("inp1");
var_inp1.insert(var_inp);
while iteration.changed() {
var_out.from_map(&var_inp1, | &(y, x,)| (x, y,));
}
out = var_out.complete();
}
let out = out.complete();
"##,
)
.unwrap();
Expand Down
13 changes: 13 additions & 0 deletions src/generator_new/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
use proc_macro2::TokenStream;
use quote::ToTokens;

mod ast;
mod encode;
mod to_tokens;

pub fn generate_datafrog(input: TokenStream) -> TokenStream {
let parsed_program = match syn::parse2(input) {
Ok(program) => program,
Err(err) => return TokenStream::from(err.to_compile_error()),
};
let typechecked_program = crate::typechecker::typecheck(parsed_program).unwrap();
let encoded_program = encode::encode(typechecked_program);
encoded_program.to_token_stream()
}
Loading

0 comments on commit 4a01ddc

Please sign in to comment.