diff --git a/Cargo.toml b/Cargo.toml index 630c352..a22e1c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" [dependencies] rustc-hash = "1.0.0" -syn = "1.0.8" +syn = { version = "1.0.8", features = ["extra-traits"] } proc-macro2 = { version = "1.0.6", features = ["span-locations"] } quote = "1.0.2" log = "0.4.8" diff --git a/src/ast.rs b/src/ast.rs index 3190548..1b615da 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -98,6 +98,15 @@ pub enum Arg { Wildcard, } +impl Arg { + pub fn to_ident(&self) -> syn::Ident { + match self { + Arg::Ident(ident) => ident.clone(), + Arg::Wildcard => syn::Ident::new("_", proc_macro2::Span::call_site()), + } + } +} + impl fmt::Display for Arg { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { diff --git a/src/generator_new/ast.rs b/src/generator_new/ast.rs index f643f92..23cf7a3 100644 --- a/src/generator_new/ast.rs +++ b/src/generator_new/ast.rs @@ -8,7 +8,7 @@ //! r(x, y) = in(y, x); //! ``` //! ``in`` is assumed to be a variable of type ``&Vec<(u32, u32)>``. -//! ```rust +//! ```ignore //! let r = in.iter().map(|(y, x)| {(x, y)}); //! ``` //! @@ -21,7 +21,7 @@ //! r(x, y) = r(x, z), r(z, y); //! ``` //! ``in`` is assumed to be a variable of type ``&Vec<(u32, u32)>``. -//! ```rust +//! ```ignore //! let mut iteration = Iteration::new(); //! let r = iteration.variable::<(u32, u32)>("r"); //! let r_1 = iteration.variable::<(u32, u32)>("r_1"); @@ -34,63 +34,106 @@ //! let r = in.iter().map(|(y, x)| {(x, y)}); //! ``` +use std::collections::HashMap; + /// A Datalog variable. /// /// For example, `x` in the following: -/// ```rust +/// ```ignore /// r_1.from_map(&r, |(x, z)| {(z, x)}); /// ``` -struct DVar { - name: syn::Ident, +#[derive(Debug)] +pub(crate) struct DVar { + pub name: syn::Ident, +} + +impl DVar { + pub fn new(name: syn::Ident) -> Self { + Self { name: name } + } } /// A flat tuple of `DVar`s. Typically used to represent the user defined types. -struct DVarTuple { - vars: Vec, +#[derive(Debug)] +pub(crate) struct DVarTuple { + pub vars: Vec, } /// A (key, value) representation of `DVar`s. It is used for joins. -struct DVarKeyVal { - key: Vec, - value: Vec, +#[derive(Debug)] +pub(crate) struct DVarKeyVal { + pub key: Vec, + pub value: Vec, } /// An ordered set of `DVar`s. -enum DVars { +#[derive(Debug)] +pub(crate) enum DVars { Tuple(DVarTuple), KeyVal(DVarKeyVal), } +impl DVars { + pub fn new_tuple(args: Vec) -> Self { + DVars::Tuple(DVarTuple { + vars: args.into_iter().map(|ident| DVar::new(ident)).collect(), + }) + } +} + +/// A type that matches some `DVars`. +#[derive(Debug)] +pub(crate) enum DVarTypes { + Tuple(Vec), + KeyVal { + key: Vec, + value: Vec, + }, +} + +/// A Datafrog relation. +#[derive(Debug)] +pub(crate) struct RelationDecl { + pub var: Variable, + pub typ: Vec, +} + /// A Datafrog variable. /// /// For example, `rule` in the following: -/// ```rust +/// ```ignore /// let rule = iteration.variable::<(u32, u32)>("rule"); /// ``` -struct Variable { - name: syn::Ident, +#[derive(Debug)] +pub(crate) struct VariableDecl { + pub var: Variable, + /// The type by shape must match `DVarKeyVal`. + pub typ: DVarTypes, + pub is_output: bool, } -struct VariableDecl { - var: Variable, - typ: syn::Type, - is_output: bool, +/// A reference to a Datafrog relation or variable. +#[derive(Debug, Clone)] +pub(crate) struct Variable { + pub name: syn::Ident, } /// An operation that reorders and potentially drops Datalog variables. /// /// It is encoded as a Datafrog `from_map`. -struct ReorderOp { +#[derive(Debug)] +pub(crate) struct ReorderOp { /// A variable into which we write the result. - output: Variable, + pub output: Variable, /// A variable from which we read the input. - input: Variable, - input_vars: DVars, - output_vars: DVars, + pub input: Variable, + pub input_vars: DVars, + pub output_vars: DVars, } /// An operation that evaluates the given expression and adds it as a last output variable. -struct BindVarOp { +#[derive(Debug)] +pub(crate) struct BindVarOp { /// A variable into which we write the result. output: Variable, /// A variable from which we read the input. @@ -102,7 +145,8 @@ struct BindVarOp { } /// An operation that joins two variables. -struct JoinOp { +#[derive(Debug)] +pub(crate) struct JoinOp { /// A variable into which we write the result. output: Variable, /// The first variable, which we use in join. @@ -118,7 +162,8 @@ struct JoinOp { } /// An operation that filters out facts. -struct FilterOp { +#[derive(Debug)] +pub(crate) struct FilterOp { /// A variable which we want to filter. variable: Variable, vars: DVars, @@ -126,7 +171,8 @@ struct FilterOp { expr: syn::Expr, } -enum Operation { +#[derive(Debug)] +pub(crate) enum Operation { Reorder(ReorderOp), BindVar(BindVarOp), Join(JoinOp), @@ -134,7 +180,35 @@ enum Operation { } /// A Datafrog iteration. -struct Iteration { - variables: Vec, - operations: Vec, +#[derive(Debug)] +pub(crate) struct Iteration { + pub relations: HashMap, + pub variables: HashMap, + pub operations: Vec, +} + +impl Iteration { + pub fn new(relations: Vec, variables: Vec) -> Self { + Self { + relations: relations + .into_iter() + .map(|decl| (decl.var.name.clone(), decl)) + .collect(), + variables: variables + .into_iter() + .map(|decl| (decl.var.name.clone(), decl)) + .collect(), + operations: Vec::new(), + } + } + 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() + } + } + pub fn add_operation(&mut self, operation: Operation) { + self.operations.push(operation); + } } diff --git a/src/generator_new/encode.rs b/src/generator_new/encode.rs new file mode 100644 index 0000000..961952e --- /dev/null +++ b/src/generator_new/encode.rs @@ -0,0 +1,126 @@ +use crate::ast; +use crate::generator_new::ast as gen; + +fn encode(program: ast::Program) -> gen::Iteration { + let mut relations = Vec::new(); + let mut variables = Vec::new(); + for decl in program.decls.values() { + let var = gen::Variable { + name: decl.name.clone(), + }; + let typ = decl + .parameters + .iter() + .map(|param| param.typ.clone()) + .collect(); + match decl.kind { + ast::PredicateKind::Input => { + relations.push(gen::RelationDecl { var: var, typ: typ }); + } + ast::PredicateKind::Internal => { + variables.push(gen::VariableDecl { + var: var, + typ: gen::DVarTypes::Tuple(typ), + is_output: false, + }); + } + ast::PredicateKind::Output => { + variables.push(gen::VariableDecl { + var: var, + typ: gen::DVarTypes::Tuple(typ), + is_output: true, + }); + } + } + } + let mut iteration = gen::Iteration::new(relations, variables); + for rule in &program.rules { + let mut iter = rule.body.iter(); + let literal1 = iter.next().unwrap(); + if literal1.is_negated { + unimplemented!(); + } + let variable = iteration.get_variable(&literal1.predicate); + let args = literal1.args.clone(); + + // Retrieve the main variable for the head. + // Create a new variable `res` for the rule result. + // { + // if rule.body.len() == 1 { + // res.from_map(rule.body[0], ...) + // } else if rule.body.len() == 2 { + // let first = rule.body[0]; + // let second = rule.body[1]; + // let (key, first_remainder, second_remainder) = common_args(first, second); + // let new_first = variable::<(key, first_remainder)>; + // let new_second = variable::<(key, second_remainder)>; + // new_first.from_map(first); + // new_second.from_map(second); + // res.from_join(new_first, new_second, ...) + // } + // } + + // + // Extend the main variable with the new variable. + // rule.head.extend(res); + let head_variable = iteration.get_variable(&rule.head.predicate); + let reorder_op = gen::ReorderOp { + output: head_variable, + input: variable, + input_vars: args.into(), + output_vars: rule.head.args.clone().into(), + }; + iteration.add_operation(gen::Operation::Reorder(reorder_op)); + } + iteration +} + +impl std::convert::From> for gen::DVars { + fn from(args: Vec) -> Self { + gen::DVars::new_tuple(args.into_iter().map(|arg| arg.to_ident()).collect()) + } +} + +impl std::convert::From> for gen::DVars { + fn from(args: Vec) -> Self { + gen::DVars::new_tuple(args) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::parser::parse; + use crate::typechecker::typecheck; + use proc_macro2::TokenStream; + use quote::ToTokens; + use std::str::FromStr; + + #[test] + fn encode_simple1() { + let program = parse( + " + input inp(x: u32, y: u32) + output out(x: u32, y: u32) + out(x, y) :- inp(y, x). + ", + ); + let program = typecheck(program).unwrap(); + let iteration = encode(program); + let tokens = iteration.to_token_stream().to_string(); + 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 out = out.complete(); + "##, + ) + .unwrap(); + assert_eq!(tokens.to_string(), expected_tokens.to_string()); + } +} diff --git a/src/generator_new/mod.rs b/src/generator_new/mod.rs index 0a6ab8a..7685554 100644 --- a/src/generator_new/mod.rs +++ b/src/generator_new/mod.rs @@ -1 +1,3 @@ -mod ast; \ No newline at end of file +mod ast; +mod encode; +mod to_tokens; diff --git a/src/generator_new/to_tokens.rs b/src/generator_new/to_tokens.rs new file mode 100644 index 0000000..a1824d4 --- /dev/null +++ b/src/generator_new/to_tokens.rs @@ -0,0 +1,162 @@ +use crate::generator_new::ast::*; +use proc_macro2::TokenStream; +use quote::quote; +use quote::ToTokens; + +fn type_vec_to_tokens(type_vec: &Vec) -> TokenStream { + let mut type_tokens = TokenStream::new(); + for typ in type_vec { + type_tokens.extend(quote! {#typ,}); + } + type_tokens +} + +fn var_vec_to_tokens(var_vec: &Vec) -> TokenStream { + let mut var_tokens = TokenStream::new(); + for var in var_vec { + var_tokens.extend(quote! {#var,}); + } + var_tokens +} + +impl ToTokens for DVar { + fn to_tokens(&self, tokens: &mut TokenStream) { + let name = &self.name; + tokens.extend(quote! {#name}); + } +} + +impl ToTokens for DVarTuple { + fn to_tokens(&self, tokens: &mut TokenStream) { + let mut vars = var_vec_to_tokens(&self.vars); + tokens.extend(quote! {(#vars)}); + } +} + +impl ToTokens for DVarKeyVal { + fn to_tokens(&self, tokens: &mut TokenStream) { + let mut key = var_vec_to_tokens(&self.key); + let mut value = var_vec_to_tokens(&self.value); + tokens.extend(quote! {((#key), (value))}); + } +} + +impl ToTokens for DVars { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + DVars::Tuple(tuple) => tuple.to_tokens(tokens), + DVars::KeyVal(key_val) => key_val.to_tokens(tokens), + } + } +} + +impl ToTokens for DVarTypes { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + DVarTypes::Tuple(types) => { + let type_tokens = type_vec_to_tokens(types); + tokens.extend(quote! {(#type_tokens)}); + } + DVarTypes::KeyVal { key, value } => { + let key_tokens = type_vec_to_tokens(key); + let value_tokens = type_vec_to_tokens(value); + tokens.extend(quote! {((#key_tokens), (#value_tokens))}); + } + } + } +} + +impl ToTokens for Variable { + fn to_tokens(&self, tokens: &mut TokenStream) { + let name = &self.name; + tokens.extend(quote! {#name}); + } +} + +impl ToTokens for ReorderOp { + fn to_tokens(&self, tokens: &mut TokenStream) { + let ReorderOp { + output, + input, + input_vars, + output_vars, + } = self; + tokens.extend(quote! { + #output.from_map(&#input, |&#input_vars| #output_vars); + }); + } +} + +impl ToTokens for BindVarOp { + fn to_tokens(&self, tokens: &mut TokenStream) { + unimplemented!(); + } +} + +impl ToTokens for JoinOp { + fn to_tokens(&self, tokens: &mut TokenStream) { + unimplemented!(); + } +} + +impl ToTokens for FilterOp { + fn to_tokens(&self, tokens: &mut TokenStream) { + unimplemented!(); + } +} + +impl ToTokens for Operation { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Operation::Reorder(op) => op.to_tokens(tokens), + Operation::BindVar(op) => op.to_tokens(tokens), + Operation::Join(op) => op.to_tokens(tokens), + Operation::Filter(op) => op.to_tokens(tokens), + } + } +} + +impl ToTokens for Iteration { + fn to_tokens(&self, tokens: &mut TokenStream) { + let mut declare_relations = TokenStream::new(); + for relation in self.relations.values() { + let vec_name = &relation.var.name; + let var = relation.var.to_token_stream(); + let mut typ = TokenStream::new(); + for var_typ in &relation.typ { + typ.extend(quote! {#var_typ,}); + } + declare_relations.extend(quote! { + let #var = datafrog::Relation::from_vec::<(#typ)>(#vec_name); + }); + } + let mut declare_variables = TokenStream::new(); + let mut output_results = TokenStream::new(); + for variable in self.variables.values() { + let var = variable.var.to_token_stream(); + let var_name = var.to_string(); + let typ = variable.typ.to_token_stream(); + declare_variables.extend(quote! { + let #var = iteration.variable::<#typ>(#var_name); + }); + if variable.is_output { + output_results.extend(quote! { + let #var = #var.complete(); + }); + } + } + let mut operations = TokenStream::new(); + for operation in &self.operations { + operation.to_tokens(&mut operations); + } + tokens.extend(quote! { + let mut iteration = datafrog::Iteration::new(); + #declare_relations + #declare_variables + while iteration.changed() { + #operations + } + #output_results + }); + } +} diff --git a/src/lib.rs b/src/lib.rs index 910e76e..869eb84 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ extern crate log; mod ast; mod generator; +mod generator_new; mod parser; mod typechecker;