dampen_core/codegen/
bindings.rs1use crate::CodegenError;
7use crate::expr::ast::{
8 BinaryOp, BinaryOpExpr, ConditionalExpr, Expr, FieldAccessExpr, LiteralExpr, MethodCallExpr,
9 UnaryOp, UnaryOpExpr,
10};
11use proc_macro2::TokenStream;
12use quote::{format_ident, quote};
13
14pub fn generate_expr(expr: &Expr) -> TokenStream {
25 match expr {
26 Expr::FieldAccess(field_access) => generate_field_access(field_access),
27 Expr::MethodCall(method_call) => generate_method_call(method_call),
28 Expr::BinaryOp(binary_op) => generate_binary_op(binary_op),
29 Expr::UnaryOp(unary_op) => generate_unary_op(unary_op),
30 Expr::Conditional(conditional) => generate_conditional(conditional),
31 Expr::Literal(literal) => generate_literal(literal),
32 }
33}
34
35pub fn validate_expression_inlinable(expr: &Expr) -> Result<(), CodegenError> {
39 match expr {
40 Expr::FieldAccess(_) => Ok(()),
41 Expr::MethodCall(method_expr) => {
42 validate_expression_inlinable(&method_expr.receiver)?;
43 for arg in &method_expr.args {
44 validate_expression_inlinable(arg)?;
45 }
46 Ok(())
47 }
48 Expr::BinaryOp(binary_expr) => {
49 validate_expression_inlinable(&binary_expr.left)?;
50 validate_expression_inlinable(&binary_expr.right)?;
51 Ok(())
52 }
53 Expr::UnaryOp(unary_expr) => {
54 validate_expression_inlinable(&unary_expr.operand)?;
55 Ok(())
56 }
57 Expr::Conditional(cond_expr) => {
58 validate_expression_inlinable(&cond_expr.condition)?;
59 validate_expression_inlinable(&cond_expr.then_branch)?;
60 validate_expression_inlinable(&cond_expr.else_branch)?;
61 Ok(())
62 }
63 Expr::Literal(_) => Ok(()),
64 }
65}
66
67fn generate_field_access(expr: &FieldAccessExpr) -> TokenStream {
75 if expr.path.is_empty() {
76 return quote! { String::new() };
77 }
78
79 let field_access: Vec<_> = expr.path.iter().map(|s| format_ident!("{}", s)).collect();
80
81 quote! { #(#field_access).*.to_string() }
82}
83
84fn generate_method_call(expr: &MethodCallExpr) -> TokenStream {
92 let receiver_tokens = generate_expr(&expr.receiver);
93 let method_ident = format_ident!("{}", expr.method);
94
95 if expr.args.is_empty() {
96 quote! { #receiver_tokens.#method_ident().to_string() }
97 } else {
98 let arg_tokens: Vec<TokenStream> = expr.args.iter().map(generate_expr).collect();
99 quote! { #receiver_tokens.#method_ident(#(#arg_tokens),*).to_string() }
100 }
101}
102
103fn generate_binary_op(expr: &BinaryOpExpr) -> TokenStream {
111 let left = generate_expr(&expr.left);
112 let right = generate_expr(&expr.right);
113 let op = match expr.op {
114 BinaryOp::Eq => quote! { == },
115 BinaryOp::Ne => quote! { != },
116 BinaryOp::Lt => quote! { < },
117 BinaryOp::Le => quote! { <= },
118 BinaryOp::Gt => quote! { > },
119 BinaryOp::Ge => quote! { >= },
120 BinaryOp::And => quote! { && },
121 BinaryOp::Or => quote! { || },
122 BinaryOp::Add => quote! { + },
123 BinaryOp::Sub => quote! { - },
124 BinaryOp::Mul => quote! { * },
125 BinaryOp::Div => quote! { / },
126 };
127
128 quote! { (#left #op #right).to_string() }
129}
130
131fn generate_unary_op(expr: &UnaryOpExpr) -> TokenStream {
139 let operand = generate_expr(&expr.operand);
140 let op = match expr.op {
141 UnaryOp::Not => quote! { ! },
142 UnaryOp::Neg => quote! { - },
143 };
144
145 quote! { (#op #operand).to_string() }
146}
147
148fn generate_conditional(expr: &ConditionalExpr) -> TokenStream {
156 let condition = generate_expr(&expr.condition);
157 let then_branch = generate_expr(&expr.then_branch);
158 let else_branch = generate_expr(&expr.else_branch);
159
160 quote! {
161 {
162 let __cond = #condition;
163 let __then = #then_branch;
164 let __else = #else_branch;
165 if __cond.trim() == "true" || __cond.parse::<bool>().unwrap_or(false) {
166 __then
167 } else {
168 __else
169 }
170 }
171 }
172}
173
174fn generate_literal(expr: &LiteralExpr) -> TokenStream {
182 match expr {
183 LiteralExpr::String(s) => {
184 let lit = proc_macro2::Literal::string(s);
185 quote! { #lit.to_string() }
186 }
187 LiteralExpr::Integer(n) => {
188 let lit = proc_macro2::Literal::i64_unsuffixed(*n);
189 quote! { #lit.to_string() }
190 }
191 LiteralExpr::Float(f) => {
192 let lit = proc_macro2::Literal::f64_unsuffixed(*f);
193 quote! { #lit.to_string() }
194 }
195 LiteralExpr::Bool(b) => {
196 let val = if *b { "true" } else { "false" };
197 let lit = proc_macro2::Literal::string(val);
198 quote! { #lit.to_string() }
199 }
200 }
201}
202
203pub fn generate_interpolated(parts: &[String]) -> TokenStream {
219 if parts.is_empty() {
220 return quote! { String::new() };
221 }
222
223 let mut format_args = Vec::new();
224 let mut arg_exprs = Vec::new();
225
226 for part in parts {
227 if part.starts_with('{') && part.ends_with('}') {
228 let binding_name = &part[1..part.len() - 1];
229 let field_parts: Vec<_> = binding_name
230 .split('.')
231 .map(|s| format_ident!("{}", s))
232 .collect();
233 format_args.push("{}");
234 arg_exprs.push(quote! { #(#field_parts).*.to_string() });
235 } else {
236 format_args.push(part);
237 }
238 }
239
240 let format_string = format_args.join("");
241 let lit = proc_macro2::Literal::string(&format_string);
242
243 quote! { format!(#lit, #(#arg_exprs),*) }
244}