mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-08-28 12:56:57 +02:00
This commit is contained in:
parent
68236a7210
commit
ae496987c8
7 changed files with 458 additions and 172 deletions
11
proc/Cargo.toml
Normal file
11
proc/Cargo.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "tek_proc"
|
||||
description = "Shorthands for implementing Tek commands."
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
[lib]
|
||||
proc-macro = true
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.106"
|
||||
quote = "1.0.46"
|
||||
syn = { version = "2.0.119", features = ["full", "extra-traits"] }
|
||||
189
proc/src/lib.rs
Normal file
189
proc/src/lib.rs
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
use proc_macro::{TokenStream, Literal};
|
||||
use proc_macro2::{TokenStream as TokenStream2, Span};
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
use std::collections::{HashMap, BTreeMap};
|
||||
use syn::{
|
||||
Path, ItemEnum, Ident, Type, Variant, MetaList, Expr, ExprPath, ExprBinary, Fields, BinOp,
|
||||
parse::{ParseStream, Parse, Result}
|
||||
};
|
||||
|
||||
macro_rules! attribute {
|
||||
($name:ident { $($body:tt)* }) => {
|
||||
#[proc_macro_attribute] pub fn $name (meta: TokenStream, item: TokenStream) -> TokenStream {
|
||||
write(self::$name::Def(
|
||||
syn::parse_macro_input!(meta as self::$name::Meta),
|
||||
syn::parse_macro_input!(item as self::$name::Item),
|
||||
))
|
||||
}
|
||||
mod $name {
|
||||
use crate::*;
|
||||
$($body)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attribute!(command {
|
||||
#[derive(Debug, Clone)] pub struct Def(
|
||||
pub Meta, pub Item
|
||||
);
|
||||
#[derive(Debug, Clone)] pub struct Meta(
|
||||
pub Path
|
||||
);
|
||||
#[derive(Debug, Clone)] pub struct Item(
|
||||
pub ItemEnum, pub HashMap<Ident, (Fields, Expr)>
|
||||
);
|
||||
impl Parse for Meta {
|
||||
fn parse (input: ParseStream) -> Result<Self> {
|
||||
Ok(Self(input.parse()?))
|
||||
}
|
||||
}
|
||||
impl Parse for Item {
|
||||
fn parse (input: ParseStream) -> Result<Self> {
|
||||
let mut item: ItemEnum = input.parse()?;
|
||||
let mut branches: HashMap<Ident, (Fields, Expr)> = Default::default();
|
||||
let mut variants_filtered = item.variants.clone();
|
||||
variants_filtered.clear();
|
||||
for variant in item.variants.iter_mut() {
|
||||
let Variant { attrs, ident, fields, discriminant } = variant;
|
||||
let mut attrs_filtered = attrs.clone();
|
||||
attrs_filtered.clear();
|
||||
for attr in attrs.iter() {
|
||||
if let syn::Meta::List(MetaList { ref path, ref tokens, .. }) = attr.meta
|
||||
&& path == &Path::from(Ident::new("command", Span::call_site()))
|
||||
&& let Ok(handler) = syn::parse2::<Expr>(tokens.clone())
|
||||
{
|
||||
branches.insert(ident.clone(), (fields.clone(), handler));
|
||||
} else {
|
||||
attrs_filtered.push(attr.clone());
|
||||
}
|
||||
}
|
||||
*attrs = attrs_filtered;
|
||||
variants_filtered.push(variant.clone())
|
||||
}
|
||||
item.variants = variants_filtered;
|
||||
Ok(Self(item, branches))
|
||||
}
|
||||
}
|
||||
impl ToTokens for Def {
|
||||
fn to_tokens (&self, out: &mut TokenStream2) {
|
||||
let Self(Meta(state), Item(item, branches)) = self;
|
||||
let ident = &item.ident;
|
||||
let mut body = quote! {};
|
||||
for (variant, (fields, handler)) in branches.iter() {
|
||||
let (handler, wrapper) = if let Expr::Path(ExprPath { path, .. }) = handler {
|
||||
(path, None)
|
||||
} else if let Expr::Binary(ExprBinary {
|
||||
op: BinOp::BitOr(_), left, right, ..
|
||||
}) = handler && let Expr::Path(ExprPath {
|
||||
path: handler, ..
|
||||
}) = &**left && let Expr::Path(ExprPath {
|
||||
path: wrapper, ..
|
||||
}) = &**right {
|
||||
(handler, Some(wrapper))
|
||||
} else {
|
||||
panic!()
|
||||
};
|
||||
match fields {
|
||||
Fields::Named(fields) => todo!("named command fields"),
|
||||
Fields::Unnamed(fields) => {
|
||||
let mut params = quote! {};
|
||||
let mut values = quote! {};
|
||||
for (index, _field) in fields.unnamed.iter().enumerate() {
|
||||
let name = Ident::new(&format!("arg{index}"), Span::call_site());
|
||||
append(&mut params, quote! { #name, });
|
||||
append(&mut values, quote! { #name, });
|
||||
}
|
||||
let invocation = if let Some(wrapper) = wrapper {
|
||||
quote! { Ok(Some(#wrapper(#handler(state, #values)))) }
|
||||
} else {
|
||||
quote! { #handler(state, #values) }
|
||||
};
|
||||
append(&mut body, quote! {
|
||||
#ident::#variant (#params) => #invocation,
|
||||
});
|
||||
},
|
||||
_ => {
|
||||
append(&mut body, quote! {
|
||||
#ident::#variant => #handler(state),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
append(out, quote! {
|
||||
#item
|
||||
|
||||
impl dizzle::Act<#state> for #ident {
|
||||
fn act (&self, state: &mut #state) -> Perhaps<Self> {
|
||||
match self {
|
||||
#body
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
attribute!(keyword {
|
||||
#[derive(Debug, Clone)] pub struct Def(
|
||||
pub Meta, pub Item
|
||||
);
|
||||
#[derive(Debug, Clone)] pub struct Meta(
|
||||
pub Path
|
||||
);
|
||||
#[derive(Debug, Clone)] pub struct Item(
|
||||
pub ItemEnum, pub BTreeMap<Ident, Vec<(Literal, BTreeMap<Ident, Type>)>>
|
||||
);
|
||||
impl Parse for Meta {
|
||||
fn parse (input: ParseStream) -> Result<Self> {
|
||||
Ok(Self(input.parse()?))
|
||||
}
|
||||
}
|
||||
impl Parse for Item {
|
||||
fn parse (input: ParseStream) -> Result<Self> {
|
||||
let mut item: ItemEnum = input.parse()?;
|
||||
for Variant { attrs, ident, fields, discriminant } in item.variants.iter_mut() {
|
||||
attrs.retain(|attr|if let syn::Meta::List(MetaList {
|
||||
ref path, ref tokens, ..
|
||||
}) = attr.meta && path == &Path::from(Ident::new(
|
||||
"keyword", Span::call_site()
|
||||
)) {
|
||||
// TODO
|
||||
false
|
||||
} else {
|
||||
true
|
||||
});
|
||||
}
|
||||
Ok(Self(item, Default::default()))
|
||||
}
|
||||
}
|
||||
impl ToTokens for Def {
|
||||
fn to_tokens (&self, out: &mut TokenStream2) {
|
||||
let Self(Meta(state), Item(item, variants)) = self;
|
||||
let ident = &item.ident;
|
||||
let body = quote! {};
|
||||
append(out, quote! {
|
||||
#item
|
||||
|
||||
impl<'a> Namespace<'a, #ident> for #state {
|
||||
symbols!('a |stte| -> #ident {
|
||||
#body,
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fn write <T: ToTokens> (t: T) -> TokenStream {
|
||||
let mut out = TokenStream2::new();
|
||||
t.to_tokens(&mut out);
|
||||
out.into()
|
||||
}
|
||||
|
||||
fn append (out: &mut TokenStream2, quote: TokenStream2) {
|
||||
for token in quote {
|
||||
out.append(token);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue