diff --git a/.gitignore b/.gitignore index 2f7896d..17bd830 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ target/ + +.direnv +.envrc \ No newline at end of file diff --git a/README.md b/README.md index d8d9413..4ba329b 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,95 @@ **Misses** is a DSL compiler that transforms `.mrs` macro definitions into ready-to-use Rust proc macro source files. Write your macro once in a concise, readable syntax; Misses generates the full `syn`/`quote` boilerplate for you. + + +--- + +## Why Misses? + +Writing proc macros means wrestling with `syn` parsing, `quote!` boilerplate, error handling, and variadic patterns. You write the same patterns over and over. + +**Misses lets you write the macro once, in readable syntax, and generates all the boilerplate for you.** + +Here's a real example: + +### `derive_getters.mrs` + +```mrs +macro derive_getters! { + struct $name:ident { $($field:ident : $ty:ty),* } => { + T~{ + impl $name { + for field in $fields => + pub fn $field.name(&self) -> &$field.ty { &self.$field.name } + } + } + } +} +``` + +
+ + + Generated output + + +```rust + +extern crate proc_macro; +#[proc_macro] +pub fn derive_rpc(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = proc_macro2::TokenStream::from(input); + use quote::format_ident; + let (name, params, ret) = { + use syn::parse::Parser as _; + let __parser = | + _input: syn::parse::ParseStream, + | -> syn::Result<(syn::Ident, Vec<(syn::Ident, syn::Type)>, syn::Type)> { + let name: syn::Ident = _input.parse()?; + let params = { + let mut __f = Vec::new(); + while !__brace_content.is_empty() { + let __fn: syn::Ident = __brace_content.parse()?; + __brace_content.parse::()?; + let __ft: syn::Type = __brace_content.parse()?; + __f.push((__fn, __ft)); + if __brace_content.peek(syn::Token![,]) { + __brace_content.parse::()?; + } + } + __f + }; + let ret: syn::Type = _input.parse()?; + Ok((name, params, ret)) + }; + match __parser.parse2(input.clone()) { + Ok(v) => v, + Err(e) => return e.into_compile_error().into(), + } + }; + let req_name = format_ident!("{}Request", name); + let res_name = format_ident!("{}Response", name); + let __spread_0: proc_macro2::TokenStream = params + .iter() + .map(|(__field_name, __field_ty)| { + quote::quote! { + pub # __field_name : # __field_ty, + } + }) + .collect(); + let __output = quote::quote! { + pub struct # req_name { # __spread_0 } pub struct # res_name { pub result : # + ret, } + }; + __output.into() +} + +``` + +
+ + --- ## Quick start diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..46c57b3 --- /dev/null +++ b/shell.nix @@ -0,0 +1,22 @@ +{ pkgs ? import {}}: +pkgs.mkShell { + buildInputs = with pkgs; [ + # Rust toolchain + cargo + rustc + rustfmt + clippy + rust-analyzer + + # Additional tools + cargo-watch + cargo-edit + # Profiling tools + cargo-flamegraph + samply + perf + ]; + + # Environment variables + RUST_BACKTRACE = 1; +}