rewrite parsing. add support for paths and optional name
parent
ecb12f4358
commit
224d61d4e1
|
@ -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()
|
||||||
|
|
|
@ -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
|
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 {
|
||||||
|
|
|
@ -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 {
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue