From e8359dfe571641f6b76d7348453d76aaa6ea64b8 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Wed, 7 May 2025 14:36:14 +0300 Subject: [PATCH] wip: proc: command macro (pt.2) --- proc/src/proc_command.rs | 179 +++++++++++++++++++++++++++------------ 1 file changed, 127 insertions(+), 52 deletions(-) diff --git a/proc/src/proc_command.rs b/proc/src/proc_command.rs index 8b16dec..54028b9 100644 --- a/proc/src/proc_command.rs +++ b/proc/src/proc_command.rs @@ -4,72 +4,150 @@ use crate::*; pub(crate) struct CommandDef(pub(crate) CommandMeta, pub(crate) CommandImpl); #[derive(Debug, Clone)] -pub(crate) struct CommandMeta { - target: Ident, -} +pub(crate) struct CommandMeta(Ident); #[derive(Debug, Clone)] pub(crate) struct CommandImpl(ItemImpl, BTreeMap, CommandArm>); #[derive(Debug, Clone)] -struct CommandVariant(Ident, Vec); +struct CommandArm(Ident, Vec, ReturnType); #[derive(Debug, Clone)] -struct CommandArm(Arc, Ident, Vec, ReturnType); +struct CommandVariant(Ident, Vec); impl Parse for CommandMeta { fn parse (input: ParseStream) -> Result { - Ok(Self { - target: input.parse::()?, - }) + Ok(Self(input.parse::()?)) } } impl Parse for CommandImpl { fn parse (input: ParseStream) -> Result { - let block = input.parse::()?; + let block = input.parse::()?; + let exposed = Self::collect(&block.items).map_err(|e|input.error(e))?; + Ok(Self(block, exposed)) + } +} + +impl CommandImpl { + fn collect (items: &Vec) + -> std::result::Result, CommandArm>, String> + { let mut exposed: BTreeMap, CommandArm> = Default::default(); - for item in block.items.iter() { - if let ImplItem::Fn(ImplItemFn { - sig: Signature { ident, inputs, output, .. }, .. - }) = item { - let key: Arc = - format!("{}", AsKebabCase(format!("{}", &ident))).into(); - let variant: Arc = - format!("{}", AsUpperCamelCase(format!("{}", &ident))).into(); + for item in items.iter() { + if let ImplItem::Fn( + ImplItemFn { sig: Signature { ident, inputs, output, .. }, .. } + ) = item { + let key = CommandArm::ident_to_key(&ident); if exposed.contains_key(&key) { - return Err(input.error(format!("already defined: {ident}"))); + return Err(format!("already defined: {ident}")); } exposed.insert(key, CommandArm( - variant, ident.clone(), inputs.iter().map(|x|x.clone()).collect(), output.clone(), )); } } - Ok(Self(block, exposed)) + Ok(exposed) + } +} + +impl CommandArm { + fn to_key (&self) -> Arc { + Self::ident_to_key(&self.0) + } + fn to_enum_variant_ident (&self) -> Ident { + Ident::new(&Self::ident_to_enum_variant(&self.0), Span::call_site()) + } + fn ident_to_key (ident: &Ident) -> Arc { + format!("{}", AsKebabCase(format!("{ident}"))).into() + } + fn ident_to_enum_variant (ident: &Ident) -> Arc { + format!("{}", AsUpperCamelCase(format!("{ident}"))).into() + } + fn to_enum_variant (&self) -> TokenStream2 { + let mut out = TokenStream2::new(); + out.append(self.to_enum_variant_ident()); + if self.1.len() > 2 { + out.append(Group::new(Delimiter::Brace, { + let mut out = TokenStream2::new(); + for arg in self.1.iter().skip(2) { + if let FnArg::Typed(PatType { attrs, pat, colon_token, ty }) = arg { + pat.to_tokens(&mut out); + out.append(Punct::new(':', Alone)); + ty.to_tokens(&mut out); + out.append(Punct::new(',', Alone)); + } + } + out + })); + } + out.append(Punct::new(',', Alone)); + out + } + fn to_matcher (&self) -> TokenStream2 { + let mut out = TokenStream2::new(); + let key = LitStr::new(&self.to_key(), Span::call_site()); + let ident = &self.0; + let mut take_args = vec![TokenStream2::new();0]; + let mut take_rest = TokenStream2::new(); + for token in quote! { + Some(::tengri::dsl::Token { value: Value::Key(#key), .. }) => { + let mut iter = iter.clone(); + #(#take_args)* + #(#take_rest)? + self.#ident() + }, + } { + out.append(token); + } + out + //$(Some(Token { value: Value::Key($key), .. }) => { + //let mut iter = iter.clone(); + //$( + //let next = iter.next(); + //if next.is_none() { panic!("no argument: {}", stringify!($arg)); } + //let $arg = next.unwrap(); + //$(let $arg: Option<$type> = Context::<$type>::get($state, &$arg.value);)? + //)* + //$(let $rest = iter.clone();)? + //return $command + //}),* + } + fn to_implementation (&self) -> TokenStream2 { + let mut out = TokenStream2::new(); + let mut variant = TokenStream2::new(); + let mut call = TokenStream2::new(); + for token in quote! { + #variant => #call, + } { + out.append(token); + } + out } } impl ToTokens for CommandDef { fn to_tokens (&self, out: &mut TokenStream2) { - let Self(CommandMeta { target }, CommandImpl(block, exposed)) = self; - let enumeration = &block.self_ty; - let definitions = exposed.values().map(|x|CommandVariant( - x.1.clone(), - x.2.clone(), - )); - let implementations = exposed.values().map(|x|CommandArm( - x.0.clone(), - x.1.clone(), - x.2.clone(), - x.3.clone(), - )); + let Self(CommandMeta(target), CommandImpl(block, exposed)) = self; + let enumeration = &block.self_ty; + let variants = exposed.values().map(CommandArm::to_enum_variant); + let matchers = exposed.values().map(CommandArm::to_matcher); + let implementations = exposed.values().map(CommandArm::to_implementation); for token in quote! { #block enum #enumeration { - #(#definitions)* + #(#variants)* + } + impl<'a> TryFromDsl<'a, #target> for #enumeration { + fn try_from_expr (state: &#target, iter: TokenIter) -> Option { + let mut iter = iter.clone(); + match iter.next() { + #(#matchers)* + _ => None + } + } } impl Command<#target> for #enumeration { fn execute (self, state: &mut #target) -> Perhaps { @@ -81,6 +159,9 @@ impl ToTokens for CommandDef { } { out.append(token) } + if exposed.len() > 0 { + panic!("\n{}", quote! {#out}); + } } } @@ -92,9 +173,7 @@ impl ToTokens for CommandVariant { out.append(Group::new(Delimiter::Parenthesis, { let mut out = TokenStream2::new(); for arg in args.iter() { - if let FnArg::Typed(PatType { - attrs, pat, colon_token, ty - }) = arg { + if let FnArg::Typed(PatType { attrs, pat, colon_token, ty }) = arg { out.append(LitStr::new( &format!("{}", quote! { #ty }), Span::call_site() @@ -110,34 +189,30 @@ impl ToTokens for CommandVariant { impl ToTokens for CommandArm { fn to_tokens (&self, out: &mut TokenStream2) { - let Self(symbol, ident, args, returnType) = self; - out.append(Punct::new(':', Joint)); - out.append(Punct::new(':', Alone)); - out.append(Ident::new("tengri", Span::call_site())); - out.append(Punct::new(':', Joint)); - out.append(Punct::new(':', Alone)); - out.append(Ident::new("dsl", Span::call_site())); - out.append(Punct::new(':', Joint)); - out.append(Punct::new(':', Alone)); - out.append(Ident::new("Value", Span::call_site())); - out.append(Punct::new(':', Joint)); - out.append(Punct::new(':', Alone)); - out.append(Ident::new("Sym", Span::call_site())); + let Self(ident, args, returnType) = self; + for ident in ["tengri", "dsl", "Value", "Sym"].iter() { + out.append(Punct::new(':', Joint)); + out.append(Punct::new(':', Alone)); + out.append(Ident::new(ident, Span::call_site())); + } out.append(Group::new(Delimiter::Parenthesis, { let mut out = TokenStream2::new(); + out.append(self.to_enum_variant_ident()); for arg in args.iter() { - out.append(LitStr::new(&symbol, Span::call_site()).token()); } out })); out.append(Punct::new('=', Joint)); out.append(Punct::new('>', Alone)); - out.append(LitStr::new(&format!("{}", ident), Span::call_site()).token()); + out.append(Ident::new("self", Span::call_site())); + out.append(Punct::new('.', Alone)); + out.append(ident.clone()); out.append(Group::new(Delimiter::Parenthesis, { let mut out = TokenStream2::new(); for arg in args.iter() { // TODO - //out.append(LitStr::new(&symbol, Span::call_site()).token()); + out.append(LitStr::new(&self.to_key(), Span::call_site()).token()); + out.append(Punct::new(',', Alone)); } out }));