diff --git a/proc/src/proc_command.rs b/proc/src/proc_command.rs index ac1e2a9..40f8140 100644 --- a/proc/src/proc_command.rs +++ b/proc/src/proc_command.rs @@ -4,72 +4,133 @@ 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 (&self) -> CommandVariant { + CommandVariant(self.to_enum_variant_ident(), self.1.clone()) + } + 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_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 +142,9 @@ impl ToTokens for CommandDef { } { out.append(token) } + if exposed.len() > 0 { + panic!("{}", quote! {#out}); + } } } @@ -108,34 +172,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 })); diff --git a/proc/src/proc_expose.rs b/proc/src/proc_expose.rs index bf7ae58..9315f2e 100644 --- a/proc/src/proc_expose.rs +++ b/proc/src/proc_expose.rs @@ -81,7 +81,7 @@ impl ToTokens for ExposeImpl { ], _ => vec![], }; - let values = variants.values(); + let values = variants.iter().map(|(k, v)|ExposeArm(k.clone(), v.clone())); let trait_impl = quote! { impl ::tengri::dsl::Context<#t> for #target { fn get (&self, dsl: &::tengri::dsl::Value) -> Option<#t> { @@ -97,6 +97,9 @@ impl ToTokens for ExposeImpl { out.append(token); } } + //if exposed.len() > 0 { + //panic!("{}", quote! {#out}); + //} } } @@ -122,9 +125,12 @@ impl ToTokens for ExposeArm { })); out.append(Punct::new('=', Joint)); out.append(Punct::new('>', Alone)); + out.append(Ident::new("self", Span::call_site())); + out.append(Punct::new('.', Alone)); for token in quote! { #value } { out.append(token); } + out.append(Group::new(Delimiter::Parenthesis, TokenStream2::new())); } }