use crate::utils::{syn_assert, syn_error, wrap_expr_block}; use proc_macro2::TokenStream; use quote::quote; use std::fmt::Display; use syn::{ext::*, punctuated::*, spanned::*, *}; pub fn generate(expr: &Expr) -> Result { let mut f = Formatter::default(); match expr { Expr::Block(block) => generate_block(&mut f, &block.block, Context::stmt(true))?, _ => generate_expr(&mut f, expr, Context::expr(true))?, } Ok(f.done()) } #[derive(Default)] struct Formatter { buf: String, space: bool, format_args: Vec, } impl Formatter { fn write(&mut self, s: impl Display) -> &mut Self { fn sep(c: char) -> bool { match c { '(' | ')' | '[' | ']' | '{' | '}' | '+' | '-' | '*' | '/' | '%' | '^' | '#' | '=' | '~' | '<' | '>' | ':' | ';' | '.' | ',' | '\'' | '"' | ' ' => true, _ => false, } } // we are inside a format string, so we need to escape braces let s = format!("{s}").replace("{", "{{").replace("}", "}}"); if !s.is_empty() { if self.space && !sep(s.chars().next().unwrap()) { self.buf.push(' '); } self.buf.push_str(&s); self.space = !sep(s.chars().last().unwrap()); } self } fn interpolate(&mut self, arg: Expr) -> &mut Self { self.space.then(|| self.buf.push(' ')); self.buf.push_str("{}"); self.format_args.push(arg); self.space = true; self } fn done(self) -> TokenStream { let fmt = self.buf; let args = self.format_args; if args.is_empty() { let fmt = fmt.replace("{{", "{").replace("}}", "}"); quote!(#fmt) } else { quote!(::std::format!(#fmt, #(#args),*)) } } } macro_rules! assert_no_attrs { ($src:expr) => {{ syn_assert!($src.attrs.is_empty(), $src, "unsupported attribute") }}; } macro_rules! assert_no_suffix { ($src:expr) => {{ syn_assert!($src.suffix() == "", $src, "cannot have suffix") }}; } #[derive(Debug, Clone, Copy)] enum Context { Stmt { ret: bool }, Expr { multi: bool }, } impl Context { fn stmt(ret: bool) -> Self { Self::Stmt { ret } } fn expr(multi: bool) -> Self { Self::Expr { multi } } fn is_stmt(&self) -> bool { matches!(self, Self::Stmt { .. }) } fn is_ret(&self) -> bool { matches!(self, Self::Stmt { ret: true, .. }) } fn is_expr(&self) -> bool { matches!(self, Self::Expr { .. }) } fn is_multi_expr(&self) -> bool { matches!(self, Self::Expr { multi: true, .. }) } fn is_value(&self) -> bool { self.is_expr() | self.is_ret() } } fn generate_expr(f: &mut Formatter, expr: &Expr, cx: Context) -> Result<()> { match expr { Expr::Assign(ass) => generate_expr_assign(f, ass, cx), Expr::Binary(bin) => generate_expr_binary(f, bin, cx), Expr::Block(block) => generate_expr_block(f, block, cx), Expr::Break(brk) => generate_expr_break(f, brk, cx), Expr::Call(call) => generate_expr_call(f, call, cx), Expr::Closure(clo) => generate_expr_closure(f, clo, cx), Expr::Continue(cont) => generate_expr_continue(f, cont, cx), Expr::Field(field) => generate_expr_field(f, field, cx), Expr::ForLoop(fo) => generate_expr_forloop(f, fo, cx), Expr::If(xif) => generate_expr_if(f, xif, cx), Expr::Index(index) => generate_expr_index(f, index, cx), Expr::Infer(infer) => generate_expr_infer(f, infer, cx), Expr::Lit(lit) => generate_expr_lit(f, lit, cx), Expr::Loop(lo) => generate_expr_loop(f, lo, cx), Expr::Macro(mac) => generate_expr_macro(f, mac, cx), Expr::MethodCall(call) => generate_expr_method_call(f, call, cx), Expr::Paren(paren) => generate_expr_paren(f, paren, cx), Expr::Path(path) => generate_expr_path(f, path, cx), Expr::Return(ret) => generate_expr_return(f, ret, cx), Expr::Tuple(tuple) => generate_expr_tuple(f, tuple, cx), Expr::Unary(un) => generate_expr_unary(f, un, cx), Expr::While(whil) => generate_expr_while(f, whil, cx), expr => syn_error!(expr, "unsupported expression"), } } fn generate_expr_assign(f: &mut Formatter, ass: &ExprAssign, cx: Context) -> Result<()> { assert_no_attrs!(ass); syn_assert!( cx.is_stmt(), ass, "assignment must be in statement position" ); generate_expr(f, &ass.left, Context::expr(true))?; f.write("="); generate_expr(f, &ass.right, Context::expr(true))?; Ok(()) } fn generate_expr_binary(f: &mut Formatter, bin: &ExprBinary, cx: Context) -> Result<()> { assert_no_attrs!(bin); match bin.op { BinOp::Add(_) | BinOp::Sub(_) | BinOp::Mul(_) | BinOp::Div(_) | BinOp::Rem(_) | BinOp::BitAnd(_) | BinOp::BitOr(_) | BinOp::BitXor(_) | BinOp::Shl(_) | BinOp::Shr(_) | BinOp::Eq(_) | BinOp::Ne(_) | BinOp::Lt(_) | BinOp::Le(_) | BinOp::Ge(_) | BinOp::Gt(_) | BinOp::And(_) | BinOp::Or(_) => { syn_assert!(cx.is_value(), bin, "must be in expression position"); cx.is_ret().then(|| f.write("return")); } BinOp::AddAssign(_) | BinOp::SubAssign(_) | BinOp::MulAssign(_) | BinOp::DivAssign(_) | BinOp::RemAssign(_) | BinOp::BitXorAssign(_) | BinOp::BitAndAssign(_) | BinOp::BitOrAssign(_) | BinOp::ShlAssign(_) | BinOp::ShrAssign(_) => { syn_assert!(cx.is_stmt(), bin, "must be in statement position"); } op => syn_error!(op, "unsupported binary operator"), } let cx = Context::expr(false); macro_rules! bin_op { ($op:expr) => {{ generate_expr(f, &bin.left, cx)?; f.write($op); generate_expr(f, &bin.right, cx)?; }}; } macro_rules! call_op { ($name:expr) => {{ f.write(format_args!("{}(", $name)); generate_expr(f, &bin.left, cx)?; f.write(","); generate_expr(f, &bin.right, cx)?; f.write(")"); }}; } macro_rules! assign_bin_op { ($op:expr) => {{ generate_expr(f, &bin.left, cx)?; f.write("="); bin_op!($op); }}; } macro_rules! assign_call_op { ($name:expr) => {{ generate_expr(f, &bin.left, cx)?; f.write("="); call_op!($name); }}; } match bin.op { BinOp::Add(_) => bin_op!("+"), BinOp::AddAssign(_) => assign_bin_op!("+"), BinOp::Sub(_) => bin_op!("-"), BinOp::SubAssign(_) => assign_bin_op!("-"), BinOp::Mul(_) => bin_op!("*"), BinOp::MulAssign(_) => assign_bin_op!("*"), BinOp::Div(_) => bin_op!("/"), BinOp::DivAssign(_) => assign_bin_op!("/"), BinOp::Rem(_) => call_op!("__fmod"), BinOp::RemAssign(_) => assign_call_op!("__fmod"), BinOp::BitAnd(_) => call_op!("__band"), BinOp::BitAndAssign(_) => assign_call_op!("__band"), BinOp::BitOr(_) => call_op!("__bor"), BinOp::BitOrAssign(_) => assign_call_op!("__bor"), BinOp::BitXor(_) => call_op!("__bxor"), BinOp::BitXorAssign(_) => assign_call_op!("__bxor"), BinOp::Shl(_) => call_op!("__blshift"), BinOp::ShlAssign(_) => assign_call_op!("__blshift"), BinOp::Shr(_) => call_op!("__barshift"), BinOp::ShrAssign(_) => assign_call_op!("__barshift"), BinOp::Eq(_) => bin_op!("=="), BinOp::Ne(_) => bin_op!("~="), BinOp::Lt(_) => bin_op!("<"), BinOp::Le(_) => bin_op!("<="), BinOp::Ge(_) => bin_op!(">="), BinOp::Gt(_) => bin_op!(">"), BinOp::And(_) => bin_op!("and"), BinOp::Or(_) => bin_op!("or"), op => syn_error!(op, "unsupported binary operator"), }; Ok(()) } fn generate_expr_block(f: &mut Formatter, block: &ExprBlock, cx: Context) -> Result<()> { assert_no_attrs!(block); syn_assert!(cx.is_stmt(), block, "block must be in statement position"); f.write("do"); generate_block(f, &block.block, cx)?; if let Some(ref label) = block.label { generate_label_continue(f, label)?; } f.write("end"); if let Some(ref label) = block.label { generate_label_break(f, label)?; } Ok(()) } fn generate_expr_break(f: &mut Formatter, brk: &ExprBreak, cx: Context) -> Result<()> { assert_no_attrs!(brk); syn_assert!(cx.is_stmt(), brk, "break must be in statement position"); syn_assert!(brk.expr.is_none(), brk, "use return instead"); match brk.label { Some(ref label) => f.write(format_args!("goto {}_brk", label.ident.unraw())), None => f.write("break"), }; Ok(()) } fn generate_expr_call(f: &mut Formatter, call: &ExprCall, cx: Context) -> Result<()> { assert_no_attrs!(call); cx.is_ret().then(|| f.write("return")); generate_expr(f, &call.func, Context::expr(false))?; f.write("("); generate_punctuated_expr(f, &call.args)?; f.write(")"); Ok(()) } fn generate_expr_closure(f: &mut Formatter, clo: &ExprClosure, cx: Context) -> Result<()> { assert_no_attrs!(clo); syn_assert!(cx.is_value(), clo, "closure must be in expression position"); if let Some(ref bounds) = clo.lifetimes { syn_error!(bounds, "cannot have lifetime bindings"); } else if let Some(ref stat) = clo.movability { syn_error!(stat, "cannot be static"); } else if let Some(ref cons) = clo.constness { syn_error!(cons, "cannot be const"); } else if let Some(ref asyn) = clo.asyncness { syn_error!(asyn, "cannot be async"); } else if let Some(ref capt) = clo.capture { syn_error!(capt, "cannot be move"); } else if let ReturnType::Type(_, ref ty) = clo.output { syn_error!(ty, "cannot have return type"); } cx.is_ret().then(|| f.write("return")); f.write("function("); generate_punctuated_pat(f, &clo.inputs)?; f.write(")"); generate_block(f, &wrap_expr_block(&clo.body), Context::stmt(true))?; f.write("end"); Ok(()) } fn generate_expr_continue(f: &mut Formatter, cont: &ExprContinue, cx: Context) -> Result<()> { assert_no_attrs!(cont); syn_assert!(cx.is_stmt(), cont, "continue must be in statement position"); match cont.label { Some(ref label) => f.write(format_args!("goto {}_cnt", label.ident.unraw())), None => syn_error!(cont, "continue requires a label"), }; Ok(()) } fn generate_expr_field(f: &mut Formatter, field: &ExprField, cx: Context) -> Result<()> { assert_no_attrs!(field); syn_assert!(cx.is_value(), field, "must be in expression position"); cx.is_ret().then(|| f.write("return")); generate_expr(f, &field.base, Context::expr(false))?; match field.member { Member::Named(ref ident) => { f.write("."); generate_ident(f, ident)?; } Member::Unnamed(ref index) => { f.write("["); generate_index(f, index)?; f.write("]"); } } Ok(()) } fn generate_expr_forloop(f: &mut Formatter, fo: &ExprForLoop, cx: Context) -> Result<()> { assert_no_attrs!(fo); syn_assert!(cx.is_stmt(), fo, "for loop must be in statement position"); f.write("for"); match *fo.expr { Expr::Range(ref range) => { assert_no_attrs!(range); // TODO: reverse ranges let start = match range.start { Some(ref start) => (**start).clone(), None => parse_quote!(0), }; let end = match range.end { Some(ref end) => match range.limits { RangeLimits::HalfOpen(_) => parse_quote!(#end - 1), RangeLimits::Closed(_) => (**end).clone(), }, None => syn_error!(range, "end of range must be specified"), }; generate_pat(f, &fo.pat, PatContext::Single)?; f.write("="); generate_expr(f, &start, Context::expr(false))?; f.write(","); generate_expr(f, &end, Context::expr(false))?; } ref expr => { generate_pat(f, &fo.pat, PatContext::Multi)?; f.write("in"); generate_expr(f, expr, Context::expr(true))?; } } f.write("do"); generate_block(f, &fo.body, Context::stmt(false))?; if let Some(ref label) = fo.label { generate_label_continue(f, label)?; } f.write("end"); if let Some(ref label) = fo.label { generate_label_break(f, label)?; } Ok(()) } fn generate_expr_if(f: &mut Formatter, mut xif: &ExprIf, cx: Context) -> Result<()> { assert_no_attrs!(xif); syn_assert!(cx.is_stmt(), xif, "if must be in statement position"); f.write("if"); loop { generate_expr(f, &xif.cond, Context::expr(false))?; f.write("then"); generate_block(f, &xif.then_branch, cx)?; if let Some((_, ref expr)) = xif.else_branch { match **expr { Expr::If(ref elseif) => { f.write("elseif"); xif = elseif; continue; } ref els => { f.write("else"); generate_block(f, &wrap_expr_block(els), cx)?; } } } break; } f.write("end"); Ok(()) } fn generate_expr_index(f: &mut Formatter, index: &ExprIndex, cx: Context) -> Result<()> { assert_no_attrs!(index); syn_assert!(cx.is_value(), index, "must be in expression position"); cx.is_ret().then(|| f.write("return")); generate_expr(f, &index.expr, Context::expr(false))?; f.write("["); generate_expr(f, &index.index, Context::expr(false))?; f.write("]"); Ok(()) } fn generate_expr_infer(f: &mut Formatter, infer: &ExprInfer, cx: Context) -> Result<()> { assert_no_attrs!(infer); syn_assert!(cx.is_value(), infer, "must be in expression position"); cx.is_ret().then(|| f.write("return")); f.write("_"); Ok(()) } fn generate_expr_lit(f: &mut Formatter, lit: &ExprLit, cx: Context) -> Result<()> { assert_no_attrs!(lit); syn_assert!(cx.is_value(), lit, "literal must be in expression position"); cx.is_ret().then(|| f.write("return")); generate_lit(f, &lit.lit) } fn generate_expr_loop(f: &mut Formatter, lo: &ExprLoop, cx: Context) -> Result<()> { assert_no_attrs!(lo); syn_assert!(cx.is_stmt(), lo, "loop must be in statement position"); f.write("while true do"); generate_block(f, &lo.body, Context::stmt(false))?; if let Some(ref label) = lo.label { generate_label_continue(f, label)?; } f.write("end"); if let Some(ref label) = lo.label { generate_label_break(f, label)?; } Ok(()) } fn generate_expr_macro(f: &mut Formatter, mac: &ExprMacro, cx: Context) -> Result<()> { assert_no_attrs!(mac); generate_macro(f, &mac.mac, cx) } fn generate_expr_method_call(f: &mut Formatter, call: &ExprMethodCall, cx: Context) -> Result<()> { assert_no_attrs!(call); if let Some(ref fish) = call.turbofish { syn_error!(fish, "cannot be generic"); } cx.is_ret().then(|| f.write("return")); generate_expr(f, &call.receiver, Context::expr(false))?; f.write(":"); generate_ident(f, &call.method)?; f.write("("); generate_punctuated_expr(f, &call.args)?; f.write(")"); Ok(()) } fn generate_expr_paren(f: &mut Formatter, paren: &ExprParen, cx: Context) -> Result<()> { assert_no_attrs!(paren); syn_assert!(cx.is_value(), paren, "must be in expression position"); cx.is_ret().then(|| f.write("return")); f.write("("); generate_expr(f, &paren.expr, Context::expr(false))?; f.write(")"); Ok(()) } fn generate_expr_path(f: &mut Formatter, path: &ExprPath, cx: Context) -> Result<()> { assert_no_attrs!(path); syn_assert!(cx.is_value(), path, "must be in expression position"); cx.is_ret().then(|| f.write("return")); match path.qself { Some(ref qself) => syn_error!(qself, "cannot be generic"), None => generate_path(f, &path.path), } } fn generate_expr_return(f: &mut Formatter, ret: &ExprReturn, cx: Context) -> Result<()> { assert_no_attrs!(ret); syn_assert!(cx.is_stmt(), ret, "return must be in statement position"); f.write("return"); match ret.expr { Some(ref value) => generate_expr(f, value, Context::expr(true)), None => Ok(()), } } fn generate_expr_tuple(f: &mut Formatter, tuple: &ExprTuple, cx: Context) -> Result<()> { assert_no_attrs!(tuple); syn_assert!(cx.is_value(), tuple, "tuple must be in expression position"); cx.is_ret().then(|| f.write("return")); match tuple.elems.len() { 0 => { f.write("nil"); Ok(()) } _ if cx.is_ret() || cx.is_multi_expr() => generate_punctuated_expr(f, &tuple.elems), _ => syn_error!(tuple, "expected single-valued expression"), } } fn generate_expr_unary(f: &mut Formatter, un: &ExprUnary, cx: Context) -> Result<()> { assert_no_attrs!(un); syn_assert!(cx.is_value(), un, "must be in expression position"); cx.is_ret().then(|| f.write("return")); let cx = Context::expr(false); macro_rules! un_op { ($op:expr) => {{ f.write($op); generate_expr(f, &un.expr, cx)?; }}; } match un.op { UnOp::Not(_) => un_op!("not"), UnOp::Neg(_) => un_op!("-"), op => syn_error!(op, "unsupported unary operator"), } Ok(()) } fn generate_expr_while(f: &mut Formatter, whil: &ExprWhile, cx: Context) -> Result<()> { assert_no_attrs!(whil); syn_assert!(cx.is_stmt(), whil, "while must be in statement position"); f.write("while"); generate_expr(f, &whil.cond, Context::expr(false))?; f.write("do"); generate_block(f, &whil.body, Context::stmt(false))?; if let Some(ref label) = whil.label { generate_label_continue(f, label)?; } f.write("end"); if let Some(ref label) = whil.label { generate_label_break(f, label)?; } Ok(()) } fn generate_punctuated_expr(f: &mut Formatter, exprs: &Punctuated) -> Result<()> { let len = exprs.len(); for (i, expr) in exprs.iter().enumerate() { (i != 0).then(|| f.write(",")); generate_expr(f, expr, Context::expr(i == len - 1))?; } Ok(()) } fn generate_ident(f: &mut Formatter, ident: &Ident) -> Result<()> { // https://www.lua.org/manual/5.2/manual.html#3.1 let name = format!("{}", ident.unraw()); match name.as_str() { "and" | "break" | "do" | "else" | "elseif" | "end" | "false" | "for" | "function" | "goto" | "if" | "in" | "local" | "nil" | "not" | "or" | "repeat" | "return" | "then" | "true" | "until" | "while" => { syn_error!(ident, "'{name}' cannot be used as an identifier") } s => f.write(s), }; Ok(()) } fn generate_index(f: &mut Formatter, index: &Index) -> Result<()> { f.write(index.index); Ok(()) } fn generate_label_continue(f: &mut Formatter, label: &Label) -> Result<()> { f.write(format_args!("::{}_cnt::", label.name.ident.unraw())); Ok(()) } fn generate_label_break(f: &mut Formatter, label: &Label) -> Result<()> { f.write(format_args!("::{}_brk::", label.name.ident.unraw())); Ok(()) } fn generate_lit(f: &mut Formatter, lit: &Lit) -> Result<()> { match lit { Lit::Bool(b) => generate_lit_bool(f, b), Lit::Byte(b) => generate_lit_byte(f, b), Lit::Int(n) => generate_lit_int(f, n), Lit::Float(n) => generate_lit_float(f, n), Lit::Str(s) => generate_lit_str(f, s), Lit::ByteStr(s) => generate_lit_byte_str(f, s), Lit::CStr(s) => generate_lit_cstr(f, s), lit => syn_error!(lit, "unsupported literal"), } } fn generate_lit_bool(f: &mut Formatter, b: &LitBool) -> Result<()> { f.write(b.value()); Ok(()) } fn generate_lit_byte(f: &mut Formatter, b: &LitByte) -> Result<()> { assert_no_suffix!(b); f.write(b.value()); Ok(()) } fn generate_lit_int(f: &mut Formatter, n: &LitInt) -> Result<()> { assert_no_suffix!(n); f.write(n.base10_parse::()?); Ok(()) } fn generate_lit_float(f: &mut Formatter, n: &LitFloat) -> Result<()> { assert_no_suffix!(n); f.write(n.base10_parse::()?); Ok(()) } fn generate_lit_str(f: &mut Formatter, s: &LitStr) -> Result<()> { assert_no_suffix!(s); f.write(format!(r#""{}""#, escape_str(s.value()))); Ok(()) } fn generate_lit_byte_str(f: &mut Formatter, s: &LitByteStr) -> Result<()> { assert_no_suffix!(s); f.write(format!(r#""{}""#, escape_str(s.value()))); Ok(()) } fn generate_lit_cstr(f: &mut Formatter, s: &LitCStr) -> Result<()> { assert_no_suffix!(s); f.write(format!(r#""{}\0""#, escape_str(s.value().as_bytes()))); Ok(()) } fn escape_str(s: impl AsRef<[u8]>) -> String { // this produces an escaped string with \xNN hexadecimal notation which lua 5.1 normally // wouldn't understand, but luajit supports this unconditionally as an extension // https://docs.rs/bstr/latest/bstr/trait.ByteSlice.html#method.escape_bytes // https://luajit.org/extensions.html#lua52 String::from_utf8( s.as_ref() .iter() .flat_map(|b| std::ascii::escape_default(*b)) .collect(), ) .unwrap() } fn generate_path(f: &mut Formatter, path: &Path) -> Result<()> { for (i, segment) in path.segments.iter().enumerate() { (i != 0).then(|| f.write(".")); generate_path_segment(f, segment)?; } Ok(()) } fn generate_path_segment(f: &mut Formatter, seg: &PathSegment) -> Result<()> { match seg.arguments { PathArguments::AngleBracketed(ref arg) => syn_error!(arg, "cannot be generic"), PathArguments::Parenthesized(ref arg) => syn_error!(arg, "cannot be generic"), PathArguments::None => generate_ident(f, &seg.ident), } } fn generate_block(f: &mut Formatter, block: &Block, cx: Context) -> Result<()> { let len = block.stmts.len(); for (i, stmt) in block.stmts.iter().enumerate() { match stmt { Stmt::Local(local) => generate_local(f, local), Stmt::Item(item) => generate_item(f, item), Stmt::Expr(expr, semi) => match semi { None if i == len - 1 && cx.is_ret() => generate_expr(f, expr, Context::stmt(true)), _ => generate_expr(f, expr, Context::stmt(false)), }, Stmt::Macro(smac) => generate_macro(f, &smac.mac, cx), }?; f.write(";"); } Ok(()) } fn generate_local(f: &mut Formatter, local: &Local) -> Result<()> { assert_no_attrs!(local); f.write("local"); generate_pat(f, &local.pat, PatContext::Multi)?; match local.init { Some(ref init) => generate_local_init(f, init), None => Ok(()), } } fn generate_local_init(f: &mut Formatter, init: &LocalInit) -> Result<()> { match init.diverge { Some((ref token, _)) => syn_error!(token, "let-else is not supported"), None => { f.write("="); generate_expr(f, &init.expr, Context::expr(true)) } } } fn generate_item(f: &mut Formatter, item: &Item) -> Result<()> { match item { Item::Fn(func) => generate_item_fn(f, func), item => syn_error!(item, "unsupported item"), } } fn generate_item_fn(f: &mut Formatter, func: &ItemFn) -> Result<()> { assert_no_attrs!(func); let Visibility::Inherited = func.vis else { syn_error!(func, "cannot have visibility"); }; f.write("local"); generate_signature(f, &func.sig)?; generate_block(f, &func.block, Context::stmt(true))?; f.write("end"); Ok(()) } fn generate_signature(f: &mut Formatter, sig: &Signature) -> Result<()> { if let Some(ref cons) = sig.constness { syn_error!(cons, "cannot be const"); } else if let Some(ref asyn) = sig.asyncness { syn_error!(asyn, "cannot be async"); } else if let Some(ref uns) = sig.unsafety { syn_error!(uns, "cannot be unsafe"); } else if let Some(ref abi) = sig.abi { syn_error!(abi, "cannot be extern"); } else if !sig.generics.params.is_empty() { syn_error!(sig.generics.params, "cannot be generic"); } else if let Some(ref wher) = sig.generics.where_clause { syn_error!(wher, "cannot have where clause"); } else if let ReturnType::Type(_, ref ty) = sig.output { syn_error!(ty, "cannot have return type"); } f.write("function"); generate_ident(f, &sig.ident)?; f.write("("); for (i, param) in sig.inputs.iter().enumerate() { (i != 0).then(|| f.write(",")); generate_fn_arg(f, param)?; } f.write(")"); Ok(()) } fn generate_fn_arg(f: &mut Formatter, param: &FnArg) -> Result<()> { match param { FnArg::Receiver(recv) => generate_receiver(f, recv), FnArg::Typed(typed) => generate_pat_typed(f, typed, PatContext::Single), } } fn generate_receiver(f: &mut Formatter, recv: &Receiver) -> Result<()> { assert_no_attrs!(recv); syn_assert!(recv.colon_token.is_none(), recv, "must be `self`"); if let Some(ref l) = recv.lifetime() { syn_error!(l, "cannot have lifetimes"); } else if let Some(ref m) = recv.mutability { syn_error!(m, "cannot be mut (implicitly mutable)"); } f.write("self"); Ok(()) } fn generate_macro(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> { match format!("{}", mac.path.require_ident()?).as_str() { "concat" => generate_macro_concat(f, mac, cx), "len" => generate_macro_len(f, mac, cx), "variadic" => generate_macro_variadic(f, mac, cx), "embed" => generate_macro_embed(f, mac, cx), "raw" => generate_macro_raw(f, mac, cx), name => syn_error!(mac.path, "unknown macro '{name}'"), } } fn generate_macro_concat(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> { syn_assert!(cx.is_value(), mac, "concat! must be in expression position"); cx.is_ret().then(|| f.write("return")); let args = mac.parse_body_with(>::parse_terminated)?; syn_assert!(!args.is_empty(), mac, "expected at least one argument"); for (i, arg) in args.iter().enumerate() { (i != 0).then(|| f.write("..")); generate_expr(f, arg, Context::expr(false))?; } Ok(()) } fn generate_macro_len(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> { syn_assert!(cx.is_value(), mac, "len! must be in expression position"); cx.is_ret().then(|| f.write("return")); f.write("#"); generate_expr(f, &mac.parse_body()?, Context::expr(false)) } fn generate_macro_variadic(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> { syn_assert!( cx.is_multi_expr(), mac, "variadic! must be in multi-value expression position" ); cx.is_ret().then(|| f.write("return")); f.write("..."); Ok(()) } fn generate_macro_embed(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> { syn_assert!(cx.is_value(), mac, "embed! must be in expression position"); cx.is_ret().then(|| f.write("return")); let arg = mac.parse_body::()?; f.write("\""); f.interpolate(parse_quote! { str::replace(&str::replace(&::std::format!("{}", #arg), "\\", "\\\\"), "\"", "\\\"") }); f.write("\""); Ok(()) } fn generate_macro_raw(f: &mut Formatter, mac: &Macro, _cx: Context) -> Result<()> { f.interpolate(mac.parse_body()?); Ok(()) } #[derive(Debug, Clone, Copy)] enum PatContext { Single, Multi, } impl PatContext { fn _is_single(&self) -> bool { matches!(self, Self::Single) } fn is_multi(&self) -> bool { matches!(self, Self::Multi) } } fn generate_pat(f: &mut Formatter, pat: &Pat, cx: PatContext) -> Result<()> { match pat { Pat::Ident(ident) => generate_pat_ident(f, ident, cx), Pat::Macro(mac) => generate_pat_macro(f, mac, cx), Pat::Tuple(tuple) => generate_pat_tuple(f, tuple, cx), Pat::Type(typed) => generate_pat_typed(f, typed, cx), Pat::Wild(wild) => generate_pat_wild(f, wild, cx), pat => syn_error!(pat, "unsupported pattern"), } } fn generate_pat_ident(f: &mut Formatter, ident: &PatIdent, _cx: PatContext) -> Result<()> { assert_no_attrs!(ident); if let Some(ref r) = ident.by_ref { syn_error!(r, "cannot be ref"); } else if let Some(ref m) = ident.mutability { syn_error!(m, "cannot be mut (implicitly mutable)"); } else if let Some((_, ref pat)) = ident.subpat { syn_error!(pat, "subpatterns are not supported"); } generate_ident(f, &ident.ident) } fn generate_pat_macro(f: &mut Formatter, mac: &PatMacro, cx: PatContext) -> Result<()> { assert_no_attrs!(mac); generate_macro(f, &mac.mac, Context::expr(cx.is_multi())) } fn generate_pat_tuple(f: &mut Formatter, tuple: &PatTuple, cx: PatContext) -> Result<()> { assert_no_attrs!(tuple); match tuple.elems.len() { 0 => syn_error!(tuple, "must have at least one element"), _ if cx.is_multi() => generate_punctuated_pat(f, &tuple.elems), _ => syn_error!(tuple, "expected single-valued pattern"), } } fn generate_pat_typed(f: &mut Formatter, typed: &PatType, cx: PatContext) -> Result<()> { assert_no_attrs!(typed); match *typed.ty { Type::Infer(_) => generate_pat(f, &typed.pat, cx), ref ty => syn_error!(ty, "cannot have type"), } } fn generate_pat_wild(f: &mut Formatter, wild: &PatWild, _cx: PatContext) -> Result<()> { assert_no_attrs!(wild); f.write("_"); Ok(()) } fn generate_punctuated_pat(f: &mut Formatter, pats: &Punctuated) -> Result<()> { let len = pats.len(); for (i, pat) in pats.iter().enumerate() { (i != 0).then(|| f.write(",")); let cx = if i == len - 1 { PatContext::Multi } else { PatContext::Single }; generate_pat(f, pat, cx)?; } Ok(()) }