diff --git a/effers-derive/src/lib.rs b/effers-derive/src/lib.rs index bb64e25..2b63b96 100644 --- a/effers-derive/src/lib.rs +++ b/effers-derive/src/lib.rs @@ -1,13 +1,11 @@ use proc_macro2::{Span, TokenStream}; use quote::quote; -use syn::parse::{Parse, ParseStream, Result}; -use syn::punctuated::Punctuated; use syn::token::{Mut, SelfValue}; use syn::visit_mut::VisitMut; -use syn::{ - parse_macro_input, Expr, ExprCall, FnArg, Ident, ItemFn, Path, PathSegment, Receiver, Token, - Type, -}; +use syn::{parse_macro_input, Expr, ExprCall, FnArg, Ident, ItemFn, Path, PathSegment, Receiver}; + +mod parse; +use parse::Args; #[proc_macro_attribute] pub fn program( @@ -31,6 +29,7 @@ pub fn program( proc_macro::TokenStream::from(out) } +// TODO change this to change from camel_case to PascalCase fn first_letter_to_uppper_case(s1: String) -> String { let mut c = s1.chars(); match c.next() { @@ -39,87 +38,6 @@ fn first_letter_to_uppper_case(s1: String) -> String { } } -#[derive(Debug)] -struct Args { - name: Option, - effects: Vec, -} - -#[derive(Debug)] -struct Effect { - name: Ident, - path: Path, - functions: Vec, -} - -#[derive(Debug)] -struct EffectFunction { - ident: Ident, - alias: Option, -} - -fn get_name_from_args(input: &mut ParseStream) -> Result { - let name: Ident = input.parse()?; - input.parse::]>()?; - Ok(name) -} -impl Parse for Args { - fn parse(mut input: ParseStream) -> Result { - let name = get_name_from_args(&mut input).ok(); - - let effects: Vec<_> = Punctuated::::parse_terminated(input)? - .into_iter() - .collect(); - - let effects: Vec = effects - .into_iter() - .flat_map(|e| { - Some(Effect { - name: name_from_expr_call(&e)?, - path: if let Expr::Path(p) = &*e.func { - Some(p.path.clone()) - } else { - None - }?, - functions: effects_from_expr_call(&e), - }) - }) - .collect(); - - Ok(Args { name, effects }) - } -} - -fn name_from_expr_call(e: &ExprCall) -> Option { - if let Expr::Path(e) = &*e.func { - Some(e.path.get_ident()?.clone()) - } else { - None - } -} - -/// returns the list of functions, with their optional alias -fn effects_from_expr_call(e: &ExprCall) -> Vec { - e.args - .iter() - .cloned() - .flat_map(|p| match p { - Expr::Path(e) => Some(EffectFunction { - ident: e.path.get_ident().unwrap().clone(), // TODO remove this unwrap - alias: None, - }), - Expr::Cast(cast) => match (*cast.expr, *cast.ty) { - (Expr::Path(expr), Type::Path(ty)) => Some(EffectFunction { - ident: expr.path.get_ident().unwrap().clone(), - alias: Some(ty.path.get_ident()?.clone()), - }), - _ => None, - }, - _ => None, - }) - .collect() -} - #[derive(Clone, Copy)] struct LettersIter { idx: u32, @@ -180,7 +98,7 @@ fn rewrite_item_into_struct(func: ItemFn, args: Args) -> TokenStream { struct IntermediateStruct { tokens: TokenStream, id: Ident, - traits: Vec, + traits: Vec, letters: Vec, generics: TokenStream, } @@ -189,7 +107,7 @@ impl IntermediateStruct { fn new( tokens: TokenStream, id: Ident, - traits: Vec, + traits: Vec, letters: Vec, generics: TokenStream, ) -> Self { @@ -212,7 +130,7 @@ fn intermediate_structs(args: &Args, prog_name: &Ident) -> Vec, + pub effects: Vec, +} + +#[derive(Debug)] +pub struct Effect { + pub name: Ident, + pub path: Path, + pub paren: Paren, + pub functions: Vec, +} + +#[derive(Debug)] +pub struct EffectFunction { + pub ident: Ident, + pub alias: Option, + pub mut_token: Option, +} + +impl Parse for Args { + fn parse(input: ParseStream) -> Result { + let name = if input.peek2(Token!(=>)) { + let name: Ident = input.parse()?; + input.parse::]>()?; + Some(name) + } else { + None + }; + + let effects: Vec = Punctuated::::parse_terminated(input)? + .into_iter() + .collect(); + + Ok(Args { name, effects }) + } +} + +impl Parse for Effect { + fn parse(input: ParseStream) -> Result { + let path: Path = input.parse()?; + let content; + let paren = parenthesized!(content in input); + let functions = Punctuated::::parse_terminated(&content)? + .into_iter() + .collect(); + + let name = (&path) + .segments + .last() + .expect("There must be at least one PathSegment") + .ident + .clone(); + + Ok(Effect { + name, + path, + functions, + paren, + }) + } +} +impl Parse for EffectFunction { + fn parse(input: ParseStream) -> Result { + let mut_token: Option = if input.peek(Token![mut]) { + input.parse()? + } else { + None + }; + + let ident = input.parse()?; + + let alias: Option = if input.peek(Token![as]) { + input.parse::()?; + input.parse()? + } else { + None + }; + + Ok(EffectFunction { + ident, + alias, + mut_token, + }) + } +} diff --git a/examples/main.rs b/examples/main.rs index 1d80d70..4e475a1 100644 --- a/examples/main.rs +++ b/examples/main.rs @@ -10,8 +10,13 @@ fn smth(val: u8) -> u8 { val + 3 } +#[program(Printer(print as p))] +fn other() { + p("hey hi hello"); +} + fn main() { - // maybe smth like this? + // call the first program twice let result: u8 = Smth.add(IoPrinter).add(FileLogger).run(3); assert_eq!(result, 6); let other_result: u8 = Smth @@ -21,6 +26,9 @@ fn main() { }) .run(8); assert_eq!(other_result, 11); + + // other program + Other.add(IoPrinter).run(); } trait Printer { diff --git a/examples/path.rs b/examples/path.rs new file mode 100644 index 0000000..a9815b3 --- /dev/null +++ b/examples/path.rs @@ -0,0 +1,26 @@ +use effers::program; + +// Effects can be referenced from inside a module +#[program(inc::Incrementer(increment))] +fn prog(val: u8) -> u8 { + let x = increment(val); + let y = increment(x); + x + y +} + +mod inc { + pub trait Incrementer { + fn increment(&mut self, v: u8) -> u8; + } + + pub struct TestInc; + impl Incrementer for TestInc { + fn increment(&mut self, v: u8) -> u8 { + v + 3 + } + } +} + +fn main() { + Prog.add(inc::TestInc).run(1); +} diff --git a/src/lib.rs b/src/lib.rs index f93691b..211d6fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,21 +4,38 @@ pub use effers_derive::program; mod test { use super::*; - #[program(Smth => Printer(print as p), Logger(debug, info))] - fn smth(val: u8) -> u8 { - p("hey hi hello"); + // #[program(Smth => Printer(print as p), Logger(debug, info), inc::Incrementer(increment))] + // fn smth(val: u8) -> u8 { + // let s = p("hey hi hello"); - debug("this is a debug-level log"); - info("this is a info-level log"); + // debug("this is a debug-level log"); + // info("this is a info-level log"); - val + 3 - } + // let _s = p("hey hi hello"); + + // dbg!(s); + + // let x = increment(val); + // let y = increment(x); + // x + y + // } trait Printer { - fn print(&mut self, s: &str); + fn print(&self, s: &str) -> &str; } trait Logger { fn debug(&mut self, s: &str); fn info(&mut self, s: &str); } + mod inc { + pub trait Incrementer { + fn increment(&mut self, v: u8) -> u8; + } + } + + // TODO make nameless programs work + #[program(Printer(print as p))] + fn ohter() { + let _s = p("hey hi hello"); + } }