use quote! for generating code instead

This commit is contained in:
alyssa 2024-09-13 18:34:32 +09:00
parent c3cc5c9d03
commit fd7d3e6a3a
2 changed files with 50 additions and 36 deletions

View file

@ -5,3 +5,7 @@ edition = "2021"
[lib] [lib]
proc-macro = true proc-macro = true
[dependencies]
quote = "1.0"
proc-macro2 = "1.0"

View file

@ -1,19 +1,28 @@
use proc_macro::{Delimiter, TokenStream, TokenTree}; use proc_macro::{Delimiter, TokenStream, TokenTree};
use proc_macro2::{Ident, Literal, Span};
use quote::quote;
fn make_command(tokens: Vec<String>, help: String, cb: String) -> String { fn make_command(
let tokens = tokens tokens: Vec<proc_macro2::TokenStream>,
.iter() help: String,
.map(|v| format!("Token::{v}")) cb: String,
.collect::<Vec<String>>() ) -> proc_macro2::TokenStream {
.join(","); let help = Literal::string(&help);
format!( let cb = Literal::string(&cb);
r#"Command {{ tokens: vec![{tokens}], help: {help}.to_string(), cb: "{cb}".to_string() }}"#
) quote! {
Command { tokens: vec![#(#tokens),*], help: #help.to_string(), cb: #cb.to_string() }
}
} }
fn command_from_stream(stream: TokenStream) -> String { // horrible, but the best way i could find to do this
fn token_to_string(i: String) -> String {
i.to_string()[1..i.to_string().len() - 1].to_string()
}
fn command_from_stream(stream: TokenStream) -> proc_macro2::TokenStream {
let mut part = 0; let mut part = 0;
let mut found_tokens: Vec<String> = Vec::new(); let mut found_tokens: Vec<proc_macro2::TokenStream> = Vec::new();
let mut found_cb: Option<String> = None; let mut found_cb: Option<String> = None;
let mut found_help: Option<String> = None; let mut found_help: Option<String> = None;
@ -25,9 +34,11 @@ fn command_from_stream(stream: TokenStream) -> String {
None if part == 2 && found_help.is_some() => break 'a, None if part == 2 && found_help.is_some() => break 'a,
Some(TokenTree::Ident(ident)) if part == 0 => { Some(TokenTree::Ident(ident)) if part == 0 => {
found_tokens.push(if is_token_lit { found_tokens.push(if is_token_lit {
format!("{ident}") let ident = Ident::new(ident.to_string().as_str(), Span::call_site());
quote! { Token::#ident }.into()
} else { } else {
format!("Value(vec![\"{ident}\".to_string()])") let ident = Literal::string(format!("{ident}").as_str());
quote! { Token::Value(vec![#ident.to_string() ]) }
}); });
// reset this // reset this
is_token_lit = false; is_token_lit = false;
@ -42,7 +53,9 @@ fn command_from_stream(stream: TokenStream) -> String {
part += 1 part += 1
} }
Some(TokenTree::Ident(ident)) if part == 1 => found_cb = Some(format!("{ident}")), Some(TokenTree::Ident(ident)) if part == 1 => found_cb = Some(format!("{ident}")),
Some(TokenTree::Literal(lit)) if part == 2 => found_help = Some(format!("{lit}")), Some(TokenTree::Literal(lit)) if part == 2 => {
found_help = Some(token_to_string(lit.to_string()))
}
_ => panic!("invalid command definition: {stream}"), _ => panic!("invalid command definition: {stream}"),
} }
} }
@ -51,7 +64,7 @@ fn command_from_stream(stream: TokenStream) -> String {
#[proc_macro] #[proc_macro]
pub fn commands(stream: TokenStream) -> TokenStream { pub fn commands(stream: TokenStream) -> TokenStream {
let mut commands: Vec<String> = Vec::new(); let mut commands: Vec<proc_macro2::TokenStream> = Vec::new();
let mut top_level_tokens = stream.into_iter(); let mut top_level_tokens = stream.into_iter();
'a: loop { 'a: loop {
@ -77,33 +90,30 @@ pub fn commands(stream: TokenStream) -> TokenStream {
let command_registrations = commands let command_registrations = commands
.iter() .iter()
.map(|v| format!("tree.register_command({v});")) .map(|v| -> proc_macro2::TokenStream { quote! { tree.register_command(#v); }.into() })
.collect::<Vec<String>>() .collect::<proc_macro2::TokenStream>();
.join("\n");
let res = format!( let res = quote! {
r#" lazy_static::lazy_static! {
lazy_static::lazy_static! {{ static ref COMMAND_TREE: TreeBranch = {
static ref COMMAND_TREE: TreeBranch = {{ let mut tree = TreeBranch {
let mut tree = TreeBranch {{ current_command_key: None,
current_command_key: None, possible_tokens: vec![],
possible_tokens: vec![], branches: HashMap::new(),
branches: HashMap::new(), };
}};
{command_registrations} #command_registrations
tree.sort_tokens(); tree.sort_tokens();
// println!("{{tree:#?}}"); // println!("{{tree:#?}}");
tree tree
}}; };
}} }
"# };
);
// panic!("{res}"); // panic!("{res}");
res.parse().unwrap() res.into()
} }