Documented
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
//! # RSX Emission
|
||||
//!
|
||||
//! Converts parsed RSX AST into Rust code that constructs RSX objects.
|
||||
|
||||
use crate::parse::*;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
/// Emits code for the root RSX
|
||||
pub fn emit_rsx(root: RsxRoot) -> TokenStream {
|
||||
emit_rsx_vec(&root.nodes)
|
||||
}
|
||||
|
||||
/// Emits code for a vector of RSX nodes
|
||||
pub fn emit_rsx_vec(nodes: &Vec<RsxNode>) -> TokenStream {
|
||||
let nodes = nodes.iter().map(emit_node_scope);
|
||||
|
||||
@@ -16,6 +22,7 @@ pub fn emit_rsx_vec(nodes: &Vec<RsxNode>) -> TokenStream {
|
||||
}}
|
||||
}
|
||||
|
||||
/// Emits variable bindings for dependencies
|
||||
fn emit_deps(deps: &[Dep]) -> TokenStream {
|
||||
deps.iter()
|
||||
.map(|d| {
|
||||
@@ -29,6 +36,7 @@ fn emit_deps(deps: &[Dep]) -> TokenStream {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Emits a Vec of dependencies as HookDependency trait objects
|
||||
fn emit_deps_vec(deps: &[Dep]) -> TokenStream {
|
||||
let deps = deps.iter().map(|d| {
|
||||
let ident = &d.ident;
|
||||
@@ -43,6 +51,7 @@ fn emit_deps_vec(deps: &[Dep]) -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
/// Emits code for a single node within a scope
|
||||
fn emit_node_scope(node: &RsxNode) -> TokenStream {
|
||||
match node {
|
||||
RsxNode::Text(_) => {
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
//! # OSUI Macros
|
||||
//!
|
||||
//! Procedural macros for OSUI that provide ergonomic syntax for defining components.
|
||||
//!
|
||||
//! ## Features
|
||||
//!
|
||||
//! - `#[component]` - Transforms a function into a reusable component with props
|
||||
//! - `rsx!` - Creates RSX (React-like Syntax) for component hierarchies
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{FnArg, ItemFn, Pat, ReturnType, Type, parse_macro_input};
|
||||
@@ -5,12 +14,41 @@ use syn::{FnArg, ItemFn, Pat, ReturnType, Type, parse_macro_input};
|
||||
mod emit;
|
||||
mod parse;
|
||||
|
||||
/// RSX (React-like Syntax) macro for building component hierarchies
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// rsx! {
|
||||
/// Component {
|
||||
/// prop: value,
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn rsx(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as parse::RsxRoot);
|
||||
emit::emit_rsx(ast).into()
|
||||
}
|
||||
|
||||
/// Component attribute macro for defining reusable components
|
||||
///
|
||||
/// Transforms a function into a component with automatic prop handling.
|
||||
/// The first parameter must be `cx: &Arc<Context>`.
|
||||
/// Remaining parameters become component props.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[component]
|
||||
/// pub fn Counter(cx: &Arc<Context>, initial: &i32) -> View {
|
||||
/// let count = use_state(*initial);
|
||||
///
|
||||
/// Arc::new(move |ctx| {
|
||||
/// ctx.draw_text(Point { x: 0, y: 0 }, &format!("Count: {}", count.get_dl()));
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_attribute]
|
||||
pub fn component(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(item as ItemFn);
|
||||
|
||||
+26
-1
@@ -1,3 +1,7 @@
|
||||
//! # RSX Parser
|
||||
//!
|
||||
//! Parses RSX syntax into an AST that can be emitted as Rust code.
|
||||
|
||||
use syn::braced;
|
||||
use syn::parse::discouraged::Speculative;
|
||||
use syn::{
|
||||
@@ -6,7 +10,9 @@ use syn::{
|
||||
token::Brace,
|
||||
};
|
||||
|
||||
/// Root of an RSX expression
|
||||
pub struct RsxRoot {
|
||||
/// Top-level nodes in the RSX
|
||||
pub nodes: Vec<RsxNode>,
|
||||
}
|
||||
|
||||
@@ -20,30 +26,49 @@ impl Parse for RsxRoot {
|
||||
}
|
||||
}
|
||||
|
||||
/// A single prop: `ident: expr`.
|
||||
/// A single component prop: `name: value`
|
||||
pub struct RsxProp {
|
||||
/// Property name
|
||||
pub name: Ident,
|
||||
/// Property value expression
|
||||
pub value: Expr,
|
||||
}
|
||||
|
||||
/// AST node representing different RSX constructs
|
||||
pub enum RsxNode {
|
||||
/// String literal: `"text"`
|
||||
Text(LitStr),
|
||||
/// Expression node: `{expr}`
|
||||
Expr(Expr),
|
||||
/// Component instantiation: `Component { prop: value, ... }`
|
||||
Component {
|
||||
/// Component path (e.g., `my_module::MyComponent`)
|
||||
path: Path,
|
||||
/// Component properties
|
||||
props: Vec<RsxProp>,
|
||||
/// Child nodes
|
||||
children: Vec<RsxNode>,
|
||||
},
|
||||
/// Mount lifecycle: `@mount`
|
||||
Mount(Ident),
|
||||
/// Conditional rendering: `@if condition { ... }`
|
||||
If {
|
||||
/// Dependencies to track for reactivity
|
||||
deps: Vec<Dep>,
|
||||
/// Condition expression
|
||||
cond: Expr,
|
||||
/// Child nodes to render if true
|
||||
children: Vec<RsxNode>,
|
||||
},
|
||||
/// Loop rendering: `@for pattern in expr { ... }`
|
||||
For {
|
||||
/// Dependencies to track for reactivity
|
||||
deps: Vec<Dep>,
|
||||
/// Loop pattern (e.g., `(key, value)`)
|
||||
pat: Pat,
|
||||
/// Iterable expression
|
||||
expr: Expr,
|
||||
/// Child nodes to render for each iteration
|
||||
children: Vec<RsxNode>,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user