From e8d835e196116f41ffbf05069b897bb1333c290f Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 7 Mar 2026 19:23:53 +0000 Subject: [PATCH] Add misses-rt crate, component example, README, and fix codegen for non-keyword literal tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - misses-rt: add Buffer/Markup HTML helpers, name/type helpers, parse_sections/parse_kv, and template AST - codegen: add is_syn_keyword() — non-keyword idents in patterns now generate parse+compare instead of syn::Token![…] - parser: split punct vs ident chars in literal pattern tokens so `props:` becomes two tokens - examples: add component.mrs/.rs demonstrating named literal tokens and tt capture - README: comprehensive guide covering .mrs syntax, running the compiler, misses-rt helpers, and examples https://claude.ai/code/session_01Tppkab4eLsL21asHGjQJoF --- Cargo.lock | 9 + Cargo.toml | 2 +- README.md | 229 ++++++++++++ misses-compiler/examples/component.mrs | 32 ++ misses-compiler/examples/component.rs | 8 + misses-compiler/src/codegen.rs | 33 +- misses-compiler/src/parser.rs | 33 +- misses-rt/Cargo.toml | 9 + misses-rt/src/lib.rs | 207 +++++++++++ misses-rt/src/parse_helpers.rs | 130 +++++++ misses-rt/src/template.rs | 485 +++++++++++++++++++++++++ 11 files changed, 1156 insertions(+), 21 deletions(-) create mode 100644 README.md create mode 100644 misses-compiler/examples/component.mrs create mode 100644 misses-compiler/examples/component.rs create mode 100644 misses-rt/Cargo.toml create mode 100644 misses-rt/src/lib.rs create mode 100644 misses-rt/src/parse_helpers.rs create mode 100644 misses-rt/src/template.rs diff --git a/Cargo.lock b/Cargo.lock index 5b5a2ae..09264f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,6 +12,15 @@ dependencies = [ "syn", ] +[[package]] +name = "misses-rt" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "prettyplease" version = "0.2.37" diff --git a/Cargo.toml b/Cargo.toml index d0af0f8..b54ae9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["misses-compiler"] +members = ["misses-compiler", "misses-rt"] resolver = "2" diff --git a/README.md b/README.md new file mode 100644 index 0000000..ad3bece --- /dev/null +++ b/README.md @@ -0,0 +1,229 @@ +# Misses + +**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. + +--- + +## Quick start + +```bash +# Build everything +cargo build + +# Run a bundled example (compare .mrs source with generated Rust) +cargo run --example make_id_type +cargo run --example derive_builder +cargo run --example derive_getters +cargo run --example declare_table +cargo run --example component + +# Compile a .mrs file to Rust +cargo run --bin misses -- path/to/my_macro.mrs > src/generated.rs +``` + +--- + +## The `.mrs` syntax + +A `.mrs` file contains one or more macro definitions: + +``` +macro macro_name! { + => { + + } +} +``` + +### Pattern bindings + +| Syntax | Parsed as | +|---|---| +| `$name:ident` | A single identifier | +| `$expr:expr` | Any Rust expression | +| `$ty:ty` | A Rust type | +| `$body:tt` | A raw token-tree (brace group) | +| `$($field:ident : $ty:ty),*` | Variadic — zero-or-more comma-separated pairs | + +Literal tokens in the pattern are matched verbatim. Rust keywords use `syn::Token![…]`; arbitrary identifiers are matched by value. + +### Output blocks + +An output block may contain any mix of: + +**Escape block** `@{ … }` — arbitrary Rust code executed at macro-expansion time: + +``` +@{ + let builder_name = format_ident!("{}_Builder", name.to_string()); +} +``` + +**Token template** `T~{ … }` — generates the `quote::quote! { … }` call. Inside a template you may use: + +| Syntax | Meaning | +|---|---| +| `$name` | Interpolate the bound variable `name` | +| `$field.name` / `$field.ty` | Access a field of a variadic element | +| `${ expr }` | Evaluate an arbitrary Rust expression and interpolate the result | +| `for x in $xs => …` | Spread — iterate the variadic collection and repeat a line | +| `#[a => b_$name]` | Construct an identifier from parts (uses `format_ident!`) | + +--- + +## Examples + +### `make_id_type.mrs` — newtype ID wrapper + +``` +macro make_id_type! { + $name:ident => { + T~{ + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct $name(pub u64); + + impl $name { + pub fn new(val: u64) -> Self { Self(val) } + pub fn value(self) -> u64 { self.0 } + } + } + } +} +``` + +### `derive_builder.mrs` — builder pattern + +``` +macro derive_builder! { + struct $name:ident { $($field:ident : $ty:ty),* } => { + @{ + let builder_name = format_ident!("{}_Builder", name.to_string()); + } + T~{ + pub struct ${ builder_name } { + for field in $fields => + pub $field.name: Option<$field.ty>, + } + impl ${ builder_name } { + pub fn build(self) -> $name { + $name { + for field in $fields => + $field.name: self.$field.name.unwrap(), + } + } + } + } + } +} +``` + +### `declare_table.mrs` — DSL to struct with runtime helper + +``` +macro declare_table! { + $name:ident { $body:tt } => { + @{ + let table_str = misses_rt::to_table_name(&name); + } + T~{ + pub struct $name { + pub id: u64, + } + impl $name { + pub const TABLE: &'static str = ${ table_str.as_str() }; + } + } + } +} +``` + +--- + +## The `misses-rt` runtime library + +`misses-rt` provides helpers that generated proc macro code can call at expansion time. Add it to your proc macro crate: + +```toml +[dependencies] +misses-rt = { path = "../misses-rt" } +``` + +### Name helpers + +```rust +misses_rt::to_table_name(&ident) // UsersAccount → "users_accounts" +misses_rt::to_snake_case(&ident) // MyType → "my_type" +misses_rt::to_snake_case_str(s) // "MyType" → "my_type" +misses_rt::to_pascal_case(s) // "my_type" → MyType (Ident) +misses_rt::concat_ident(&[a, b]) // ["foo", "Bar"] → fooBar (Ident) +``` + +### Type helpers + +```rust +misses_rt::is_option(&ty) // Option → true +misses_rt::unwrap_option(&ty) // Option → Some(&T) +misses_rt::to_sql_type(&ty) // syn::Type → "TEXT", "INTEGER", … +``` + +### HTML template helpers + +```rust +let mut buf = misses_rt::Buffer::new(); +buf.push_str(""); +buf.push_escaped("