From 80f322cd866708191ccfc577488ed051a3180670 Mon Sep 17 00:00:00 2001 From: Jonathan Giddy Date: Mon, 22 Dec 2025 07:26:35 +0000 Subject: [PATCH] Move macro expression functions to `expr` module --- src/context.rs | 227 -------------------------------------- src/context/expr.rs | 227 ++++++++++++++++++++++++++++++++++++++ tests/expand/typle-for.rs | 2 +- 3 files changed, 228 insertions(+), 228 deletions(-) diff --git a/src/context.rs b/src/context.rs index 6afda7e..9937754 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1106,233 +1106,6 @@ impl TypleContext { Ok(()) } - fn replace_typle_fold_expr( - &self, - mac: &mut Macro, - attrs: Vec, - state: &mut BlockState, - default_span: Span, - ) -> syn::Result { - let mut inner_state = BlockState::default(); - let token_stream = std::mem::take(&mut mac.tokens); - let mut tokens = token_stream.into_iter(); - let mut init_expr = - syn::parse2::(Self::extract_to_semicolon(&mut tokens, default_span)?)?; - self.replace_expr(&mut init_expr, &mut inner_state)?; - let (pattern, range) = self.parse_pattern_range(&mut tokens)?; - if range.is_empty() { - return Ok(Expr::Paren(syn::ExprParen { - attrs, - paren_token: token::Paren::default(), - expr: Box::new(init_expr), - })); - } - let fold_ident = Self::parse_fold_ident(&mut tokens, default_span)?; - let fold_pat = Pat::Ident(syn::PatIdent { - attrs: Vec::new(), - by_ref: None, - mutability: None, - ident: fold_ident.clone(), - subpat: None, - }); - let expr = syn::parse2::(tokens.collect())?; - let mut stmts = Vec::with_capacity(range.len() + 2); - stmts.push(Stmt::Local(syn::Local { - attrs: Vec::new(), - let_token: token::Let::default(), - pat: fold_pat.clone(), - init: Some(syn::LocalInit { - eq_token: token::Eq::default(), - expr: Box::new(init_expr), - diverge: None, - }), - semi_token: token::Semi::default(), - })); - if !range.is_empty() { - let mut ctx = pattern.as_ref().map(|ident| { - let mut context = self.clone(); - context.constants.insert(ident.clone(), 0); - (context, ident) - }); - for (index, mut expr) in range.zip_clone(expr) { - if let Some((ref mut context, ident)) = ctx { - *context.constants.get_mut(ident).unwrap() = index; - } - let context = ctx.as_ref().map_or(self, |c| &c.0); - context.replace_expr(&mut expr, &mut inner_state)?; - stmts.push(Stmt::Local(syn::Local { - attrs: Vec::new(), - let_token: token::Let::default(), - pat: fold_pat.clone(), - init: Some(syn::LocalInit { - eq_token: token::Eq::default(), - expr: Box::new(expr), - diverge: None, - }), - semi_token: token::Semi::default(), - })); - } - } - if let Some(span) = inner_state.unlabelled_continue { - abort!( - span, - "unlabelled `continue` not supported in `typle_fold!` macro" - ); - } - state.propagate(inner_state, None); - stmts.push(Stmt::Expr( - Expr::Break(syn::ExprBreak { - attrs: Vec::new(), - break_token: token::Break::default(), - label: None, - expr: Some(Box::new(Expr::Path(syn::PatPath { - attrs: Vec::new(), - qself: None, - path: ident_to_path(fold_ident), - }))), - }), - Some(token::Semi::default()), - )); - // The fold may be used in an expression `typle_fold!() + ...`, in which - // case the loop needs to be in parentheses. - Ok(Expr::Paren(syn::ExprParen { - attrs, - paren_token: token::Paren::default(), - expr: Box::new(Expr::Loop(syn::ExprLoop { - attrs: Vec::new(), - label: None, - loop_token: token::Loop::default(), - body: Block { - brace_token: token::Brace::default(), - stmts, - }, - })), - })) - } - - fn replace_macro_expr( - &self, - mac: &mut Macro, - attrs: &mut Vec, - state: &mut BlockState, - ) -> syn::Result> { - self.replace_attrs(attrs)?; - if let Some(macro_name) = mac.path.get_ident() { - let default_span = macro_name.span(); - if macro_name == "typle" { - // This is outside a comma-separated sequence so only no-range form is accepted - // typle!(=> if T::LEN == 0 {} else {}) - let token_stream = std::mem::take(&mut mac.tokens); - return self.expand_typle_macro_singleton(token_stream, |context, token_stream| { - let mut expr = syn::parse2::(token_stream)?; - let mut state = BlockState::default(); - context.replace_expr(&mut expr, &mut state)?; - Ok(Some(expr)) - }); - } else if macro_name == "typle_fold" { - let expr = - self.replace_typle_fold_expr(mac, std::mem::take(attrs), state, default_span)?; - return Ok(Some(expr)); - } else if macro_name == "typle_all" { - let token_stream = std::mem::take(&mut mac.tokens); - let expr = self.anyall( - token_stream, - state, - BinOp::And(token::AndAnd::default()), - true, - )?; - return Ok(Some(expr)); - } else if macro_name == "typle_any" { - let token_stream = std::mem::take(&mut mac.tokens); - let expr = self.anyall( - token_stream, - state, - BinOp::Or(token::OrOr::default()), - false, - )?; - return Ok(Some(expr)); - } - } - mac.tokens = self.replace_macro_token_stream(std::mem::take(&mut mac.tokens))?; - Ok(None) - } - - fn anyall( - &self, - token_stream: TokenStream, - state: &mut BlockState, - op: BinOp, - default: bool, - ) -> syn::Result { - let mut tokens = token_stream.into_iter(); - let (pattern, mut range) = self.parse_pattern_range(&mut tokens)?; - let expr = syn::parse2::(tokens.collect())?; - let all = match range.next() { - Some(index) => { - // Parenthesize low precedence expressions. - let expr = match expr { - expr @ Expr::Lit(syn::PatLit { - lit: Lit::Bool(_), .. - }) => expr, - expr @ Expr::Block(_) => expr, - expr @ Expr::Paren(_) => expr, - expr @ Expr::Path(_) => expr, - expr @ Expr::MethodCall(_) => expr, - expr @ Expr::Field(_) => expr, - expr @ Expr::Call(_) => expr, - expr @ Expr::Index(_) => expr, - expr @ Expr::Unary(_) => expr, - expr @ Expr::Cast(_) => expr, - expr => Expr::Paren(syn::ExprParen { - attrs: Vec::new(), - paren_token: token::Paren::default(), - expr: Box::new(expr), - }), - }; - - let mut context = self.clone(); - if let Some(ident) = &pattern { - context.constants.insert(ident.clone(), index); - } - let mut all = expr.clone(); - context.replace_expr(&mut all, state)?; - for (index, mut expr) in range.zip_clone(expr) { - if let Some(ident) = &pattern { - *context.constants.get_mut(ident).unwrap() = index; - } - context.replace_expr(&mut expr, state)?; - all = Expr::Binary(syn::ExprBinary { - attrs: Vec::new(), - left: Box::new(all), - op, - right: Box::new(expr), - }); - } - if let Expr::Binary(expr) = all { - // Wrap entire expression in parentheses to: - // 1. ensure that it is a single expression in a larger expression. - // 2. handle ambiguity: `{ true } || { true }` is not a boolean expression. - // It is a block followed by a closure that returns a boolean. Adding - // parentheses `({ true } || { true })` makes it a boolean expression. - all = Expr::Paren(syn::ExprParen { - attrs: Vec::new(), - paren_token: token::Paren::default(), - expr: Box::new(Expr::Binary(expr)), - }); - } - all - } - None => Expr::Lit(syn::PatLit { - attrs: Vec::new(), - lit: Lit::Bool(syn::LitBool { - value: default, - span: expr.span(), - }), - }), - }; - Ok(all) - } - // Look for `typle_ty!(...)` or `typle_expr!(...)` and evaluate body. fn replace_macro_token_stream(&self, input: TokenStream) -> syn::Result { enum TTState { diff --git a/src/context/expr.rs b/src/context/expr.rs index ed2adbb..095ca3b 100644 --- a/src/context/expr.rs +++ b/src/context/expr.rs @@ -682,4 +682,231 @@ impl TypleContext { } Ok(None) } + + fn replace_typle_fold_expr( + &self, + mac: &mut Macro, + attrs: Vec, + state: &mut BlockState, + default_span: Span, + ) -> syn::Result { + let mut inner_state = BlockState::default(); + let token_stream = std::mem::take(&mut mac.tokens); + let mut tokens = token_stream.into_iter(); + let mut init_expr = + syn::parse2::(Self::extract_to_semicolon(&mut tokens, default_span)?)?; + self.replace_expr(&mut init_expr, &mut inner_state)?; + let (pattern, range) = self.parse_pattern_range(&mut tokens)?; + if range.is_empty() { + return Ok(Expr::Paren(syn::ExprParen { + attrs, + paren_token: token::Paren::default(), + expr: Box::new(init_expr), + })); + } + let fold_ident = Self::parse_fold_ident(&mut tokens, default_span)?; + let fold_pat = Pat::Ident(syn::PatIdent { + attrs: Vec::new(), + by_ref: None, + mutability: None, + ident: fold_ident.clone(), + subpat: None, + }); + let expr = syn::parse2::(tokens.collect())?; + let mut stmts = Vec::with_capacity(range.len() + 2); + stmts.push(Stmt::Local(syn::Local { + attrs: Vec::new(), + let_token: token::Let::default(), + pat: fold_pat.clone(), + init: Some(syn::LocalInit { + eq_token: token::Eq::default(), + expr: Box::new(init_expr), + diverge: None, + }), + semi_token: token::Semi::default(), + })); + if !range.is_empty() { + let mut ctx = pattern.as_ref().map(|ident| { + let mut context = self.clone(); + context.constants.insert(ident.clone(), 0); + (context, ident) + }); + for (index, mut expr) in range.zip_clone(expr) { + if let Some((ref mut context, ident)) = ctx { + *context.constants.get_mut(ident).unwrap() = index; + } + let context = ctx.as_ref().map_or(self, |c| &c.0); + context.replace_expr(&mut expr, &mut inner_state)?; + stmts.push(Stmt::Local(syn::Local { + attrs: Vec::new(), + let_token: token::Let::default(), + pat: fold_pat.clone(), + init: Some(syn::LocalInit { + eq_token: token::Eq::default(), + expr: Box::new(expr), + diverge: None, + }), + semi_token: token::Semi::default(), + })); + } + } + if let Some(span) = inner_state.unlabelled_continue { + abort!( + span, + "unlabelled `continue` not supported in `typle_fold!` macro" + ); + } + state.propagate(inner_state, None); + stmts.push(Stmt::Expr( + Expr::Break(syn::ExprBreak { + attrs: Vec::new(), + break_token: token::Break::default(), + label: None, + expr: Some(Box::new(Expr::Path(syn::PatPath { + attrs: Vec::new(), + qself: None, + path: ident_to_path(fold_ident), + }))), + }), + Some(token::Semi::default()), + )); + // The fold may be used in an expression `typle_fold!() + ...`, in which + // case the loop needs to be in parentheses. + Ok(Expr::Paren(syn::ExprParen { + attrs, + paren_token: token::Paren::default(), + expr: Box::new(Expr::Loop(syn::ExprLoop { + attrs: Vec::new(), + label: None, + loop_token: token::Loop::default(), + body: Block { + brace_token: token::Brace::default(), + stmts, + }, + })), + })) + } + + pub(super) fn replace_macro_expr( + &self, + mac: &mut Macro, + attrs: &mut Vec, + state: &mut BlockState, + ) -> syn::Result> { + self.replace_attrs(attrs)?; + if let Some(macro_name) = mac.path.get_ident() { + let default_span = macro_name.span(); + if macro_name == "typle" { + // This is outside a comma-separated sequence so only no-range form is accepted + // typle!(=> if T::LEN == 0 {} else {}) + let token_stream = std::mem::take(&mut mac.tokens); + return self.expand_typle_macro_singleton(token_stream, |context, token_stream| { + let mut expr = syn::parse2::(token_stream)?; + let mut state = BlockState::default(); + context.replace_expr(&mut expr, &mut state)?; + Ok(Some(expr)) + }); + } else if macro_name == "typle_fold" { + let expr = + self.replace_typle_fold_expr(mac, std::mem::take(attrs), state, default_span)?; + return Ok(Some(expr)); + } else if macro_name == "typle_all" { + let token_stream = std::mem::take(&mut mac.tokens); + let expr = self.anyall( + token_stream, + state, + BinOp::And(token::AndAnd::default()), + true, + )?; + return Ok(Some(expr)); + } else if macro_name == "typle_any" { + let token_stream = std::mem::take(&mut mac.tokens); + let expr = self.anyall( + token_stream, + state, + BinOp::Or(token::OrOr::default()), + false, + )?; + return Ok(Some(expr)); + } + } + mac.tokens = self.replace_macro_token_stream(std::mem::take(&mut mac.tokens))?; + Ok(None) + } + + fn anyall( + &self, + token_stream: TokenStream, + state: &mut BlockState, + op: BinOp, + default: bool, + ) -> syn::Result { + let mut tokens = token_stream.into_iter(); + let (pattern, mut range) = self.parse_pattern_range(&mut tokens)?; + let expr = syn::parse2::(tokens.collect())?; + let all = match range.next() { + Some(index) => { + // Parenthesize low precedence expressions. + let expr = match expr { + expr @ Expr::Lit(syn::PatLit { + lit: Lit::Bool(_), .. + }) => expr, + expr @ Expr::Block(_) => expr, + expr @ Expr::Paren(_) => expr, + expr @ Expr::Path(_) => expr, + expr @ Expr::MethodCall(_) => expr, + expr @ Expr::Field(_) => expr, + expr @ Expr::Call(_) => expr, + expr @ Expr::Index(_) => expr, + expr @ Expr::Unary(_) => expr, + expr @ Expr::Cast(_) => expr, + expr => Expr::Paren(syn::ExprParen { + attrs: Vec::new(), + paren_token: token::Paren::default(), + expr: Box::new(expr), + }), + }; + + let mut context = self.clone(); + if let Some(ident) = &pattern { + context.constants.insert(ident.clone(), index); + } + let mut all = expr.clone(); + context.replace_expr(&mut all, state)?; + for (index, mut expr) in range.zip_clone(expr) { + if let Some(ident) = &pattern { + *context.constants.get_mut(ident).unwrap() = index; + } + context.replace_expr(&mut expr, state)?; + all = Expr::Binary(syn::ExprBinary { + attrs: Vec::new(), + left: Box::new(all), + op, + right: Box::new(expr), + }); + } + if let Expr::Binary(expr) = all { + // Wrap entire expression in parentheses to: + // 1. ensure that it is a single expression in a larger expression. + // 2. handle ambiguity: `{ true } || { true }` is not a boolean expression. + // It is a block followed by a closure that returns a boolean. Adding + // parentheses `({ true } || { true })` makes it a boolean expression. + all = Expr::Paren(syn::ExprParen { + attrs: Vec::new(), + paren_token: token::Paren::default(), + expr: Box::new(Expr::Binary(expr)), + }); + } + all + } + None => Expr::Lit(syn::PatLit { + attrs: Vec::new(), + lit: Lit::Bool(syn::LitBool { + value: default, + span: expr.span(), + }), + }), + }; + Ok(all) + } } diff --git a/tests/expand/typle-for.rs b/tests/expand/typle-for.rs index 95cd7d6..ab5d8b6 100644 --- a/tests/expand/typle-for.rs +++ b/tests/expand/typle-for.rs @@ -18,7 +18,7 @@ where // Arbitrary expressions can be used for the indices and // the iterator variable can be left out if not needed let init: [Option; T::LEN] = [typle!(T::LEN * 2..T::LEN * 3 => None)]; - // const-if can be used to filter components in `typle_for!`. + // const-if can be used to filter components in `typle!`. let c = (typle!(i in .. => if i == 0 {b[[i]]})); } }