wip: proc: command macro
Some checks are pending
/ build (push) Waiting to run

This commit is contained in:
🪞👃🪞 2025-05-06 21:33:53 +03:00
parent 7570aefcc2
commit 7df7cb839c
2 changed files with 157 additions and 2 deletions

View file

@ -2,6 +2,7 @@ extern crate proc_macro;
pub(crate) use std::collections::{BTreeMap, BTreeSet};
pub(crate) use std::cmp::Ordering;
pub(crate) use std::sync::Arc;
pub(crate) use proc_macro::TokenStream;
pub(crate) use proc_macro2::{
TokenStream as TokenStream2, TokenTree,
@ -11,16 +12,17 @@ pub(crate) use syn::{
parse, parse_macro_input, parse_quote as pq,
braced, bracketed, parenthesized, Token,
Arm, Expr, Attribute, Meta, MetaList, Path, PathSegment, PathArguments,
ImplItem, ImplItemFn, LitStr, Type, ItemImpl, ReturnType, Signature,
ImplItem, ImplItemFn, LitStr, Type, ItemImpl, ReturnType, Signature, FnArg, PatType,
parse::{Parse, ParseStream, Result},
token::{PathSep, Brace},
punctuated::Punctuated,
};
pub(crate) use quote::{quote, TokenStreamExt, ToTokens};
pub(crate) use heck::AsKebabCase;
pub(crate) use heck::{AsKebabCase, AsUpperCamelCase};
mod proc_view;
mod proc_expose;
mod proc_command;
#[proc_macro_attribute]
pub fn view (meta: TokenStream, item: TokenStream) -> TokenStream {
@ -40,6 +42,15 @@ pub fn expose (meta: TokenStream, item: TokenStream) -> TokenStream {
))
}
#[proc_macro_attribute]
pub fn command (meta: TokenStream, item: TokenStream) -> TokenStream {
use self::proc_command::{CommandDef, CommandMeta, CommandImpl};
write_macro(CommandDef(
parse_macro_input!(meta as CommandMeta),
parse_macro_input!(item as CommandImpl),
))
}
fn write_macro <T: ToTokens> (t: T) -> TokenStream {
let mut out = TokenStream2::new();
t.to_tokens(&mut out);

144
proc/src/proc_command.rs Normal file
View file

@ -0,0 +1,144 @@
use crate::*;
#[derive(Debug, Clone)]
pub(crate) struct CommandDef(pub(crate) CommandMeta, pub(crate) CommandImpl);
#[derive(Debug, Clone)]
pub(crate) struct CommandMeta {
target: Ident,
}
#[derive(Debug, Clone)]
pub(crate) struct CommandImpl(ItemImpl, BTreeMap<Arc<str>, CommandArm>);
#[derive(Debug, Clone)]
struct CommandVariant(Ident, Vec<FnArg>);
#[derive(Debug, Clone)]
struct CommandArm(Arc<str>, Ident, Vec<FnArg>, ReturnType);
impl Parse for CommandMeta {
fn parse (input: ParseStream) -> Result<Self> {
Ok(Self {
target: input.parse::<Ident>()?,
})
}
}
impl Parse for CommandImpl {
fn parse (input: ParseStream) -> Result<Self> {
let block = input.parse::<ItemImpl>()?;
let mut exposed: BTreeMap<Arc<str>, CommandArm> = Default::default();
for item in block.items.iter() {
if let ImplItem::Fn(ImplItemFn {
sig: Signature { ident, inputs, output, .. }, ..
}) = item {
let key: Arc<str> =
format!("{}", AsKebabCase(format!("{}", &ident))).into();
let variant: Arc<str> =
format!("{}", AsUpperCamelCase(format!("{}", &ident))).into();
if exposed.contains_key(&key) {
return Err(input.error(format!("already defined: {ident}")));
}
exposed.insert(key, CommandArm(
variant,
ident.clone(),
inputs.iter().map(|x|x.clone()).collect(),
output.clone(),
));
}
}
Ok(Self(block, exposed))
}
}
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(),
));
for token in quote! {
#block
enum #enumeration {
#(#definitions)*
}
impl Command<#target> for #enumeration {
fn execute (self, state: &mut #target) -> Perhaps<Self> {
match self {
#(#implementations)*
}
}
}
} {
out.append(token)
}
}
}
impl ToTokens for CommandVariant {
fn to_tokens (&self, out: &mut TokenStream2) {
let Self(ident, args) = self;
out.append(LitStr::new(&format!("{}", ident), Span::call_site())
.token());
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 {
out.append(LitStr::new(
&format!("{}", quote! { #ty }),
Span::call_site()
).token());
out.append(Punct::new(',', Alone));
}
}
out
}));
out.append(Punct::new(',', Alone));
}
}
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()));
out.append(Group::new(Delimiter::Parenthesis, {
let mut out = TokenStream2::new();
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(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
}));
out.append(Punct::new(',', Alone));
}
}