rewrite parsing. add support for paths and optional name

main
annieversary 2022-01-21 10:38:02 +00:00
parent ecb12f4358
commit 224d61d4e1
5 changed files with 160 additions and 99 deletions

View File

@ -1,13 +1,11 @@
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::quote; use quote::quote;
use syn::parse::{Parse, ParseStream, Result};
use syn::punctuated::Punctuated;
use syn::token::{Mut, SelfValue}; use syn::token::{Mut, SelfValue};
use syn::visit_mut::VisitMut; use syn::visit_mut::VisitMut;
use syn::{ use syn::{parse_macro_input, Expr, ExprCall, FnArg, Ident, ItemFn, Path, PathSegment, Receiver};
parse_macro_input, Expr, ExprCall, FnArg, Ident, ItemFn, Path, PathSegment, Receiver, Token,
Type, mod parse;
}; use parse::Args;
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn program( pub fn program(
@ -31,6 +29,7 @@ pub fn program(
proc_macro::TokenStream::from(out) proc_macro::TokenStream::from(out)
} }
// TODO change this to change from camel_case to PascalCase
fn first_letter_to_uppper_case(s1: String) -> String { fn first_letter_to_uppper_case(s1: String) -> String {
let mut c = s1.chars(); let mut c = s1.chars();
match c.next() { match c.next() {
@ -39,87 +38,6 @@ fn first_letter_to_uppper_case(s1: String) -> String {
} }
} }
#[derive(Debug)]
struct Args {
name: Option<Ident>,
effects: Vec<Effect>,
}
#[derive(Debug)]
struct Effect {
name: Ident,
path: Path,
functions: Vec<EffectFunction>,
}
#[derive(Debug)]
struct EffectFunction {
ident: Ident,
alias: Option<Ident>,
}
fn get_name_from_args(input: &mut ParseStream) -> Result<Ident> {
let name: Ident = input.parse()?;
input.parse::<Token![=>]>()?;
Ok(name)
}
impl Parse for Args {
fn parse(mut input: ParseStream) -> Result<Self> {
let name = get_name_from_args(&mut input).ok();
let effects: Vec<_> = Punctuated::<ExprCall, Token![,]>::parse_terminated(input)?
.into_iter()
.collect();
let effects: Vec<Effect> = 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<Ident> {
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<EffectFunction> {
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)] #[derive(Clone, Copy)]
struct LettersIter { struct LettersIter {
idx: u32, idx: u32,
@ -180,7 +98,7 @@ fn rewrite_item_into_struct(func: ItemFn, args: Args) -> TokenStream {
struct IntermediateStruct { struct IntermediateStruct {
tokens: TokenStream, tokens: TokenStream,
id: Ident, id: Ident,
traits: Vec<Ident>, traits: Vec<Path>,
letters: Vec<Ident>, letters: Vec<Ident>,
generics: TokenStream, generics: TokenStream,
} }
@ -189,7 +107,7 @@ impl IntermediateStruct {
fn new( fn new(
tokens: TokenStream, tokens: TokenStream,
id: Ident, id: Ident,
traits: Vec<Ident>, traits: Vec<Path>,
letters: Vec<Ident>, letters: Vec<Ident>,
generics: TokenStream, generics: TokenStream,
) -> Self { ) -> Self {
@ -212,7 +130,7 @@ fn intermediate_structs(args: &Args, prog_name: &Ident) -> Vec<IntermediateStruc
|(mut structs, name, mut traits), eff| { |(mut structs, name, mut traits), eff| {
let name = format!("{}{}", &name, &eff.name); let name = format!("{}{}", &name, &eff.name);
traits.push(eff.name.clone()); traits.push(eff.path.clone());
// kinda messy // kinda messy
let letters = LettersIter::new() let letters = LettersIter::new()

View File

@ -0,0 +1,92 @@
use syn::parse::{Parse, ParseStream, Result};
use syn::punctuated::Punctuated;
use syn::token::Paren;
use syn::{parenthesized, Ident, Path, Token};
#[derive(Debug)]
pub struct Args {
pub name: Option<Ident>,
pub effects: Vec<Effect>,
}
#[derive(Debug)]
pub struct Effect {
pub name: Ident,
pub path: Path,
pub paren: Paren,
pub functions: Vec<EffectFunction>,
}
#[derive(Debug)]
pub struct EffectFunction {
pub ident: Ident,
pub alias: Option<Ident>,
pub mut_token: Option<Token![mut]>,
}
impl Parse for Args {
fn parse(input: ParseStream) -> Result<Self> {
let name = if input.peek2(Token!(=>)) {
let name: Ident = input.parse()?;
input.parse::<Token![=>]>()?;
Some(name)
} else {
None
};
let effects: Vec<Effect> = Punctuated::<Effect, Token![,]>::parse_terminated(input)?
.into_iter()
.collect();
Ok(Args { name, effects })
}
}
impl Parse for Effect {
fn parse(input: ParseStream) -> Result<Self> {
let path: Path = input.parse()?;
let content;
let paren = parenthesized!(content in input);
let functions = Punctuated::<EffectFunction, Token![,]>::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<Self> {
let mut_token: Option<Token![mut]> = if input.peek(Token![mut]) {
input.parse()?
} else {
None
};
let ident = input.parse()?;
let alias: Option<Ident> = if input.peek(Token![as]) {
input.parse::<Token![as]>()?;
input.parse()?
} else {
None
};
Ok(EffectFunction {
ident,
alias,
mut_token,
})
}
}

View File

@ -10,8 +10,13 @@ fn smth(val: u8) -> u8 {
val + 3 val + 3
} }
#[program(Printer(print as p))]
fn other() {
p("hey hi hello");
}
fn main() { fn main() {
// maybe smth like this? // call the first program twice
let result: u8 = Smth.add(IoPrinter).add(FileLogger).run(3); let result: u8 = Smth.add(IoPrinter).add(FileLogger).run(3);
assert_eq!(result, 6); assert_eq!(result, 6);
let other_result: u8 = Smth let other_result: u8 = Smth
@ -21,6 +26,9 @@ fn main() {
}) })
.run(8); .run(8);
assert_eq!(other_result, 11); assert_eq!(other_result, 11);
// other program
Other.add(IoPrinter).run();
} }
trait Printer { trait Printer {

26
examples/path.rs Normal file
View File

@ -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);
}

View File

@ -4,21 +4,38 @@ pub use effers_derive::program;
mod test { mod test {
use super::*; use super::*;
#[program(Smth => Printer(print as p), Logger(debug, info))] // #[program(Smth => Printer(print as p), Logger(debug, info), inc::Incrementer(increment))]
fn smth(val: u8) -> u8 { // fn smth(val: u8) -> u8 {
p("hey hi hello"); // let s = p("hey hi hello");
debug("this is a debug-level log"); // debug("this is a debug-level log");
info("this is a info-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 { trait Printer {
fn print(&mut self, s: &str); fn print(&self, s: &str) -> &str;
} }
trait Logger { trait Logger {
fn debug(&mut self, s: &str); fn debug(&mut self, s: &str);
fn info(&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");
}
} }