rewrite parsing. add support for paths and optional name
parent
ecb12f4358
commit
224d61d4e1
|
@ -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<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)]
|
||||
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<Ident>,
|
||||
traits: Vec<Path>,
|
||||
letters: Vec<Ident>,
|
||||
generics: TokenStream,
|
||||
}
|
||||
|
@ -189,7 +107,7 @@ impl IntermediateStruct {
|
|||
fn new(
|
||||
tokens: TokenStream,
|
||||
id: Ident,
|
||||
traits: Vec<Ident>,
|
||||
traits: Vec<Path>,
|
||||
letters: Vec<Ident>,
|
||||
generics: TokenStream,
|
||||
) -> Self {
|
||||
|
@ -212,7 +130,7 @@ fn intermediate_structs(args: &Args, prog_name: &Ident) -> Vec<IntermediateStruc
|
|||
|(mut structs, name, mut traits), eff| {
|
||||
let name = format!("{}{}", &name, &eff.name);
|
||||
|
||||
traits.push(eff.name.clone());
|
||||
traits.push(eff.path.clone());
|
||||
|
||||
// kinda messy
|
||||
let letters = LettersIter::new()
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
33
src/lib.rs
33
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");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue