From 81b3c777235682fe99e4de75ad1f627a23cd6b17 Mon Sep 17 00:00:00 2001 From: phicr Date: Wed, 11 Sep 2019 16:44:37 -0300 Subject: [PATCH 1/2] Restructure type matching Instead of using tuples of TypeIds/TypeMatches, use dedicated enums this will facilitate having more than 12 arguments per method, as the main reason that limitation was in place was because the Hash impl for tuples only went as far as 12 elements. The TypeMatches enum also has variants signifying it contains a vararg which is the first step for supporting them. --- multimethods_helper_proc/src/lib.rs | 101 +++++++++++++- multimethods_proc/src/lib.rs | 28 ++-- src/lib.rs | 4 +- src/main.rs | 17 ++- src/method.rs | 4 +- src/types/abs.rs | 197 +++++++++++++++++++++++++--- src/types/mod.rs | 22 +++- src/types/type_match_tree.rs | 18 +-- 8 files changed, 334 insertions(+), 57 deletions(-) diff --git a/multimethods_helper_proc/src/lib.rs b/multimethods_helper_proc/src/lib.rs index 592223c..8e7b625 100644 --- a/multimethods_helper_proc/src/lib.rs +++ b/multimethods_helper_proc/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(decl_macro)] extern crate proc_macro; use proc_macro as pm; @@ -5,6 +6,12 @@ use proc_macro2 as pm2; use syn::*; use quote::*; +macro ident($str: literal$(, $expr: expr)*) { + Ident::new(&format!($str$(, $expr)*), pm2::Span::call_site()) +} + +const MAX_ARGS: usize = 12; + #[proc_macro] pub fn impl_types(tokens: pm::TokenStream) -> pm::TokenStream { let n: usize = tokens.to_string().parse().expect("impl_types! expects a number"); @@ -19,7 +26,7 @@ pub fn impl_types(tokens: pm::TokenStream) -> pm::TokenStream { for i in 0..n { let ix = syn::Index::from(i); - let tp = Ident::new(&format!("T{}", i), pm2::Span::call_site()); + let tp = ident!("T{}", i); type_parameters.push(tp.clone()); where_clauses.push(quote!(#tp: #types::SubType)); @@ -38,15 +45,14 @@ pub fn impl_types(tokens: pm::TokenStream) -> pm::TokenStream { }); } - for i in n..12 { - type_ids.push(quote!(::std::any::TypeId::of::())); + for i in n..MAX_ARGS { is_refs.push(quote!(false)); let type_n = Ident::new(&format!("type{}", i), pm2::Span::call_site()); type_n_def.push(quote! { fn #type_n(&self) -> #types::ConcreteType { #types::ConcreteType { - id: ::std::any::TypeId::of::(), + id: ::std::any::TypeId::of::(), is_ref: false, parent: #types::ANY } @@ -54,13 +60,15 @@ pub fn impl_types(tokens: pm::TokenStream) -> pm::TokenStream { }); } + let variant = Ident::new(&format!("T{}", n), pm2::Span::call_site()); + quote!( impl<#(#type_parameters),*> #types::Types for (#(#type_parameters,)*) where #(#where_clauses,)* { - fn type_tuple(&self) -> #types::TypeTuple { - (#(#type_ids),*) + fn type_tuple(&self) -> #types::TypeIds { + #types::TypeIds::#variant(#(#type_ids),*) } fn has_ref(&self) -> bool { @@ -72,3 +80,84 @@ pub fn impl_types(tokens: pm::TokenStream) -> pm::TokenStream { ).into() } +struct VarargArgs { + e: Expr, + vararg: Ident, + positionals: Vec +} + +impl syn::parse::Parse for VarargArgs { + fn parse(stream: syn::parse::ParseStream) -> Result { + let e = stream.parse::().unwrap(); + stream.parse::().unwrap(); + let names = stream.parse_terminated::(Ident::parse).unwrap(); + + let len = names.len(); + let vararg = names.iter().last().unwrap().clone(); + let positionals = names.into_iter().take(len-1).collect(); + + Ok(VarargArgs { e, vararg, positionals }) + } +} + +#[proc_macro] +pub fn match_vararg(tokens: pm::TokenStream) -> pm::TokenStream { + let VarargArgs { e, vararg, positionals } = parse_macro_input!(tokens as VarargArgs); + + let tms = quote!(crate::types::TypeMatches); + let mut match_arms = Vec::new(); + + for i in positionals.len()..=MAX_ARGS { + let bs = (0..i).map(|j| ident!("b{}", j)).collect::>(); + + // normal match + let variant = ident!("T{}", i); + let mut matches = Vec::new(); + + for j in 0..positionals.len() { + let a = positionals[j].clone(); + let b = bs[j].clone(); + + matches.push(quote! { + #a.is_super_match(&#b) + }); + } + + for j in positionals.len()..i { + let b = bs[j].clone(); + + matches.push(quote! { + #vararg.is_super_match(&#b) + }); + } + + match_arms.push(quote! { + #tms::#variant(#(#bs),*) => true #(&& #matches)*, + }); + + + // vararg match + if i < MAX_ARGS { + let variant = ident!("V{}", i+1); + matches.push(quote! { + #vararg.is_super_match(bv) + }); + + match_arms.push(quote! { + #tms::#variant(#(#bs,)* bv) => true #(&& #matches)*, + }); + } + } + + if positionals.len() > 0 { + match_arms.push(quote! { + _ => false + }); + } + + quote!( + match #e { + #(#match_arms)* + } + ).into() +} diff --git a/multimethods_proc/src/lib.rs b/multimethods_proc/src/lib.rs index 94dc006..a0aa272 100644 --- a/multimethods_proc/src/lib.rs +++ b/multimethods_proc/src/lib.rs @@ -318,7 +318,8 @@ fn type_ids(inputs: I, output: &ReturnType, fmc: bool) -> pm2::TokenStream where I: Iterator { - let root = root(fmc); + let root = root(fmc); + let type_ids = quote!(#root types::TypeIds); let mut types = Vec::new(); let mut ref_inputs = false; @@ -328,14 +329,11 @@ fn type_ids(inputs: I, output: &ReturnType, fmc: bool) -> pm2::TokenStream types.push(quote!(<#ty as #root TypeOf>::associated_type_of())); } - while types.len() < 12 { - types.push(quote!(::std::any::TypeId::of::<#root Never>())); - } - + let variant = Ident::new(&format!("T{}", types.len()), pm2::Span::call_site()); let returns_ref = is_ref_return(output); quote! { - (((#(#types,)*), #ref_inputs), #returns_ref) + ((#type_ids::#variant(#(#types),*), #ref_inputs), #returns_ref) } } @@ -344,11 +342,12 @@ fn type_matches(inputs: I, output: &ReturnType, fmc: bool) -> pm2::TokenStrea where I: Iterator { - let root = root(fmc); - let type_match = quote!(#root types::TypeMatch); - let sub_type = quote!(#root types::SubType); - let assoc_type = quote!(associated_concrete_type); - let mut types = Vec::new(); + let root = root(fmc); + let type_match = quote!(#root types::TypeMatch); + let type_matches = quote!(#root types::TypeMatches); + let sub_type = quote!(#root types::SubType); + let assoc_type = quote!(associated_concrete_type); + let mut types = Vec::new(); for input in inputs { let ty = arg_type(input); @@ -359,14 +358,11 @@ fn type_matches(inputs: I, output: &ReturnType, fmc: bool) -> pm2::TokenStrea } } - while types.len() < 12 { - types.push(quote!(#type_match::Concrete(<#root Never as #sub_type>::#assoc_type()))); - } - + let variant = Ident::new(&format!("T{}", types.len()), pm2::Span::call_site()); let returns_ref = is_ref_return(output); quote! { - ((#(#types,)*), #returns_ref) + (#type_matches::#variant(#(#types),*), #returns_ref) } } diff --git a/src/lib.rs b/src/lib.rs index a11a6e7..23d9b72 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,14 +5,12 @@ #![feature(trait_alias)] #![feature(decl_macro)] #![feature(associated_type_defaults)] -#![feature(never_type)] +#![feature(proc_macro_hygiene)] #![allow(non_upper_case_globals)] #![feature(test)] pub use lazy_static; -pub type Never = !; - pub mod value; pub use value::*; diff --git a/src/main.rs b/src/main.rs index 3b8bdf7..5059305 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,14 +5,12 @@ #![feature(trait_alias)] #![feature(decl_macro)] #![feature(associated_type_defaults)] -#![feature(never_type)] +#![feature(proc_macro_hygiene)] #![allow(non_upper_case_globals)] #![feature(test)] extern crate test; -pub type Never = !; - pub mod value; pub use value::*; @@ -97,6 +95,13 @@ multifunction! { } } +#[__fmc] +multifunction! { + fn sunga(a: i32) -> i32 { + a + 1 + } +} + #[__fmc] multifunction! { fn debugin(a: i32) -> String { @@ -165,4 +170,10 @@ fn main() { println!("{}", into(Type![i64], 2i32)); println!("hi0: {}", hi()); println!("hi1: {}", hi(String::from("john"))); + + let mut k = 0i32.into_value(); + for _ in 0..=200_000 { + k = sunga(k); + } + println!("{}", k); } diff --git a/src/method.rs b/src/method.rs index f28b92d..9a54e43 100644 --- a/src/method.rs +++ b/src/method.rs @@ -12,8 +12,8 @@ pub struct MethodKey; pub fn initialize_methods(_: &MethodKey) {} -type ConcreteTypeKey = ((TypeTuple, bool), bool); -type AbstractTypeKey = (TypeMatchTuple, bool); +type ConcreteTypeKey = ((TypeIds, bool), bool); +type AbstractTypeKey = (TypeMatches, bool); type TypeKeys = (ConcreteTypeKey, AbstractTypeKey); diff --git a/src/types/abs.rs b/src/types/abs.rs index cb814b6..7078958 100644 --- a/src/types/abs.rs +++ b/src/types/abs.rs @@ -133,39 +133,204 @@ impl AbstractType { } } +type TM = TypeMatch; +#[derive(Debug,Clone,PartialEq)] +pub enum TypeMatches { + T0(), + T1(TM), + T2(TM,TM), + T3(TM,TM,TM), + T4(TM,TM,TM,TM), + T5(TM,TM,TM,TM,TM), + T6(TM,TM,TM,TM,TM,TM), + T7(TM,TM,TM,TM,TM,TM,TM), + T8(TM,TM,TM,TM,TM,TM,TM,TM), + T9(TM,TM,TM,TM,TM,TM,TM,TM,TM), + T10(TM,TM,TM,TM,TM,TM,TM,TM,TM,TM), + T11(TM,TM,TM,TM,TM,TM,TM,TM,TM,TM,TM), + T12(TM,TM,TM,TM,TM,TM,TM,TM,TM,TM,TM,TM), + V1(TM), + V2(TM,TM), + V3(TM,TM,TM), + V4(TM,TM,TM,TM), + V5(TM,TM,TM,TM,TM), + V6(TM,TM,TM,TM,TM,TM), + V7(TM,TM,TM,TM,TM,TM,TM), + V8(TM,TM,TM,TM,TM,TM,TM,TM), + V9(TM,TM,TM,TM,TM,TM,TM,TM,TM), + V10(TM,TM,TM,TM,TM,TM,TM,TM,TM,TM), + V11(TM,TM,TM,TM,TM,TM,TM,TM,TM,TM,TM), + V12(TM,TM,TM,TM,TM,TM,TM,TM,TM,TM,TM,TM), +} + pub type TypeMatchTuple = (TypeMatch, TypeMatch, TypeMatch, TypeMatch, TypeMatch, TypeMatch, TypeMatch, TypeMatch, TypeMatch, TypeMatch, TypeMatch, TypeMatch); pub trait AsTypeMatches { - fn as_type_matches(&self) -> TypeMatchTuple; + fn as_type_matches(&self) -> TypeMatches; } -impl AsTypeMatches for TypeMatchTuple { - fn as_type_matches(&self) -> TypeMatchTuple { +impl AsTypeMatches for TypeMatches { + fn as_type_matches(&self) -> TypeMatches { self.clone() } } impl AsTypeMatches for T { - default fn as_type_matches(&self) -> TypeMatchTuple { - let concrete = |c| TypeMatch::Concrete(c); + default fn as_type_matches(&self) -> TypeMatches { + macro n { + (0) => {self.type0()}, + (1) => {self.type1()}, + (2) => {self.type2()}, + (3) => {self.type3()}, + (4) => {self.type4()}, + (5) => {self.type5()}, + (6) => {self.type6()}, + (7) => {self.type7()}, + (8) => {self.type8()}, + (9) => {self.type9()}, + (10) => {self.type10()}, + (11) => {self.type11()}, + } - (concrete(self.type0()), concrete(self.type1()), concrete(self.type2()), - concrete(self.type3()), concrete(self.type4()), concrete(self.type5()), - concrete(self.type6()), concrete(self.type7()), concrete(self.type8()), - concrete(self.type9()), concrete(self.type10()), concrete(self.type11())) + macro concretes($variant: ident$(, $($n: tt),*)?) { + TypeMatches::$variant($($(TypeMatch::Concrete(n!($n))),*)*) + } + + match self.type_tuple() { + TypeIds::T0(..) => concretes!(T0), + TypeIds::T1(..) => concretes!(T1, 0), + TypeIds::T2(..) => concretes!(T2, 0, 1), + TypeIds::T3(..) => concretes!(T3, 0, 1, 2), + TypeIds::T4(..) => concretes!(T4, 0, 1, 2, 3), + TypeIds::T5(..) => concretes!(T5, 0, 1, 2, 3, 4), + TypeIds::T6(..) => concretes!(T6, 0, 1, 2, 3, 4, 5), + TypeIds::T7(..) => concretes!(T7, 0, 1, 2, 3, 4, 5, 6), + TypeIds::T8(..) => concretes!(T8, 0, 1, 2, 3, 4, 5, 6, 7), + TypeIds::T9(..) => concretes!(T9, 0, 1, 2, 3, 4, 5, 6, 7, 8), + TypeIds::T10(..) => concretes!(T10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9), + TypeIds::T11(..) => concretes!(T11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), + TypeIds::T12(..) => concretes!(T12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), + } } } -pub fn matches_all (a: &TypeMatchTuple, b: &T) -> bool { - let (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) = a; - let (b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11) = b.as_type_matches(); +pub fn matches_all (a: &TypeMatches, b: &T) -> bool { + use TypeMatches::*; + + macro match_all($(($a: ident, $b: ident)),*) { + true $(&& $a.is_super_match(&$b))* + } + + match (a, &b.as_type_matches()) { + (T0(), + T0()) => true, + + (T1(a0), + T1(b0)) => + match_all!((a0,b0)), + + (T2(a0, a1), + T2(b0, b1)) => + match_all!((a0,b0), (a1,b1)), + + (T3(a0, a1, a2), + T3(b0, b1, b2)) => + match_all!((a0,b0),(a1,b1),(a2,b2)), + + (T4(a0, a1, a2, a3), + T4(b0, b1, b2, b3)) => + match_all!((a0,b0),(a1,b1),(a2,b2),(a3,b3)), + + (T5(a0, a1, a2, a3, a4), + T5(b0, b1, b2, b3, b4)) => + match_all!((a0,b0),(a1,b1),(a2,b2),(a3,b3),(a4,b4)), + + (T6(a0, a1, a2, a3, a4, a5), + T6(b0, b1, b2, b3, b4, b5)) => + match_all!((a0,b0),(a1,b1),(a2,b2),(a3,b3),(a4,b4),(a5,b5)), + + (T7(a0, a1, a2, a3, a4, a5, a6), + T7(b0, b1, b2, b3, b4, b5, b6)) => + match_all!((a0,b0),(a1,b1),(a2,b2),(a3,b3),(a4,b4),(a5,b5), + (a6,b6)), + + (T8(a0, a1, a2, a3, a4, a5, a6, a7), + T8(b0, b1, b2, b3, b4, b5, b6, b7)) => + match_all!((a0,b0),(a1,b1),(a2,b2),(a3,b3),(a4,b4),(a5,b5), + (a6,b6),(a7,b7)), + + (T9(a0, a1, a2, a3, a4, a5, a6, a7, a8), + T9(b0, b1, b2, b3, b4, b5, b6, b7, b8)) => + match_all!((a0,b0),(a1,b1),(a2,b2),(a3,b3),(a4,b4),(a5,b5), + (a6,b6),(a7,b7),(a8,b8)), + + (T10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9), + T10(b0, b1, b2, b3, b4, b5, b6, b7, b8, b9)) => + match_all!((a0,b0),(a1,b1),(a2,b2),(a3,b3),(a4,b4),(a5,b5), + (a6,b6),(a7,b7),(a8,b8),(a9,b9)), + + (T11(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10), + T11(b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10)) => + match_all!((a0,b0),(a1,b1),(a2,b2),(a3,b3),(a4, b4),(a5,b5), + (a6,b6),(a7,b7),(a8,b8),(a9,b9),(a10,b10)), + + (T12(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11), + T12(b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11)) => + match_all!((a0,b0),(a1,b1),(a2,b2),(a3,b3),(a4, b4), (a5, b5), + (a6,b6),(a7,b7),(a8,b8),(a9,b9),(a10,b10), (a11,b11)), + + + (V1(av), b) => { + match_vararg!(b, av) + } + + (V2(a0, av), b) => { + match_vararg!(b, a0, av) + } + + (V3(a0, a1, av), b) => { + match_vararg!(b, a0, a1, av) + } + + (V4(a0, a1, a2, av), b) => { + match_vararg!(b, a0, a1, a2, av) + } + + (V5(a0, a1, a2, a3, av), b) => { + match_vararg!(b, a0, a1, a2, a3, av) + } + (V6(a0, a1, a2, a3, a4, av), b) => { + match_vararg!(b, a0, a1, a2, a3, a4, av) + } + + (V7(a0, a1, a2, a3, a4, a5, av), b) => { + match_vararg!(b, a0, a1, a2, a3, a4, a5, av) + } - (a0.is_super_match(&b0) && a1.is_super_match(&b1) && a2.is_super_match(&b2) && - a3.is_super_match(&b3) && a4.is_super_match(&b4) && a5.is_super_match(&b5) && - a6.is_super_match(&b6) && a7.is_super_match(&b7) && a8.is_super_match(&b8) && - a9.is_super_match(&b9) && a10.is_super_match(&b10) && a11.is_super_match(&b11)) + (V8(a0, a1, a2, a3, a4, a5, a6, av), b) => { + match_vararg!(b, a0, a1, a2, a3, a4, a5, a6, av) + } + + (V9(a0, a1, a2, a3, a4, a5, a6, a7, av), b) => { + match_vararg!(b, a0, a1, a2, a3, a4, a5, a6, a7, av) + } + + (V10(a0, a1, a2, a3, a4, a5, a6, a7, a8, av), b) => { + match_vararg!(b, a0, a1, a2, a3, a4, a5, a6, a7, a8, av) + } + + (V11(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, av), b) => { + match_vararg!(b, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, av) + } + + (V12(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, av), b) => { + match_vararg!(b, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, av) + } + + _ => false + } } diff --git a/src/types/mod.rs b/src/types/mod.rs index b92b0b6..2fc2507 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -15,6 +15,24 @@ pub use abstract_impl::*; pub mod type_value; pub use type_value::*; +type TI = TypeId; +#[derive(Debug,Clone,PartialEq,Eq,Hash)] +pub enum TypeIds { + T0(), + T1(TI), + T2(TI,TI), + T3(TI,TI,TI), + T4(TI,TI,TI,TI), + T5(TI,TI,TI,TI,TI), + T6(TI,TI,TI,TI,TI,TI), + T7(TI,TI,TI,TI,TI,TI,TI), + T8(TI,TI,TI,TI,TI,TI,TI,TI), + T9(TI,TI,TI,TI,TI,TI,TI,TI,TI), + T10(TI,TI,TI,TI,TI,TI,TI,TI,TI,TI), + T11(TI,TI,TI,TI,TI,TI,TI,TI,TI,TI,TI), + T12(TI,TI,TI,TI,TI,TI,TI,TI,TI,TI,TI,TI), +} + pub trait TypeOf { fn is_ref(&self) -> bool; @@ -102,10 +120,10 @@ pub type TypeTuple = TypeId, TypeId, TypeId, TypeId); pub trait Types { - fn type_tuple(&self) -> TypeTuple; + fn type_tuple(&self) -> TypeIds; fn has_ref(&self) -> bool; - fn types(&self) -> (TypeTuple, bool) { + fn types(&self) -> (TypeIds, bool) { (self.type_tuple(), self.has_ref()) } diff --git a/src/types/type_match_tree.rs b/src/types/type_match_tree.rs index ddd5301..905fe2e 100644 --- a/src/types/type_match_tree.rs +++ b/src/types/type_match_tree.rs @@ -2,7 +2,7 @@ use crate::types::*; pub struct TypeMatchNode { value: T, - type_match: TypeMatchTuple, + type_match: TypeMatches, children: Vec> } @@ -27,25 +27,25 @@ impl TypeMatchTree { if rr { &mut self.children_rr } else { &mut self.children } } - pub fn insert(&mut self, key: TypeMatchTuple, value: T, rr: bool) { + pub fn insert(&mut self, key: TypeMatches, value: T, rr: bool) { insert_to_children(self.children_mut(rr), TypeMatchNode::new(key, value)); } - pub fn get<'a>(&'a self, key: &TypeMatchTuple, rr: bool) -> Option<&'a T> { + pub fn get<'a>(&'a self, key: &TypeMatches, rr: bool) -> Option<&'a T> { get_from_children(self.children(rr), key) } - pub fn get_mut<'a>(&'a mut self, key: &TypeMatchTuple, rr: bool) -> Option<&'a mut T> { + pub fn get_mut<'a>(&'a mut self, key: &TypeMatches, rr: bool) -> Option<&'a mut T> { get_from_children_mut(self.children_mut(rr), key) } - pub fn remove<'a>(&'a mut self, key: &TypeMatchTuple, rr: bool) -> Option { + pub fn remove<'a>(&'a mut self, key: &TypeMatches, rr: bool) -> Option { remove_from_children(self.children_mut(rr), key) } } impl TypeMatchNode { - fn new(key: TypeMatchTuple, value: T) -> Self { + fn new(key: TypeMatches, value: T) -> Self { TypeMatchNode { value, type_match: key, @@ -92,7 +92,7 @@ fn insert_to_children( fn get_from_children<'a, T>( - children: &'a Vec>, key: &TypeMatchTuple) -> Option<&'a T> { + children: &'a Vec>, key: &TypeMatches) -> Option<&'a T> { for child in children.iter() { if matches_all(&child.type_match, key) { @@ -108,7 +108,7 @@ fn get_from_children<'a, T>( } fn get_from_children_mut<'a, T>( - children: &'a mut Vec>, key: &TypeMatchTuple) -> Option<&'a mut T> { + children: &'a mut Vec>, key: &TypeMatches) -> Option<&'a mut T> { for child in children.iter_mut() { if matches_all(&child.type_match, key) { @@ -124,7 +124,7 @@ fn get_from_children_mut<'a, T>( } fn remove_from_children<'a, T> ( - children: &'a mut Vec>, key: &TypeMatchTuple) -> Option { + children: &'a mut Vec>, key: &TypeMatches) -> Option { for i in 0..children.len() { if matches_all(&children[i].type_match, key) { From 3212063265af72f9dd00acf8f58f74186f550be0 Mon Sep 17 00:00:00 2001 From: phicr Date: Thu, 12 Sep 2019 13:42:44 -0300 Subject: [PATCH 2/2] Implement Varargs --- README.md | 33 +++++- multimethods_proc/src/lib.rs | 177 +++++++++++++++++++++++++-------- multimethods_tests/src/main.rs | 29 ++++++ src/main.rs | 40 +++++--- src/method.rs | 4 + src/types/abs.rs | 24 ++++- src/types/mod.rs | 3 + src/types/type_match_tree.rs | 56 ++++++++++- src/types/vararg.rs | 73 ++++++++++++++ 9 files changed, 379 insertions(+), 60 deletions(-) create mode 100644 src/types/vararg.rs diff --git a/README.md b/README.md index d5bb031..ff0454f 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ fn main() { } ``` -Methods that Return references requires the call to be expressed as `(FUNC.rr)(args...)`. Hopefully this won't be necessary in the future. (or I will find a nicer syntax, at least) +Methods that return references requires the call to be expressed as `(FUNC.rr)(args...)`. Hopefully this won't be necessary in the future. (or I will find a nicer syntax, at least) ```rust multifunction! { @@ -287,6 +287,37 @@ fn main() { } ``` +A variadic method can be defined using the special `Vararg![T]` macro. The type of the variadic +argument is `multimethods::types::vararg::Vararg`, which can be iterated through and indexed. + +```rust +// Vararg doesn't need to be imported as it's merely a marker for the multifunction! macro +use multimethods::multifunction; + +multifunction! { + fn SUM(args: Vararg![i32]) -> i32 { + args.iter().sum() + } +} + +// Vararg![] is equivalent to Vararg![Abstract![ANY]] +multifunction! { + fn PRINT_ALL(args: Vararg![]) { + for arg in args { + println!("{}", arg) + } + } +} + +fn main() { + println!("{}", SUM(1, 2, 3)); // 6 + + PRINT_ALL("a", 2); // a + // 2 +} +``` + + ## Limitations * Only up to 12 arguments per method are allowed. This number was chosen as it is the largest size of a tuple that has trait implementations for it in the standard library. diff --git a/multimethods_proc/src/lib.rs b/multimethods_proc/src/lib.rs index a0aa272..bcd9d7f 100644 --- a/multimethods_proc/src/lib.rs +++ b/multimethods_proc/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(decl_macro)] + extern crate proc_macro; use std::collections::HashMap; @@ -5,9 +7,14 @@ use proc_macro as pm; use proc_macro2 as pm2; use syn::*; use syn::punctuated::Punctuated; -use syn::token::{Paren, Comma}; use quote::*; +macro ident($str: literal$(, $expr: expr)*) { + Ident::new(&format!($str$(, $expr)*), pm2::Span::call_site()) +} + +const MAX_ARGS: usize = 12; + struct Method { public: bool, expr: pm2::TokenStream, @@ -19,7 +26,7 @@ struct Keys(Vec); impl syn::parse::Parse for Keys { fn parse(input: syn::parse::ParseStream) -> Result { - let punct = >::parse_terminated(input).unwrap(); + let punct = >::parse_terminated(input).unwrap(); Ok(Keys(punct.into_iter().collect())) } } @@ -216,18 +223,29 @@ fn method_defs<'a, I: Iterator>(item_fns: I, fmc: bool) -> Meth let mut methods = Methods::new(); for item_fn in item_fns { - let root = root(fmc); let name = item_fn.sig.ident.clone(); - let num_args = item_fn.sig.inputs.len(); - let is_abstract = has_abstract_type(args(&item_fn.sig)); - let types = types(args(&item_fn.sig), &item_fn.sig.output, is_abstract, fmc); - let closure = create_closure(&item_fn, fmc); - let insertion = get_insertion_function(is_abstract); - let variant = get_variant(num_args, &item_fn.sig.output); - let inner_func = get_inner_function(num_args, &item_fn.sig.output, fmc); - let constructor = get_inner_constructor(args(&item_fn.sig), &item_fn.sig.output); - let inner_trait = get_inner_trait(args(&item_fn.sig), &item_fn.sig.output, fmc); + let has_vararg = has_vararg_type(args(&item_fn.sig)); + + let is_abstract; + let match_value; + let insertion; + if has_vararg { + let funcs = vararg_functions(&item_fn, fmc); + let values = funcs.iter().map(|f| function(f,fmc)); + let positionals = item_fn.sig.inputs.len() - 1; + + is_abstract = true; + match_value = quote!((#positionals, ::std::vec![#(#values),*])); + insertion = quote!(insert_vararg); + + } else { + is_abstract = has_abstract_type(args(&item_fn.sig)); + match_value = function(item_fn, fmc); + insertion = get_insertion_function(is_abstract); + } + + let types = types(args(&item_fn.sig), &item_fn.sig.output, is_abstract, has_vararg, fmc); if !methods.contains_key(&name) { methods.insert(name.clone(), Vec::new()); @@ -238,28 +256,60 @@ fn method_defs<'a, I: Iterator>(item_fns: I, fmc: bool) -> Meth of_func.push( Method { public: if let Visibility::Public(_) = item_fn.vis { true } else { false }, - - expr: if num_args == 0 { - quote! { - table.#insertion( - #types, - #root Function::#variant(#inner_func::new(#closure)) - ) - } - } else { - quote! { - table.#insertion( - #types, - #root Function::#variant(<#inner_func as #inner_trait>::#constructor(#closure)) - ) - } - } + expr: quote!(table.#insertion(#types, #match_value)) } ); } methods } + +fn vararg_functions(origin: &ItemFn, fmc: bool) -> Vec { + let root = root(fmc); + let vis = &origin.vis; + let body = &origin.block; + let output = &origin.sig.output; + let num_args = origin.sig.inputs.len() - 1; + let p_args = args(&origin.sig).take(num_args).collect::>(); + let p_names = args(&origin.sig).take(num_args).map(arg_name).collect::>(); + let vararg_arg = origin.sig.inputs.iter().last().unwrap(); + let vararg_name = arg_name(vararg_arg.clone()); + let vararg_type = vararg(&arg_type(vararg_arg.clone()), fmc); + + let mut functions = Vec::new(); + + for i in (num_args..=MAX_ARGS).rev() { + let vs = (i..MAX_ARGS).map(|j| ident!("__VArg_Multimethods_{}", j)).collect::>(); + + functions.push(parse2(quote! { + #vis fn _f(#(#p_args,)* #(#vs: #vararg_type),*) #output { + let __VarargCall = |#(#p_args,)* #vararg_name: #root Vararg<#vararg_type>| #body; + __VarargCall(#(#p_names,)* #root Vararg::new(::std::vec![#(#vs),*])) + } + }).unwrap()); + } + functions +} + + +fn function(item_fn: &ItemFn, fmc: bool) -> pm2::TokenStream { + let root = root(fmc); + let num_args = item_fn.sig.inputs.len(); + let closure = create_closure(&item_fn, fmc); + let variant = get_variant(num_args, &item_fn.sig.output); + let inner_func = get_inner_function(num_args, &item_fn.sig.output, fmc); + let constructor = get_inner_constructor(args(&item_fn.sig), &item_fn.sig.output); + let inner_trait = get_inner_trait(args(&item_fn.sig), &item_fn.sig.output, fmc); + + if num_args == 0 { + quote!(#root Function::#variant(#inner_func::new(#closure))) + + } else { + quote!(#root Function::#variant(<#inner_func as #inner_trait>::#constructor(#closure))) + } +} + + fn is_public>(vis: I) -> bool { let mut public = None; @@ -301,12 +351,12 @@ fn args(sig: &Signature) -> impl Iterator { } -fn types(inputs: I, output: &ReturnType, is_abs: bool, fmc: bool) -> pm2::TokenStream +fn types(inputs: I, output: &ReturnType, is_abs: bool, has_var: bool, fmc: bool) -> pm2::TokenStream where I: Iterator { if is_abs { - type_matches(inputs, output, fmc) + type_matches(inputs, output, has_var, fmc) } else { type_ids(inputs, output, fmc) @@ -329,7 +379,7 @@ fn type_ids(inputs: I, output: &ReturnType, fmc: bool) -> pm2::TokenStream types.push(quote!(<#ty as #root TypeOf>::associated_type_of())); } - let variant = Ident::new(&format!("T{}", types.len()), pm2::Span::call_site()); + let variant = ident!("T{}", types.len()); let returns_ref = is_ref_return(output); quote! { @@ -338,7 +388,7 @@ fn type_ids(inputs: I, output: &ReturnType, fmc: bool) -> pm2::TokenStream } -fn type_matches(inputs: I, output: &ReturnType, fmc: bool) -> pm2::TokenStream +fn type_matches(inputs: I, output: &ReturnType, has_var: bool, fmc: bool) -> pm2::TokenStream where I: Iterator { @@ -351,14 +401,17 @@ fn type_matches(inputs: I, output: &ReturnType, fmc: bool) -> pm2::TokenStrea for input in inputs { let ty = arg_type(input); + let ty = if let Some(vty) = vararg(&ty, fmc) { vty } else { ty }; + if let Some(aty) = abstract_type(&ty) { types.push(quote!(#type_match::Abstract(#aty))); + } else { types.push(quote!(#type_match::Concrete(<#ty as #sub_type>::#assoc_type()))); } } - let variant = Ident::new(&format!("T{}", types.len()), pm2::Span::call_site()); + let variant = ident!("{}{}", if has_var {"V"} else {"T"}, types.len()); let returns_ref = is_ref_return(output); quote! { @@ -459,6 +512,7 @@ fn get_inner_trait(inputs: I, output: &ReturnType, fmc: bool) -> pm2::TokenSt } } + fn has_abstract_type(inputs: I) -> bool where I: Iterator @@ -471,6 +525,20 @@ fn has_abstract_type(inputs: I) -> bool false } +fn has_vararg_type(inputs: I) -> bool + where + I: Iterator +{ + for input in inputs { + if vararg(&arg_type(input), false).is_some() { + return true; + } + } + false +} + + + fn root(fmc: bool) -> pm2::TokenStream { if fmc { quote!() @@ -479,15 +547,20 @@ fn root(fmc: bool) -> pm2::TokenStream { } } +fn arg_name(arg: FnArg) -> Pat { + if let FnArg::Typed(pat) = arg { + *pat.pat + } else { + panic!("methods cannot have a `self` argument") + } +} + fn arg_type(arg: FnArg) -> Type { if let FnArg::Typed(pat) = arg { *pat.ty } else { - Type::Tuple(TypeTuple { - elems: Punctuated::new(), - paren_token: Paren { span: pm2::Span::call_site() } - }) + panic!("methods cannot have a `self` argument") } } @@ -534,14 +607,13 @@ fn is_ref(ty: &Type) -> bool { } } -fn abstract_type(ty: &Type) -> Option { +fn abstract_type(ty: &Type) -> Option { match ty { Type::Paren(t) => abstract_type(&*t.elem), Type::Macro(m) => { if path_ends_with(&m.mac.path, "Abstract") { - let tokens = m.mac.tokens.clone(); - parse2::(tokens).ok() + parse2::(m.mac.tokens.clone()).ok() } else { None @@ -552,6 +624,30 @@ fn abstract_type(ty: &Type) -> Option { } } +fn vararg(ty: &Type, fmc: bool) -> Option { + match ty { + Type::Paren(t) => vararg(&*t.elem, fmc), + + Type::Macro(m) => { + if path_ends_with(&m.mac.path, "Vararg") { + if m.mac.tokens.is_empty() { + let root = root(fmc); + parse2(quote!(#root Abstract![#root ANY])).ok() + + } else { + parse2(m.mac.tokens.clone()).ok() + } + + } else { + None + } + } + _ => None + } +} + + + fn path_ends_with(p: &Path, s: &str) -> bool { if let Some(segment) = p.segments.iter().last() { segment.ident.to_string() == s.to_string() @@ -561,7 +657,6 @@ fn path_ends_with(p: &Path, s: &str) -> bool { } } - #[allow(dead_code)] fn arg_has_attr(f: &FnArg, attr: &str) -> bool { match f { diff --git a/multimethods_tests/src/main.rs b/multimethods_tests/src/main.rs index 465443f..c22bcbd 100644 --- a/multimethods_tests/src/main.rs +++ b/multimethods_tests/src/main.rs @@ -293,6 +293,35 @@ mod readme { assert_eq!(IS_MY_COLLECTION(coll2), true); } } + + mod vararg { + use multimethods::{multifunction, FromValue}; + + multifunction! { + fn SUM(args: Vararg![i32]) -> i32 { + args.iter().sum() + } + } + + multifunction! { + fn PRINT_ALL(args: Vararg![]) -> Vec { + let mut result = Vec::new(); + for arg in args { + result.push(format!("{}", arg)) + } + result + } + } + + #[test] + fn readme_vararg() { + assert_eq!(SUM(1, 2, 3), 6); + assert_eq!( + >::from_value(PRINT_ALL("a", 2)), + vec!["a".to_string(), "2".to_string()] + ); + } + } } fn main() { diff --git a/src/main.rs b/src/main.rs index 5059305..6f2459d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,6 +34,29 @@ pub use conversion::*; use multimethods_proc::*; +#[__fmc] +multifunction! { + fn var(args: Vararg![i32]) { + for (i, arg) in args.iter().enumerate() { + println!("{}: {}", i, arg); + } + } + + fn var(a: i64, args: Vararg![i64]) { + println!("a: {}", a); + for (i, arg) in args.iter().enumerate() { + println!("{}: {}", i, arg); + } + } + + fn var(a: i64, args: Vararg![]) { + println!("ABSTRACT a: {}", a); + for (i, arg) in args.iter().enumerate() { + println!("ABSTRACT {}: {}", i, arg); + } + } +} + #[__fmc] multifunction! { fn hi() -> String { @@ -95,13 +118,6 @@ multifunction! { } } -#[__fmc] -multifunction! { - fn sunga(a: i32) -> i32 { - a + 1 - } -} - #[__fmc] multifunction! { fn debugin(a: i32) -> String { @@ -171,9 +187,9 @@ fn main() { println!("hi0: {}", hi()); println!("hi1: {}", hi(String::from("john"))); - let mut k = 0i32.into_value(); - for _ in 0..=200_000 { - k = sunga(k); - } - println!("{}", k); + var(); + var(1); + var(1,2); + var(1i64, 2i64); + var(1i64, 1, 2); } diff --git a/src/method.rs b/src/method.rs index 9a54e43..5411ca3 100644 --- a/src/method.rs +++ b/src/method.rs @@ -113,6 +113,10 @@ impl MethodTable { self.abstracts.insert(key.0, value, key.1); } + pub fn insert_vararg(&mut self, key: AbstractTypeKey, (p, values): (usize, Vec)) { + self.abstracts.insert_vararg(key.0, p, values, key.1); + } + pub fn get(&self, key: T) -> &Method { get_method!(self, key, get) } diff --git a/src/types/abs.rs b/src/types/abs.rs index 7078958..b65c47f 100644 --- a/src/types/abs.rs +++ b/src/types/abs.rs @@ -52,7 +52,7 @@ pub macro impl_abstract_type($($type: ty: $abstract: expr),*$(,)?) { } -pub macro Abstract($t: ident) { +pub macro Abstract($t: path) { Value } @@ -217,6 +217,28 @@ impl AsTypeMatches for T { } } +impl TypeMatches { + pub fn len(&self) -> usize { + use TypeMatches::*; + match self { + T0() => 0, + T1(..) => 1, + T2(..) => 2, + T3(..) => 3, + T4(..) => 4, + T5(..) => 5, + T6(..) => 6, + T7(..) => 7, + T8(..) => 8, + T9(..) => 9, + T10(..) => 10, + T11(..) => 11, + T12(..) => 12, + _ => panic!("unknown size") + } + } +} + pub fn matches_all (a: &TypeMatches, b: &T) -> bool { use TypeMatches::*; diff --git a/src/types/mod.rs b/src/types/mod.rs index 2fc2507..9479b55 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -15,6 +15,9 @@ pub use abstract_impl::*; pub mod type_value; pub use type_value::*; +pub mod vararg; +pub use vararg::*; + type TI = TypeId; #[derive(Debug,Clone,PartialEq,Eq,Hash)] pub enum TypeIds { diff --git a/src/types/type_match_tree.rs b/src/types/type_match_tree.rs index 905fe2e..d6a65eb 100644 --- a/src/types/type_match_tree.rs +++ b/src/types/type_match_tree.rs @@ -1,7 +1,12 @@ use crate::types::*; +pub enum MatchValue { + Single(T), + Vararg(usize, Vec) +} + pub struct TypeMatchNode { - value: T, + value: MatchValue, type_match: TypeMatches, children: Vec> } @@ -31,6 +36,10 @@ impl TypeMatchTree { insert_to_children(self.children_mut(rr), TypeMatchNode::new(key, value)); } + pub fn insert_vararg(&mut self, key: TypeMatches, p: usize, values: Vec, rr: bool) { + insert_to_children(self.children_mut(rr), TypeMatchNode::new_vararg(key, p, values)); + } + pub fn get<'a>(&'a self, key: &TypeMatches, rr: bool) -> Option<&'a T> { get_from_children(self.children(rr), key) } @@ -47,13 +56,50 @@ impl TypeMatchTree { impl TypeMatchNode { fn new(key: TypeMatches, value: T) -> Self { TypeMatchNode { - value, + value: MatchValue::Single(value), + type_match: key, + children: Vec::new() + } + } + + fn new_vararg(key: TypeMatches, positionals: usize, values: Vec) -> Self { + TypeMatchNode { + value: MatchValue::Vararg(positionals, values), type_match: key, children: Vec::new() } } } +impl MatchValue { + fn get<'a>(&'a self, key: &TypeMatches) -> &'a T { + match self { + MatchValue::Single(value) => value, + MatchValue::Vararg(n,values) => { + &values[key.len() - n] + } + } + } + + fn get_mut<'a>(&'a mut self, key: &TypeMatches) -> &'a mut T { + match self { + MatchValue::Single(value) => value, + MatchValue::Vararg(n,values) => { + &mut values[key.len() - *n] + } + } + } + + fn remove(self, key: &TypeMatches) -> T { + match self { + MatchValue::Single(value) => value, + MatchValue::Vararg(n, mut values) => { + values.remove(key.len() - n) + } + } + } +} + fn insert_to_children( children: &mut Vec>, mut node: TypeMatchNode) { @@ -100,7 +146,7 @@ fn get_from_children<'a, T>( return Some(value); } else { - return Some(&child.value); + return Some(child.value.get(key)); } } } @@ -116,7 +162,7 @@ fn get_from_children_mut<'a, T>( return Some(value) } else { - return Some(&mut child.value) + return Some(child.value.get_mut(key)) } } } @@ -134,7 +180,7 @@ fn remove_from_children<'a, T> ( } else { let mut m = children.remove(i); children.append(&mut m.children); - return Some(m.value); + return Some(m.value.remove(key)); } } } diff --git a/src/types/vararg.rs b/src/types/vararg.rs new file mode 100644 index 0000000..2e6e08c --- /dev/null +++ b/src/types/vararg.rs @@ -0,0 +1,73 @@ +use std::ops::*; +use std::iter::*; + +pub struct Vararg { + elements: Vec +} + +pub macro Vararg { + () => { Abstract![ANY] }, + ($T: ty) => { $T } +} + +impl Vararg { + pub fn new(elements: Vec) -> Self { + Vararg { elements } + } + + pub fn iter(&self) -> impl Iterator { + self.elements.iter() + } + + pub fn iter_mut(&mut self) -> impl Iterator { + self.elements.iter_mut() + } +} + +impl<'a, T> IntoIterator for &'a Vararg { + type Item = &'a T; + type IntoIter = <&'a Vec as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + (&self.elements).into_iter() + } +} + +impl<'a, T> IntoIterator for &'a mut Vararg { + type Item = &'a mut T; + type IntoIter = <&'a mut Vec as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + (&mut self.elements).into_iter() + } +} + +impl<'a, T> IntoIterator for Vararg { + type Item = T; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.elements.into_iter() + } +} + +impl Index for Vararg + where + I: std::slice::SliceIndex<[T]> +{ + type Output = as Index>::Output; + + fn index(&self, index: I) -> &Self::Output { + &self.elements[index] + } +} + +impl IndexMut for Vararg + where + I: std::slice::SliceIndex<[T]> +{ + fn index_mut(&mut self, index: I) -> &mut Self::Output { + &mut self.elements[index] + } +} +