Making an JSX like Rust Macro
Kazimir Malevich Inspired - O3 Generated

Making an JSX like Rust Macro

Preface

I’ve had this itch to build an Web Framework. Will I actually finish it? … probably not. But hey, posting about the journey keeps me moving, one tiny experiment at a time. If you want to contribute or get 1:1 mentorship, shoot me a DM and come learn with me! Okay, let’s see where this goes!

Inspiration

Jason Miller aka DevelopIt has been a big inspiration for me for a long time. His library HTM is the library that inspired this first step towards making a web framework.

What really go this idea going on is this very recent video from Ryan Carniato the author of SolidJS... A great watch prior to this lesson

Objectives

  • Learn Rust
  • Uncompromising Performance and DX Framework
  • Low Code Tools

Step one: macros_rules!

There are 2 types of macros in rust "declarative" and "procedural". I'm not smart enough for "procedural", yet. This contrived example below will use "declarative" with the macro_rules! and later I'll make a follow up iteration on the more flexible and advanced procedural

Scaffold & Test

Been using the project name "Candy" and htm because of what I said above...

cargo new candy_htm --lib
cd candy_htm        

Macros in rust use the ! and the format! macro is baked in to rust for string formatting/interpolation.

pub fn htm_element(tag: &str, children: &str) -> String {
    format!("<{}>{}</{}>", tag, children, tag)
}        

Rust comes with tests baked into the language. The syntax is a bit complicated at a glance but we can use gpt or some docs to sus it out:

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn htm_element_test() {
        let result = htm_element("p", "Hello, World");
        assert_eq!(result, "<p>Hello, World</p>");
    }
}
        

Let's validate that the test works:

Article content
cargo test

Creating a Macro

#[macro_export]
macro_rules! htm {
    (<$tag: ident> $content: literal </$end_tag: ident>) => {
        $crate::htm_element(stringify!($tag), $content)
    };
}

#[test]
fn htm_macro() {
    let x = htm!(<p>"hello world"</p>);
    assert_eq!(
        x,
        "<p>hello world</p>"
    )
}        

There is a lot of syntax to unpack here so lets go over it:

Article content
The syntax

Pattern Matching

Below is a "Pattern" that literally matches on the inner htm!(<p>"hello world"</p>)

<$tag: ident> $content: literal </$end_tag: ident>        

Fragment Specifiers

You still must give a macro pattern almost "types" called fragment specifiers... there are a lot

Article content

Crates

Next we need to point our now matched macro & parsed tokens to the input of a the function using the $crate syntax

 $crate::htm_element(stringify!($tag), $content)        

stringify! Turns a token into a string (e.g., div → "div")

Closing

I have like 10 more steps to go

  1. Arbitrary Rust Expressions
  2. HTML Attributes
  3. Allow Nested HTML
  4. Tag Mismatch Safety
  5. Create Component-Like Functions
  6. Build an HTML Tree Struct
  7. Traits for Render-to-String
  8. Migrate to Procedural Macros
  9. Signals / Reactivity


Anzal Husain Abidi

Software Engineer @Zomato | SIH'23 Winner | Building @BeABrand

2mo

I remember trying to use rust to create a rendering engine that was compatible with react using v8 isolates , too bad that there is not a well maintained package for that in golang or rust right now.

Josh Yang

CS @ Brown | HCI Research

2mo

Thanks for sharing! I recently had my first macro writing experience in Rust and it was such a nice meta-programming experience. Normally I work in Typescript, but for a current graphics project I needed that extra boost in compute so rewrote everything in Rust and compiled into WASM. So a round-about way of enjoying rust macros in the browser haha

To view or add a comment, sign in

Others also viewed

Explore topics