diff --git a/macros/src/pipe_macro.rs b/macros/src/pipe_macro.rs index 5904c811e..b4fba7708 100644 --- a/macros/src/pipe_macro.rs +++ b/macros/src/pipe_macro.rs @@ -13,14 +13,12 @@ pub(crate) struct PipeExpr { impl Parse for PipeExpr { fn parse(input: ParseStream) -> syn::Result { - let (refs, stmts) = dollar_expr_will_in_closure_parse(input)?; + let (refs, stmts) = fold_expr_as_in_closure(input)?; Ok(Self { refs, expr: stmts }) } } -pub fn dollar_expr_will_in_closure_parse( - input: ParseStream, -) -> syn::Result<(DollarRefs, Vec)> { +pub fn fold_expr_as_in_closure(input: ParseStream) -> syn::Result<(DollarRefs, Vec)> { let mut refs = DollarRefs::default(); refs.in_capture += 1; let stmts = syn::Block::parse_within(input)?; diff --git a/macros/src/rdl_macro.rs b/macros/src/rdl_macro.rs index dfc37fd94..9a7140796 100644 --- a/macros/src/rdl_macro.rs +++ b/macros/src/rdl_macro.rs @@ -1,15 +1,15 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::{quote_spanned, ToTokens}; use std::collections::HashSet; use syn::{ braced, fold::Fold, - parse::{Parse, ParseStream}, + parse::{Parse, ParseBuffer, ParseStream}, parse_quote, punctuated::Punctuated, spanned::Spanned, - token::{At, Bang, Brace, Colon, Comma}, - Expr, Ident, Macro, Path, Result as SynResult, + token::{At, Bang, Brace, Colon, Comma, Dollar}, + Expr, Ident, Macro, Path, Result as SynResult, Stmt, }; use crate::{ @@ -20,7 +20,10 @@ use crate::{ pub enum RdlBody { Literal(StructLiteral), /// Declare an expression as a object, like `rdl! { Widget::new(...) }` - ExprObj(TokenStream), + ExprObj { + span: Span, + stmts: Vec, + }, } /// Declare a object use struct literal, like `rdl! { Row { ... } }` or @@ -58,18 +61,11 @@ impl Parse for RdlBody { if fork.parse::().is_ok() && fork.peek(Brace) { Ok(RdlBody::Literal(input.parse()?)) } else { - let tokens = input.step(move |c| { - let mut cursor = *c; - - let mut tts = vec![]; - while let Some((t, c)) = cursor.token_tree() { - tts.push(t); - cursor = c; - } - - Ok((tts.into_iter().collect(), cursor)) - })?; - Ok(RdlBody::ExprObj(tokens)) + let span = input.span(); + let stmts = syn::Block::parse_within(input)?; + let mut refs = DollarRefs::default(); + let stmts = stmts.into_iter().map(|s| refs.fold_stmt(s)).collect(); + Ok(RdlBody::ExprObj { span, stmts }) } } } @@ -115,7 +111,13 @@ impl Parse for RdlParent { fn parse(input: ParseStream) -> SynResult { if input.peek(kw::_dollar_ಠ_ಠ) && input.peek2(Bang) { let mac: Macro = input.parse()?; - Ok(RdlParent::Var(mac.parse_body()?)) + + Ok(RdlParent::Var(mac.parse_body_with( + |input: &ParseBuffer| { + input.parse::()?; + input.parse() + }, + )?)) } else { Ok(RdlParent::Type(input.parse()?)) } @@ -144,7 +146,15 @@ impl ToTokens for RdlBody { Ok(declare) => declare.to_tokens(tokens), Err(err) => err.to_tokens(tokens), }, - RdlBody::ExprObj(e) => Brace(e.span()).surround(tokens, |tokens| e.to_tokens(tokens)), + RdlBody::ExprObj { span, stmts } => { + if stmts.len() > 1 { + Brace(*span).surround(tokens, |tokens| { + stmts.iter().for_each(|s| s.to_tokens(tokens)); + }) + } else { + stmts.iter().for_each(|s| s.to_tokens(tokens)); + } + } } } } diff --git a/macros/src/symbol_process.rs b/macros/src/symbol_process.rs index d0eee3995..51519bb9e 100644 --- a/macros/src/symbol_process.rs +++ b/macros/src/symbol_process.rs @@ -6,8 +6,12 @@ use proc_macro2::{Ident, TokenStream}; use quote::{quote, quote_spanned, ToTokens}; use smallvec::SmallVec; use syn::{ - fold::Fold, parse_quote, parse_quote_spanned, spanned::Spanned, Expr, ExprField, ExprMacro, - ExprMethodCall, Macro, Member, + fold::Fold, + parse::{Parse, ParseStream}, + parse_quote, + spanned::Spanned, + token::Dollar, + Expr, ExprField, ExprMethodCall, Macro, Member, }; pub const KW_DOLLAR_STR: &str = "_dollar_ಠ_ಠ"; @@ -116,7 +120,10 @@ mod tokens_pre_process { tokens.push(TokenTree::Ident(Ident::new(KW_DOLLAR_STR, p.span()))); tokens.push(TokenTree::Punct(Punct::new('!', Spacing::Alone))); let span = name.span(); - let mut g = Group::new(Delimiter::Parenthesis, TokenTree::Ident(name).into()); + let mut g = Group::new( + Delimiter::Parenthesis, + [TokenTree::Punct(p), TokenTree::Ident(name)].into_iter().collect() + ); g.set_span(span); tokens.push(TokenTree::Group(g)); } @@ -169,18 +176,20 @@ impl Fold for DollarRefs { syn::fold::fold_expr_method_call(self, i) } + + fn fold_macro(&mut self, mut mac: Macro) -> Macro { + if let Some(DollarMacro { name, .. }) = parse_clean_dollar_macro(&mac) { + mac.tokens = name.to_token_stream(); + self.refs.push(DollarRef { name, builtin_shadow: None }); + mac + } else { + self.ctx_used = mac.path.is_ident(KW_RDL) || mac.path.is_ident(KW_CTX); + syn::fold::fold_macro(self, mac) + } + } + fn fold_expr(&mut self, i: Expr) -> Expr { match i { - Expr::Macro(e @ ExprMacro { .. }) => { - if let Some(name) = dollar_macro_inner_ident(&e.mac) { - let new_expr = parse_quote_spanned! { name.span() => #name.state_ref() }; - self.refs.push(DollarRef { name, builtin_shadow: None }); - new_expr - } else { - self.ctx_used = e.mac.path.is_ident(KW_RDL) || e.mac.path.is_ident(KW_CTX); - Expr::Macro(self.fold_expr_macro(e)) - } - } Expr::Closure(c) if c.capture.is_some() => { let mut closure_refs = DollarRefs::default(); closure_refs.in_capture += 1; @@ -276,18 +285,18 @@ impl DollarRefs { }; let Expr::Macro(m) = e else { return None }; - let host = dollar_macro_inner_ident(&m.mac)?; + let DollarMacro { name: host, .. } = parse_clean_dollar_macro(&m.mac)?; let builtin_name = ribir_suffix_variable(&host, builtin_member); let builtin_member = Ident::new(builtin_member, host.span()); // When a builtin widget captured by a `move |_| {...}` closure, we need split // the builtin widget from the `FatObj` so we only capture the used builtin // part. - if self.in_capture > 0 { - *e = parse_quote!(#builtin_name.state_ref()); + m.mac.tokens = if self.in_capture > 0 { + builtin_name.to_token_stream() } else { - *e = parse_quote_spanned! { host.span() => #host.#builtin_member(ctx!()).state_ref() }; - } + quote_spanned! { host.span() => #host.#builtin_member(ctx!()) } + }; self.refs.push(DollarRef { name: builtin_name, builtin_shadow: Some(parse_quote! { #host.#builtin_member(ctx!()) }), @@ -296,14 +305,30 @@ impl DollarRefs { } } -fn dollar_macro_inner_ident(mac: &Macro) -> Option { - mac.path.is_ident(KW_DOLLAR_STR).then(|| { - let tokens = &mac.tokens; - parse_quote!(#tokens) - }) +fn parse_clean_dollar_macro(mac: &Macro) -> Option { + if mac.path.is_ident(KW_DOLLAR_STR) { + // parse fail may occur because the macro is already fold by `DollarRefs`. + mac.parse_body::().ok() + } else { + None + } } impl std::ops::Deref for DollarRefs { type Target = [DollarRef]; fn deref(&self) -> &Self::Target { &self.refs } } + +struct DollarMacro { + _dollar: Dollar, + name: Ident, +} + +impl Parse for DollarMacro { + fn parse(input: ParseStream) -> syn::Result { + Ok(Self { + _dollar: input.parse()?, + name: input.parse()?, + }) + } +} diff --git a/macros/src/watch_macro.rs b/macros/src/watch_macro.rs index 1a22c6b56..2c4e27b9c 100644 --- a/macros/src/watch_macro.rs +++ b/macros/src/watch_macro.rs @@ -1,4 +1,4 @@ -use crate::{pipe_macro::dollar_expr_will_in_closure_parse, symbol_process::DollarRefs}; +use crate::{pipe_macro::fold_expr_as_in_closure, symbol_process::DollarRefs}; use quote::{quote, ToTokens}; use syn::{ parse::{Parse, ParseStream}, @@ -12,7 +12,7 @@ pub(crate) struct WatchMacro { impl Parse for WatchMacro { fn parse(input: ParseStream) -> syn::Result { - let (refs, stmts) = dollar_expr_will_in_closure_parse(input)?; + let (refs, stmts) = fold_expr_as_in_closure(input)?; Ok(Self { refs, expr: stmts }) } }