dampen_core/codegen/
bindings.rs1use crate::CodegenError;
7use crate::expr::ast::{
8 BinaryOp, BinaryOpExpr, ConditionalExpr, Expr, FieldAccessExpr, LiteralExpr, MethodCallExpr,
9 SharedFieldAccessExpr, 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::SharedFieldAccess(shared_access) => generate_shared_field_access(shared_access),
28 Expr::MethodCall(method_call) => generate_method_call(method_call),
29 Expr::BinaryOp(binary_op) => generate_binary_op(binary_op),
30 Expr::UnaryOp(unary_op) => generate_unary_op(unary_op),
31 Expr::Conditional(conditional) => generate_conditional(conditional),
32 Expr::Literal(literal) => generate_literal(literal),
33 }
34}
35
36pub fn generate_bool_expr(expr: &Expr) -> TokenStream {
47 match expr {
48 Expr::FieldAccess(field_access) => generate_field_access_raw(field_access),
49 Expr::SharedFieldAccess(shared_access) => generate_shared_field_access_raw(shared_access),
50 Expr::MethodCall(method_call) => generate_method_call_raw(method_call),
51 Expr::BinaryOp(binary_op) => generate_binary_op_raw(binary_op),
52 Expr::UnaryOp(unary_op) => generate_unary_op_raw(unary_op),
53 Expr::Conditional(conditional) => generate_conditional_raw(conditional),
54 Expr::Literal(literal) => generate_literal_raw(literal),
55 }
56}
57
58pub fn validate_expression_inlinable(expr: &Expr) -> Result<(), CodegenError> {
62 match expr {
63 Expr::FieldAccess(_) => Ok(()),
64 Expr::SharedFieldAccess(_) => Ok(()), Expr::MethodCall(method_expr) => {
66 validate_expression_inlinable(&method_expr.receiver)?;
67 for arg in &method_expr.args {
68 validate_expression_inlinable(arg)?;
69 }
70 Ok(())
71 }
72 Expr::BinaryOp(binary_expr) => {
73 validate_expression_inlinable(&binary_expr.left)?;
74 validate_expression_inlinable(&binary_expr.right)?;
75 Ok(())
76 }
77 Expr::UnaryOp(unary_expr) => {
78 validate_expression_inlinable(&unary_expr.operand)?;
79 Ok(())
80 }
81 Expr::Conditional(cond_expr) => {
82 validate_expression_inlinable(&cond_expr.condition)?;
83 validate_expression_inlinable(&cond_expr.then_branch)?;
84 validate_expression_inlinable(&cond_expr.else_branch)?;
85 Ok(())
86 }
87 Expr::Literal(_) => Ok(()),
88 }
89}
90
91fn generate_field_access(expr: &FieldAccessExpr) -> TokenStream {
99 if expr.path.is_empty() {
100 return quote! { String::new() };
101 }
102
103 let field_access: Vec<_> = expr.path.iter().map(|s| format_ident!("{}", s)).collect();
104
105 quote! { model.#(#field_access).*.to_string() }
106}
107
108fn generate_shared_field_access(expr: &SharedFieldAccessExpr) -> TokenStream {
117 if expr.path.is_empty() {
118 return quote! { String::new() };
119 }
120
121 let field_access: Vec<_> = expr.path.iter().map(|s| format_ident!("{}", s)).collect();
124
125 quote! { shared.#(#field_access).*.to_string() }
126}
127
128fn generate_method_call(expr: &MethodCallExpr) -> TokenStream {
136 let receiver_tokens = generate_expr(&expr.receiver);
137 let method_ident = format_ident!("{}", expr.method);
138
139 if expr.args.is_empty() {
140 quote! { #receiver_tokens.#method_ident().to_string() }
141 } else {
142 let arg_tokens: Vec<TokenStream> = expr.args.iter().map(generate_expr).collect();
143 quote! { #receiver_tokens.#method_ident(#(#arg_tokens),*).to_string() }
144 }
145}
146
147fn generate_binary_op(expr: &BinaryOpExpr) -> TokenStream {
155 let left = generate_expr(&expr.left);
156 let right = generate_expr(&expr.right);
157 let op = match expr.op {
158 BinaryOp::Eq => quote! { == },
159 BinaryOp::Ne => quote! { != },
160 BinaryOp::Lt => quote! { < },
161 BinaryOp::Le => quote! { <= },
162 BinaryOp::Gt => quote! { > },
163 BinaryOp::Ge => quote! { >= },
164 BinaryOp::And => quote! { && },
165 BinaryOp::Or => quote! { || },
166 BinaryOp::Add => quote! { + },
167 BinaryOp::Sub => quote! { - },
168 BinaryOp::Mul => quote! { * },
169 BinaryOp::Div => quote! { / },
170 };
171
172 quote! { (#left #op #right).to_string() }
173}
174
175fn generate_unary_op(expr: &UnaryOpExpr) -> TokenStream {
183 let operand = generate_expr(&expr.operand);
184 let op = match expr.op {
185 UnaryOp::Not => quote! { ! },
186 UnaryOp::Neg => quote! { - },
187 };
188
189 quote! { (#op #operand).to_string() }
190}
191
192fn generate_conditional(expr: &ConditionalExpr) -> TokenStream {
200 let condition = generate_expr(&expr.condition);
201 let then_branch = generate_expr(&expr.then_branch);
202 let else_branch = generate_expr(&expr.else_branch);
203
204 quote! {
205 {
206 let __cond = #condition;
207 let __then = #then_branch;
208 let __else = #else_branch;
209 if __cond.trim() == "true" || __cond.parse::<bool>().unwrap_or(false) {
210 __then
211 } else {
212 __else
213 }
214 }
215 }
216}
217
218fn generate_literal(expr: &LiteralExpr) -> TokenStream {
226 match expr {
227 LiteralExpr::String(s) => {
228 let lit = proc_macro2::Literal::string(s);
229 quote! { #lit.to_string() }
230 }
231 LiteralExpr::Integer(n) => {
232 let lit = proc_macro2::Literal::i64_unsuffixed(*n);
233 quote! { #lit.to_string() }
234 }
235 LiteralExpr::Float(f) => {
236 let lit = proc_macro2::Literal::f64_unsuffixed(*f);
237 quote! { #lit.to_string() }
238 }
239 LiteralExpr::Bool(b) => {
240 let val = if *b { "true" } else { "false" };
241 let lit = proc_macro2::Literal::string(val);
242 quote! { #lit.to_string() }
243 }
244 }
245}
246
247pub fn generate_interpolated(parts: &[String]) -> TokenStream {
263 if parts.is_empty() {
264 return quote! { String::new() };
265 }
266
267 let mut format_args = Vec::new();
268 let mut arg_exprs = Vec::new();
269
270 for part in parts {
271 if part.starts_with('{') && part.ends_with('}') {
272 let binding_name = &part[1..part.len() - 1];
273 let field_parts: Vec<_> = binding_name
274 .split('.')
275 .map(|s| format_ident!("{}", s))
276 .collect();
277 format_args.push("{}");
278 arg_exprs.push(quote! { #(#field_parts).*.to_string() });
279 } else {
280 format_args.push(part);
281 }
282 }
283
284 let format_string = format_args.join("");
285 let lit = proc_macro2::Literal::string(&format_string);
286
287 quote! { format!(#lit, #(#arg_exprs),*) }
288}
289
290fn generate_field_access_raw(expr: &FieldAccessExpr) -> TokenStream {
297 if expr.path.is_empty() {
298 return quote! { false };
299 }
300
301 let field_access: Vec<_> = expr.path.iter().map(|s| format_ident!("{}", s)).collect();
302 quote! { model.#(#field_access).* }
303}
304
305fn generate_shared_field_access_raw(expr: &SharedFieldAccessExpr) -> TokenStream {
307 if expr.path.is_empty() {
308 return quote! { false };
309 }
310
311 let field_access: Vec<_> = expr.path.iter().map(|s| format_ident!("{}", s)).collect();
312 quote! { shared.#(#field_access).* }
313}
314
315fn generate_method_call_raw(expr: &MethodCallExpr) -> TokenStream {
317 let receiver_tokens = generate_bool_expr(&expr.receiver);
318 let method_ident = format_ident!("{}", &expr.method);
319 let arg_tokens: Vec<_> = expr.args.iter().map(generate_bool_expr).collect();
320
321 quote! { #receiver_tokens.#method_ident(#(#arg_tokens),*) }
322}
323
324fn generate_binary_op_raw(expr: &BinaryOpExpr) -> TokenStream {
326 let left = generate_bool_expr(&expr.left);
327 let right = generate_bool_expr(&expr.right);
328 let op = match expr.op {
329 BinaryOp::Eq => quote! { == },
330 BinaryOp::Ne => quote! { != },
331 BinaryOp::Lt => quote! { < },
332 BinaryOp::Le => quote! { <= },
333 BinaryOp::Gt => quote! { > },
334 BinaryOp::Ge => quote! { >= },
335 BinaryOp::And => quote! { && },
336 BinaryOp::Or => quote! { || },
337 BinaryOp::Add => quote! { + },
338 BinaryOp::Sub => quote! { - },
339 BinaryOp::Mul => quote! { * },
340 BinaryOp::Div => quote! { / },
341 };
342
343 quote! { #left #op #right }
344}
345
346fn generate_unary_op_raw(expr: &UnaryOpExpr) -> TokenStream {
348 let operand = generate_bool_expr(&expr.operand);
349 let op = match expr.op {
350 UnaryOp::Not => quote! { ! },
351 UnaryOp::Neg => quote! { - },
352 };
353
354 quote! { #op #operand }
355}
356
357fn generate_conditional_raw(expr: &ConditionalExpr) -> TokenStream {
359 let condition = generate_bool_expr(&expr.condition);
360 let then_branch = generate_bool_expr(&expr.then_branch);
361 let else_branch = generate_bool_expr(&expr.else_branch);
362
363 quote! {
364 if #condition {
365 #then_branch
366 } else {
367 #else_branch
368 }
369 }
370}
371
372fn generate_literal_raw(expr: &LiteralExpr) -> TokenStream {
374 match expr {
375 LiteralExpr::String(s) => {
376 let lit = proc_macro2::Literal::string(s);
377 quote! { #lit }
378 }
379 LiteralExpr::Integer(n) => {
380 let lit = proc_macro2::Literal::i64_unsuffixed(*n);
381 quote! { #lit }
382 }
383 LiteralExpr::Float(f) => {
384 let lit = proc_macro2::Literal::f64_unsuffixed(*f);
385 quote! { #lit }
386 }
387 LiteralExpr::Bool(b) => {
388 quote! { #b }
389 }
390 }
391}