Skip to content

Commit

Permalink
fix(macros): 🐛 expr object not fold
Browse files Browse the repository at this point in the history
  • Loading branch information
M-Adoo committed Aug 15, 2023
1 parent 6f6df27 commit bdb877b
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 48 deletions.
6 changes: 2 additions & 4 deletions macros/src/pipe_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,12 @@ pub(crate) struct PipeExpr {

impl Parse for PipeExpr {
fn parse(input: ParseStream) -> syn::Result<Self> {
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<Stmt>)> {
pub fn fold_expr_as_in_closure(input: ParseStream) -> syn::Result<(DollarRefs, Vec<Stmt>)> {
let mut refs = DollarRefs::default();
refs.in_capture += 1;
let stmts = syn::Block::parse_within(input)?;
Expand Down
48 changes: 29 additions & 19 deletions macros/src/rdl_macro.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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<Stmt>,
},
}

/// Declare a object use struct literal, like `rdl! { Row { ... } }` or
Expand Down Expand Up @@ -58,18 +61,11 @@ impl Parse for RdlBody {
if fork.parse::<RdlParent>().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 })
}
}
}
Expand Down Expand Up @@ -115,7 +111,13 @@ impl Parse for RdlParent {
fn parse(input: ParseStream) -> SynResult<Self> {
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::<Dollar>()?;
input.parse()
},
)?))
} else {
Ok(RdlParent::Type(input.parse()?))
}
Expand Down Expand Up @@ -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));
}
}
}
}
}
Expand Down
71 changes: 48 additions & 23 deletions macros/src/symbol_process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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_ಠ_ಠ";
Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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!()) }),
Expand All @@ -296,14 +305,30 @@ impl DollarRefs {
}
}

fn dollar_macro_inner_ident(mac: &Macro) -> Option<Ident> {
mac.path.is_ident(KW_DOLLAR_STR).then(|| {
let tokens = &mac.tokens;
parse_quote!(#tokens)
})
fn parse_clean_dollar_macro(mac: &Macro) -> Option<DollarMacro> {
if mac.path.is_ident(KW_DOLLAR_STR) {
// parse fail may occur because the macro is already fold by `DollarRefs`.
mac.parse_body::<DollarMacro>().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<Self> {
Ok(Self {
_dollar: input.parse()?,
name: input.parse()?,
})
}
}
4 changes: 2 additions & 2 deletions macros/src/watch_macro.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand All @@ -12,7 +12,7 @@ pub(crate) struct WatchMacro {

impl Parse for WatchMacro {
fn parse(input: ParseStream) -> syn::Result<Self> {
let (refs, stmts) = dollar_expr_will_in_closure_parse(input)?;
let (refs, stmts) = fold_expr_as_in_closure(input)?;
Ok(Self { refs, expr: stmts })
}
}
Expand Down

0 comments on commit bdb877b

Please sign in to comment.