diff --git a/src/kind-macros/src/lib.rs b/src/kind-macros/src/lib.rs index 57140253..76665d2e 100644 --- a/src/kind-macros/src/lib.rs +++ b/src/kind-macros/src/lib.rs @@ -1,37 +1,94 @@ use proc_macro::{self, TokenStream}; +use proc_macro2::Span; +use proc_macro2::TokenStream as TknStream; use quote::quote; +use syn::parse_quote; +use syn::spanned::Spanned; +use syn::Error; +use syn::Ident; use syn::ItemTrait; use syn::{parse_macro_input, PatIdent, Receiver}; -use syn::{Error}; + +#[derive(Debug)] +struct Method { + name: syn::Ident, + args: Vec, + memoizable: bool, + meta_info: bool, + ret: syn::ReturnType, + meta_ret: syn::ReturnType, +} #[derive(Debug)] struct TraitPat { - methods: Vec<(syn::Ident, Vec, syn::ReturnType)>, + methods: Vec, } -fn methods_to_pat(trait_item: &ItemTrait) -> syn::Result { +fn method_args(method: &syn::TraitItemMethod) -> syn::Result> { + if method.sig.inputs.is_empty() { + return Err(Error::new(method.sig.ident.span(), "expected `&self`")); + } + + match &method.sig.inputs[0] { + syn::FnArg::Receiver(Receiver { + mutability: None, + self_token: _, + reference: Some(_), + .. + }) => {} + id => return Err(Error::new(id.span(), "expected `&self`")), + } + + let mut iter = method.sig.inputs.iter(); + iter.next(); + + Ok(iter.cloned().collect()) +} + +fn methods_to_pat(trait_item: &mut ItemTrait) -> syn::Result { let mut pat = TraitPat { methods: Vec::new() }; - for item in &trait_item.items { + for item in &mut trait_item.items { match item { syn::TraitItem::Method(method) => { - if method.sig.inputs.is_empty() { - return Err(Error::new(method.sig.ident.span(), "expected `&self`")); - } - match method.sig.inputs[0] { - syn::FnArg::Receiver(Receiver { - mutability: None, - self_token: _, - reference: Some(_), - .. - }) => {} - _ => return Err(Error::new(method.sig.ident.span(), "expected `&self`")), - } - let mut method_args = Vec::new(); - for i in 1..method.sig.inputs.len() { - method_args.push(method.sig.inputs[i].clone()) + let mut memoizable = false; + let mut meta_info = false; + for attr in &method.attrs { + let span = attr.path.span(); + match attr.path.get_ident() { + Some(path) => { + if path == &Ident::new("memoize", Span::call_site()) { + memoizable = true; + } else if path == &Ident::new("memoize_info", Span::call_site()) { + meta_info = true; + memoizable = true; + } else { + return Err(Error::new(span, "unrecognized query attribute")); + } + } + _ => return Err(Error::new(span, "invalid query attribute")), + } } + method.attrs = Vec::new(); + let args = method_args(method)?; let name = method.sig.ident.clone(); - pat.methods.push((name, method_args, method.sig.output.clone())) + + let old = method.sig.output.clone(); + + if meta_info { + match &mut method.sig.output { + syn::ReturnType::Default => method.sig.output = parse_quote!(quote! { -> ((), bool) }), + syn::ReturnType::Type(_, ty) => *ty = Box::new(parse_quote!((#ty, bool))), + } + } + + pat.methods.push(Method { + name, + args, + memoizable, + ret: old, + meta_info, + meta_ret: method.sig.output.clone(), + }) } _ => return Err(Error::new(trait_item.ident.span(), "can only process methods")), } @@ -39,119 +96,103 @@ fn methods_to_pat(trait_item: &ItemTrait) -> syn::Result { Ok(pat) } -#[proc_macro_attribute] -pub fn make_provider(_attr: TokenStream, input: TokenStream) -> TokenStream { - let mut ast: ItemTrait = parse_macro_input!(input); +fn append_ident(ident: &syn::Ident, str: &str) -> syn::Ident { + syn::Ident::new(&format!("{}{}", ident, str), ident.span()) +} - let cache = syn::Ident::new(&format!("{}Cache", ast.ident), ast.ident.span()); - let provider = syn::Ident::new(&format!("{}Provider", ast.ident), ast.ident.span()); - let store = syn::Ident::new(&format!("{}Database", ast.ident), ast.ident.span()); - let ident = ast.ident.clone(); - - let pat = match methods_to_pat(&ast) { - Ok(res) => res, - Err(err) => return err.to_compile_error().into(), - }; +fn mk_ident(ident: &str) -> syn::Ident { + syn::Ident::new(ident, Span::call_site()) +} +fn make_cache_struct(pat: &TraitPat, ident_cache: &syn::Ident) -> syn::Result { let mut defs = Vec::new(); - for (ident, _, ret) in &pat.methods { - match ret { - syn::ReturnType::Type(_, ty) => { - defs.push(quote! { - pub #ident: std::sync::Arc>, - }); - } - _ => (), - } - } - - let mut fields = Vec::new(); - - for (ident, args, ret) in &pat.methods { - fields.push(quote! { - pub #ident: fn(&#store, #(#args),*) #ret, - }); - } - - let mut impls = Vec::new(); - - for i in 0..pat.methods.len() { - let mut new_args: Vec = Vec::new(); - let (ident, args, ret) = &pat.methods[i]; - - for arg in args { - match arg { - syn::FnArg::Typed(syn::PatType { pat, .. }) => match *pat.clone() { - syn::Pat::Ident(id) => new_args.push(id), - _ => return quote! {compile_error!("pattern type not supported yet"); }.into(), - }, - _ => return quote! {compile_error!("cannot use &self in the middle of parameters yet"); }.into(), + for Method { ret, memoizable, name, .. } in &pat.methods { + if let syn::ReturnType::Type(_, ty) = ret { + if *memoizable { + defs.push(quote! { + pub #name: std::sync::Arc>, + }); } } - - impls.push(quote! { - fn #ident(&self, #(#args),*) #ret { - self.run_query((#(#new_args)* as u64), self.cache.#ident.clone(), #i as u8, |db| { - (db.provider.#ident)(db, #(#new_args),*) - }) - } - }); } - for entry in &mut ast.items { - match entry { - syn::TraitItem::Method(met) => { - met.attrs = Vec::new(); - } - _ => (), - } - } - - let mut match_fields = Vec::new(); - - for i in 0..pat.methods.len() { - let (ident, _, _) = &pat.methods[i]; - let u = i as u8; - match_fields.push(quote! { - #u => {self.cache.#ident.remove(&hash);}, - }); - } - - quote! { - #ast - + let struct_quoted = quote! { #[derive(Default)] - pub struct #cache { + pub struct #ident_cache { #(#defs)* } + }; - pub struct #provider { + Ok(struct_quoted) +} + +fn make_provider_struct(pat: &TraitPat, ident_provider: &syn::Ident, ident_store: &syn::Ident) -> syn::Result { + let mut fields = Vec::new(); + + for Method { name, args, ret, .. } in &pat.methods { + fields.push(quote! { + pub #name: fn(&#ident_store, #(#args),*) #ret, + }); + } + + let quoted = quote! { + pub struct #ident_provider { #(#fields)* } + }; + Ok(quoted) +} + +fn make_storage_struct(pat: &TraitPat, ident_provider: &syn::Ident, ident_cache: &syn::Ident, ident_storage: &syn::Ident, conf: Option) -> syn::Result { + let mut match_fields = Vec::new(); + + for i in 0..pat.methods.iter().filter(|e| e.memoizable).count() { + let Method { name, .. } = &pat.methods[i]; + let u = i as u8; + match_fields.push(quote! { + #u => {self.cache.#name.remove(&hash);}, + }); + } + + let config = match &conf { + Some(res) => quote! { config: #res }, + None => quote! {}, + }; + + let conf_def = match conf { + Some(_) => quote! { config }, + None => quote! {}, + }; + + let res = quote! { #[derive(Clone)] - pub struct #store { - provider: std::sync::Arc<#provider>, - cache: std::sync::Arc<#cache>, - dep_graph: crate::graph::DepGraph, + pub struct #ident_storage { + provider: std::sync::Arc<#ident_provider>, + cache: std::sync::Arc<#ident_cache>, + dep_graph: crate::core::graph::DepGraph, input_hash: u64, - parent: Option + parent: Option, + #config } - impl #store { - pub fn new(provider: std::sync::Arc<#provider>) -> #store { - #store { + impl #ident_storage { + pub fn new(provider: std::sync::Arc<#ident_provider>, #config) -> #ident_storage { + #ident_storage { provider, cache: Default::default(), dep_graph: Default::default(), input_hash: Default::default(), parent: None, + #conf_def } } } - impl crate::store::Session for #store { + use crate::core::store::Session; + + impl crate::core::store::Session for #ident_storage { fn delete_from_cache(&self, hash: u64, db_idx: u8) { match db_idx { #(#match_fields)* @@ -167,14 +208,128 @@ pub fn make_provider(_attr: TokenStream, input: TokenStream) -> TokenStream { self.parent = Some(hash); } - fn dependency_graph(&self) -> crate::graph::DepGraph { + fn dependency_graph(&self) -> crate::core::graph::DepGraph { self.dep_graph.clone() } } + }; - impl #ident for #store { - #(#impls)* + Ok(res) +} + +fn make_impl(pat: &TraitPat, ident: &syn::Ident, ident_storage: &syn::Ident) -> syn::Result { + let mut impls = Vec::new(); + + let mut counter = 0; + for i in 0..pat.methods.len() { + let mut new_args: Vec = Vec::new(); + let Method { + name, + args, + memoizable, + ret, + meta_info, + meta_ret, + .. + } = &pat.methods[i]; + + for arg in args { + match arg { + syn::FnArg::Typed(syn::PatType { pat, .. }) => match *pat.clone() { + syn::Pat::Ident(id) => new_args.push(id), + pat => return Err(Error::new(pat.span(), "pattern matching not supported yet")), + }, + pat => return Err(Error::new(pat.span(), "cannot use self in this position")), + } + } + + let run_memoized = mk_ident("run_query_memoized"); + let run_flat = mk_ident("run_query_flat"); + + let meta = if *meta_info { + quote! {} + } else { + quote! { .0 } + }; + + if *memoizable { + impls.push(quote! { + fn #name(&self, #(#args),*) #meta_ret { + self.#run_memoized(fxhash::hash64(&(#i+1, #(&#new_args),*)), self.cache.#name.clone(), #counter as u8, |db| { + (db.provider.#name)(db, #(#new_args),*) + })#meta + } + }); + counter += 1; + } else { + impls.push(quote! { + fn #name(&self, #(#args),*) #ret { + self.#run_flat(fxhash::hash64(&(#i+1, #(&#new_args),*)), |db| { + (db.provider.#name)(db, #(#new_args),*) + }) + } + }); } } - .into() + + Ok(quote! { + impl #ident for #ident_storage { + #(#impls)* + } + }) +} + +fn make_provider_strutures(ast: &mut ItemTrait, ident: &syn::Ident, attr_vec: Vec) -> syn::Result { + let cache = append_ident(ident, "Cache"); + let provider = append_ident(ident, "Provider"); + let storage = append_ident(ident, "Database"); + let trait_pat = methods_to_pat(ast)?; + let attr = parse_config(attr_vec)?; + + let cache_struct = make_cache_struct(&trait_pat, &cache)?; + let provider_struct = make_provider_struct(&trait_pat, &provider, &storage)?; + let storage_struct = make_storage_struct(&trait_pat, &provider, &cache, &storage, attr)?; + + let impl_for_struct = make_impl(&trait_pat, ident, &storage)?; + + let res = quote! { + #ast + #cache_struct + #provider_struct + #storage_struct + #impl_for_struct + }; + + Ok(res) +} + +fn parse_config(ast: Vec) -> syn::Result> { + if ast.len() == 1 { + let ast = &ast[0]; + match ast { + syn::NestedMeta::Meta(meta) => match meta { + syn::Meta::Path(path) => Ok(Some(path.clone())), + _ => Err(Error::new(ast.span(), "cannot use type")), + }, + syn::NestedMeta::Lit(_) => Err(Error::new(ast.span(), "cannot parse type")), + } + } else if ast.is_empty() { + Ok(None) + } else { + Err(Error::new(Span::call_site(), "too many arguments for the attribute")) + } +} + +#[proc_macro_attribute] +pub fn make_provider(attr_inp: TokenStream, input: TokenStream) -> TokenStream { + let mut ast: ItemTrait = parse_macro_input!(input); + + for attr in &ast.attrs { + println!("{:?}", attr); + } + + let ident = ast.ident.clone(); + make_provider_strutures(&mut ast, &ident, parse_macro_input!(attr_inp)) + .map_or_else(|x| x.into_compile_error(), |x| x) + .into() } diff --git a/src/kind-parser/src/errors.rs b/src/kind-parser/src/errors.rs index 18034e9a..dc32e27e 100644 --- a/src/kind-parser/src/errors.rs +++ b/src/kind-parser/src/errors.rs @@ -1,4 +1,4 @@ -use kind_report::data::{DiagnosticFrame, Marking, Severity, Color}; +use kind_report::data::{Color, DiagnosticFrame, Marking, Severity}; use kind_span::Range; use crate::lexer::tokens::Token; @@ -49,12 +49,12 @@ impl From> for DiagnosticFrame { SyntaxError::LowerCasedDefinition(name, range) => DiagnosticFrame { code: 0, severity: Severity::Error, - title: "The definition name should be upper cased.".to_string(), + title: "The definition name must be capitalized.".to_string(), subtitles: vec![], hints: vec![{ let mut c = name.chars(); let fst = c.next().unwrap().to_uppercase(); - format!("Change it to '{}{}'", fst, c.as_str()).to_string() + format!("Change it to '{}{}'", fst, c.as_str()) }], positions: vec![Marking { position: range, @@ -74,20 +74,18 @@ impl From> for DiagnosticFrame { text: "The comment starts in this position!".to_string(), }], }, - SyntaxError::InvalidEscapeSequence(kind, range) => { - DiagnosticFrame { - code: 0, - severity: Severity::Error, - title: format!("The {} character sequence is invalid!", encode_name(kind)), - subtitles: vec![], - hints: vec![], - positions: vec![Marking { - position: range, - color: Color::Fst, - text: "Here!".to_string(), - }], - } - } + SyntaxError::InvalidEscapeSequence(kind, range) => DiagnosticFrame { + code: 0, + severity: Severity::Error, + title: format!("The {} character sequence is invalid!", encode_name(kind)), + subtitles: vec![], + hints: vec![], + positions: vec![Marking { + position: range, + color: Color::Fst, + text: "Here!".to_string(), + }], + }, SyntaxError::InvalidNumberRepresentation(repr, range) => DiagnosticFrame { code: 0, severity: Severity::Error, @@ -132,4 +130,4 @@ impl From<&Box> for DiagnosticFrame { fn from(err: &Box) -> Self { (err.clone()).into() } -} \ No newline at end of file +} diff --git a/src/kind-parser/src/expr.rs b/src/kind-parser/src/expr.rs index c6b60a68..826a4723 100644 --- a/src/kind-parser/src/expr.rs +++ b/src/kind-parser/src/expr.rs @@ -97,7 +97,6 @@ impl<'a> Parser<'a> { Ok(ident) } - fn parse_lambda(&mut self) -> Result, SyntaxError> { let name_span = self.range(); diff --git a/src/kind-parser/src/lexer/literals.rs b/src/kind-parser/src/lexer/literals.rs index efb4a497..52eaabef 100644 --- a/src/kind-parser/src/lexer/literals.rs +++ b/src/kind-parser/src/lexer/literals.rs @@ -1,4 +1,4 @@ -use kind_span::{Range}; +use kind_span::Range; use crate::errors::{EncodeSequence, SyntaxError}; use crate::lexer::tokens::Token; diff --git a/src/kind-parser/src/lexer/mod.rs b/src/kind-parser/src/lexer/mod.rs index 1ba2b5e9..6f7f567a 100644 --- a/src/kind-parser/src/lexer/mod.rs +++ b/src/kind-parser/src/lexer/mod.rs @@ -45,12 +45,12 @@ impl<'a> Lexer<'a> { "open" => Token::Open, "return" => Token::Return, _ => { - if data.bytes().next().map(|x| x.is_ascii_uppercase()).unwrap_or(false) { - Token::UpperId(data.to_string()) - }else { - Token::LowerId(data.to_string()) - } - }, + if data.bytes().next().map(|x| x.is_ascii_uppercase()).unwrap_or(false) { + Token::UpperId(data.to_string()) + } else { + Token::LowerId(data.to_string()) + } + } } } diff --git a/src/kind-parser/src/lexer/state.rs b/src/kind-parser/src/lexer/state.rs index f1ec0ce4..00e2231b 100644 --- a/src/kind-parser/src/lexer/state.rs +++ b/src/kind-parser/src/lexer/state.rs @@ -2,7 +2,7 @@ use std::{iter::Peekable, str::Chars}; use kind_span::{Pos, Range, SyntaxCtxIndex}; -use crate::{lexer::tokens::Token}; +use crate::lexer::tokens::Token; /// The lexer state. pub struct Lexer<'a> { diff --git a/src/kind-parser/src/pat.rs b/src/kind-parser/src/pat.rs index 401c1d2e..0eea63ec 100644 --- a/src/kind-parser/src/pat.rs +++ b/src/kind-parser/src/pat.rs @@ -1,4 +1,4 @@ -use kind_tree::concrete::pat::{Pat, PatKind}; +use kind_tree::concrete::pat::{Pat, PatKind, PatIdent}; use crate::errors::SyntaxError; use crate::lexer::tokens::Token; @@ -56,7 +56,7 @@ impl<'a> Parser<'a> { let id = self.parse_id()?; Ok(Box::new(Pat { range: id.range, - data: PatKind::Var(id), + data: PatKind::Var(PatIdent(id)), })) } @@ -68,7 +68,6 @@ impl<'a> Parser<'a> { })) } - fn parse_pat_list(&mut self) -> Result, SyntaxError> { let range = self.range(); self.bump(); // '[' diff --git a/src/kind-parser/src/state.rs b/src/kind-parser/src/state.rs index da46dd57..504b2669 100644 --- a/src/kind-parser/src/state.rs +++ b/src/kind-parser/src/state.rs @@ -1,6 +1,6 @@ use std::collections::VecDeque; -use kind_span::{Range}; +use kind_span::Range; use crate::{errors::SyntaxError, lexer::tokens::Token, Lexer}; diff --git a/src/kind-parser/src/top_level.rs b/src/kind-parser/src/top_level.rs index 381b869a..02ca0d76 100644 --- a/src/kind-parser/src/top_level.rs +++ b/src/kind-parser/src/top_level.rs @@ -1,6 +1,6 @@ /// Parses all of the top level structures /// like Book, Entry, Rule and Argument. -use std::collections::HashMap; +use std::collections::{HashMap}; use kind_tree::concrete::{Argument, Book, Entry, Rule}; @@ -13,16 +13,17 @@ fn is_hidden_arg(token: &Token) -> bool { } impl<'a> Parser<'a> { - pub fn is_top_level_entry(&self) -> bool { - self.get().is_upper_id() && ( - self.peek(1).same_variant(Token::Colon) // ':' + pub fn is_top_level_entry_continuation(&self) -> bool { + self.peek(1).same_variant(Token::Colon) // ':' | self.peek(1).same_variant(Token::LPar) // '(' | self.peek(1).same_variant(Token::Less) // '<' | self.peek(1).same_variant(Token::Minus) // '-' | self.peek(1).same_variant(Token::Plus) // '+' - ) } + pub fn is_top_level_entry(&self) -> bool { + self.get().is_upper_id() && self.is_top_level_entry_continuation() + } pub fn complement_binding_op(&self) -> Option { match self.get() { @@ -88,17 +89,16 @@ impl<'a> Parser<'a> { })) } - pub fn parse_entry(&mut self) -> Result, SyntaxError> { + pub fn parse_entry(&mut self) -> Result { let start = self.range(); - if self.get().is_lower_id() { + if self.get().is_lower_id() && self.is_top_level_entry_continuation() { let ident = self.parse_id()?; return Err(SyntaxError::LowerCasedDefinition(ident.data.0, ident.range)); } // Just to make errors more localized if !self.is_top_level_entry() { - println!("{:?} {:?}", self.get(), self.peek(1)); self.fail(vec![])? } @@ -122,7 +122,7 @@ impl<'a> Parser<'a> { } } let end = rules.last().as_ref().map(|x| x.range).unwrap_or(tipo.range); - Ok(Box::new(Entry { + Ok(Entry { name: ident, docs, args, @@ -130,18 +130,18 @@ impl<'a> Parser<'a> { rules, attrs: Vec::new(), range: start.mix(end), - })) + }) } pub fn parse_book(&mut self) -> Book { - let mut entrs = HashMap::new(); + let mut entries = HashMap::new(); let mut names = Vec::new(); while !self.get().same_variant(Token::Eof) { match self.parse_entry() { Ok(entry) => { - if entrs.get(&entry.name.data.0).is_none() { + if entries.get(&entry.name.data.0).is_none() { names.push(entry.name.clone()); - entrs.insert(entry.name.data.0.clone(), entry); + entries.insert(entry.name.data.0.clone(), entry); } } Err(err) => { @@ -156,9 +156,9 @@ impl<'a> Parser<'a> { match res { Ok(_) => (), - Err(err) => self.errs.push(Box::new(err)) + Err(err) => self.errs.push(Box::new(err)), } - Book { names, entrs } + Book { entries, names } } }