aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay <[email protected]>2020-05-05 07:41:28 +0100
committerAkshay <[email protected]>2020-05-05 07:41:28 +0100
commitb63710492fdbdcaa66054e6eec120a2ac8211f99 (patch)
tree8c5799b09ac1f064fdd47b9741780261adf35838
parent3374854baf619e708c014301fa468120df188e7e (diff)
init
-rw-r--r--.gitignore2
-rw-r--r--Cargo.toml18
-rw-r--r--src/lib.rs97
3 files changed, 116 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 088ba6b..83c440c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
1# Generated by Cargo 1# Generated by Cargo
2# will have compiled files and executables 2# will have compiled files and executables
3/target/ 3**/target/**
4 4
5# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 5# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
6# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 6# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..bb0fb84
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,18 @@
1[package]
2name = "currying"
3version = "0.1.0"
4authors = ["Akshay <[email protected]>"]
5edition = "2018"
6
7# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8
9[dependencies]
10proc-macro2 = "1.0.9"
11quote = "1.0"
12
13[dependencies.syn]
14version = "1.0"
15features = ["full"]
16
17[lib]
18proc-macro = true
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..8a030c4
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,97 @@
1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::{format_ident, quote};
5use syn::punctuated::Punctuated;
6use syn::{parse_macro_input, Block, FnArg, ItemFn, Pat, ReturnType, Type};
7
8#[proc_macro_attribute]
9pub fn curry(_attr: TokenStream, item: TokenStream) -> TokenStream {
10 let parsed = parse_macro_input!(item as ItemFn);
11 generate_curry(parsed).into()
12}
13
14fn extract_type(a: FnArg) -> Box<Type> {
15 match a {
16 FnArg::Typed(p) => p.ty,
17 _ => panic!("Not supported on types with `self`!"),
18 }
19}
20
21fn extract_arg_pat(a: FnArg) -> Box<Pat> {
22 match a {
23 FnArg::Typed(p) => p.pat,
24 _ => panic!("Not supported on types with `self`!"),
25 }
26}
27
28fn extract_return_type(a: ReturnType) -> Box<Type> {
29 match a {
30 ReturnType::Type(_, p) => p,
31 _ => panic!("Not supported on functions without return types!"),
32 }
33}
34
35fn extract_arg_types(fn_args: Punctuated<FnArg, syn::token::Comma>) -> Vec<Box<Type>> {
36 return fn_args.into_iter().map(extract_type).collect::<Vec<_>>();
37}
38
39fn extract_arg_idents(fn_args: Punctuated<FnArg, syn::token::Comma>) -> Vec<Box<Pat>> {
40 return fn_args.into_iter().map(extract_arg_pat).collect::<Vec<_>>();
41}
42
43fn generate_type_aliases(
44 fn_arg_types: &[Box<Type>],
45 fn_return_type: Box<Type>,
46 fn_name: &syn::Ident,
47) -> Vec<proc_macro2::TokenStream> {
48 let type_t0 = format_ident!("_{}_T0", fn_name);
49 let mut type_aliases = vec![quote! { type #type_t0 = #fn_return_type }];
50 for (i, t) in (1..).zip(fn_arg_types.into_iter().rev()) {
51 let p = format_ident!("_{}_{}", fn_name, format!("T{}", i - 1));
52 let n = format_ident!("_{}_{}", fn_name, format!("T{}", i));
53 type_aliases.push(quote! {
54 type #n = impl Fn(#t) -> #p
55 })
56 }
57 return type_aliases;
58}
59
60fn generate_body(fn_args: &[Box<Pat>], body: Box<Block>) -> proc_macro2::TokenStream {
61 quote! {
62 return #( move |#fn_args| )* #body
63 }
64}
65
66fn generate_curry(parsed: ItemFn) -> proc_macro2::TokenStream {
67 let fn_body = parsed.block;
68 let sig = parsed.sig;
69 let vis = parsed.vis;
70 let fn_name = sig.ident;
71 let fn_args = sig.inputs;
72 let fn_return_type = sig.output;
73
74 let arg_idents = extract_arg_idents(fn_args.clone());
75 let first_ident = &arg_idents.first().unwrap();
76 let curried_body = generate_body(&arg_idents[1..], fn_body.clone());
77
78 let arg_types = extract_arg_types(fn_args.clone());
79 let first_type = &arg_types.first().unwrap();
80 let type_aliases = generate_type_aliases(
81 &arg_types[1..],
82 extract_return_type(fn_return_type),
83 &fn_name,
84 );
85
86 let return_type = format_ident!("_{}_{}", &fn_name, format!("T{}", type_aliases.len() - 1));
87
88 let gen = quote! {
89 #(#type_aliases);* ;
90 #vis fn #fn_name (#first_ident: #first_type) -> #return_type {
91 #curried_body ;
92 }
93 };
94
95 println!("{}", gen);
96 return gen;
97}