1use crate::diagnostics::{BuildDiagnostics, Spanned};
8use crate::expression_tree::{
9 BuiltinFunction, BuiltinMacroFunction, Callable, EasingCurve, Expression, MinMaxOp,
10 MouseCursorInner, Unit,
11};
12use crate::langtype::Type;
13use crate::parser::NodeOrToken;
14use crate::symbol_counters::SymbolCounters;
15use smol_str::{ToSmolStr, format_smolstr};
16
17pub fn lower_macro(
19 mac: BuiltinMacroFunction,
20 n: &dyn Spanned,
21 mut sub_expr: impl Iterator<Item = (Expression, Option<NodeOrToken>)>,
22 diag: &mut BuildDiagnostics,
23 symbol_counters: &SymbolCounters,
24) -> Expression {
25 match mac {
26 BuiltinMacroFunction::Min => {
27 min_max_macro(n, MinMaxOp::Min, sub_expr.collect(), diag, symbol_counters)
28 }
29 BuiltinMacroFunction::Max => {
30 min_max_macro(n, MinMaxOp::Max, sub_expr.collect(), diag, symbol_counters)
31 }
32 BuiltinMacroFunction::Clamp => clamp_macro(n, sub_expr.collect(), diag, symbol_counters),
33 BuiltinMacroFunction::Mod => mod_macro(n, sub_expr.collect(), diag, symbol_counters),
34 BuiltinMacroFunction::Abs => abs_macro(n, sub_expr.collect(), diag, symbol_counters),
35 BuiltinMacroFunction::Sign => {
36 let Some((x, arg_node)) = sub_expr.next() else {
37 diag.push_error("Expected one argument".into(), n);
38 return Expression::Invalid;
39 };
40 if sub_expr.next().is_some() {
41 diag.push_error("Expected only one argument".into(), n);
42 }
43 Expression::Condition {
44 condition: Expression::BinaryExpression {
45 source_location: None,
46 lhs: x.maybe_convert_to(Type::Float32, &arg_node, diag, symbol_counters).into(),
47 rhs: Expression::NumberLiteral(0., Unit::None).into(),
48 op: '<',
49 }
50 .into(),
51 true_expr: Expression::NumberLiteral(-1., Unit::None).into(),
52 false_expr: Expression::NumberLiteral(1., Unit::None).into(),
53 source_location: None,
54 }
55 }
56 BuiltinMacroFunction::Debug => debug_macro(n, sub_expr.collect(), diag, symbol_counters),
57 BuiltinMacroFunction::CubicBezier => {
58 let mut has_error = None;
59 let expected_argument_type_error =
60 "Arguments to cubic bezier curve must be number literal";
61 let mut a = || match sub_expr.next() {
64 None => {
65 has_error.get_or_insert((n.to_source_location(), "Not enough arguments"));
66 0.
67 }
68 Some((Expression::NumberLiteral(val, Unit::None), _)) => val as f32,
69 Some((Expression::UnaryOp { sub, op: '-' }, n)) => match *sub {
71 Expression::NumberLiteral(val, Unit::None) => -val as f32,
72 _ => {
73 has_error
74 .get_or_insert((n.to_source_location(), expected_argument_type_error));
75 0.
76 }
77 },
78 Some((_, n)) => {
79 has_error.get_or_insert((n.to_source_location(), expected_argument_type_error));
80 0.
81 }
82 };
83 let expr = Expression::EasingCurve(EasingCurve::CubicBezier(a(), a(), a(), a()));
84 if let Some((_, n)) = sub_expr.next() {
85 has_error
86 .get_or_insert((n.to_source_location(), "Too many argument for bezier curve"));
87 }
88 if let Some((n, msg)) = has_error {
89 diag.push_error(msg.into(), &n);
90 }
91
92 expr
93 }
94 BuiltinMacroFunction::Rgb => rgb_macro(n, sub_expr.collect(), diag, symbol_counters),
95 BuiltinMacroFunction::Hsv => hsv_macro(n, sub_expr.collect(), diag, symbol_counters),
96 BuiltinMacroFunction::Oklch => oklch_macro(n, sub_expr.collect(), diag, symbol_counters),
97 BuiltinMacroFunction::ArrayPush => {
98 array_push_macro(n, sub_expr.collect(), diag, symbol_counters)
99 }
100 BuiltinMacroFunction::ArrayRemove => {
101 array_remove_macro(n, sub_expr.collect(), diag, symbol_counters)
102 }
103 BuiltinMacroFunction::ArrayInsert => {
104 array_insert_macro(n, sub_expr.collect(), diag, symbol_counters)
105 }
106 BuiltinMacroFunction::ArrayIndexOf => {
107 array_index_of_macro(n, sub_expr.collect(), diag, symbol_counters)
108 }
109 BuiltinMacroFunction::CustomMouseCursor => {
110 let mut has_error = None;
111 let hotspot_type_error = "The last two arguments to custom cursor must be an integer";
112
113 let mut next_arg =
115 |valid: fn(&Type) -> bool, type_error: &'static str| match sub_expr.next() {
116 Some((e, _)) if valid(&e.ty()) => e,
117 Some(_) => {
118 has_error.get_or_insert((n.to_source_location(), type_error));
119 Expression::Invalid
120 }
121 None => {
122 has_error.get_or_insert((n.to_source_location(), "Not enough arguments"));
123 Expression::Invalid
124 }
125 };
126
127 let image = next_arg(
128 |t| matches!(t, Type::Image),
129 "The first argument to custom cursor must be image",
130 );
131 let hotspot_x = next_arg(|t| t.can_convert(&Type::Int32), hotspot_type_error);
132 let hotspot_y = next_arg(|t| t.can_convert(&Type::Int32), hotspot_type_error);
133
134 let expr = Expression::MouseCursor(MouseCursorInner::CustomMouseCursor {
135 image: Box::new(image),
136 hotspot_x: Box::new(hotspot_x),
137 hotspot_y: Box::new(hotspot_y),
138 });
139 if let Some((_, n)) = sub_expr.next() {
140 has_error.get_or_insert((
141 n.to_source_location(),
142 "Too many arguments for custom cursor",
143 ));
144 }
145 if let Some((n, msg)) = has_error {
146 diag.push_error(msg.into(), &n);
147 }
148
149 expr
150 }
151 BuiltinMacroFunction::Spring => spring_macro(n, sub_expr.collect(), diag),
152 }
153}
154
155fn spring_macro(
156 node: &dyn Spanned,
157 args: Vec<(Expression, Option<NodeOrToken>)>,
158 diag: &mut BuildDiagnostics,
159) -> Expression {
160 let literal = |e: &Expression| match e {
161 Expression::NumberLiteral(val, Unit::None) => Some(*val),
162 _ => None,
163 };
164 let bounce = match args.as_slice() {
165 [(Expression::UnaryOp { sub, op: '-' }, _)] => literal(sub).map(|v| -v),
166 [(Expression::UnaryOp { sub, op: '+' }, _)] => literal(sub),
167 [(expr, _)] => literal(expr),
168 _ => None,
169 };
170 let Some(mut bounce) = bounce else {
171 diag.push_error("The spring curve needs a single number literal argument".into(), node);
172 return Expression::EasingCurve(EasingCurve::Spring(0.));
173 };
174 if !(-1.0..=1.0).contains(&bounce) {
175 let loc = args[0].1.as_ref().map_or(node, |n| n as &dyn Spanned);
176 diag.push_error("The bounce argument to spring curve must be between -1 and 1".into(), loc);
177 bounce = 0.;
178 }
179 Expression::EasingCurve(EasingCurve::Spring(bounce as f32))
180}
181
182fn min_max_macro(
183 node: &dyn Spanned,
184 op: MinMaxOp,
185 args: Vec<(Expression, Option<NodeOrToken>)>,
186 diag: &mut BuildDiagnostics,
187 symbol_counters: &SymbolCounters,
188) -> Expression {
189 if args.is_empty() {
190 diag.push_error("Needs at least one argument".into(), node);
191 return Expression::Invalid;
192 }
193 let ty = Expression::common_target_type_for_type_list(args.iter().map(|expr| expr.0.ty()));
194 if ty.as_unit_product().is_none() {
195 diag.push_error("Invalid argument type".into(), node);
196 return Expression::Invalid;
197 }
198 let mut args = args.into_iter();
199 let (base, arg_node) = args.next().unwrap();
200 let mut base = base.maybe_convert_to(ty.clone(), &arg_node, diag, symbol_counters);
201 for (next, arg_node) in args {
202 let rhs = next.maybe_convert_to(ty.clone(), &arg_node, diag, symbol_counters);
203 base = min_max_expression(base, rhs, op);
204 }
205 base
206}
207
208fn clamp_macro(
209 node: &dyn Spanned,
210 args: Vec<(Expression, Option<NodeOrToken>)>,
211 diag: &mut BuildDiagnostics,
212 symbol_counters: &SymbolCounters,
213) -> Expression {
214 if args.len() != 3 {
215 diag.push_error(
216 "`clamp` needs three values: the `value` to clamp, the `minimum` and the `maximum`"
217 .into(),
218 node,
219 );
220 return Expression::Invalid;
221 }
222 let (value, value_node) = args.first().unwrap().clone();
223 let ty = value.ty();
224 if ty.as_unit_product().is_none() {
225 diag.push_error("Invalid argument type".into(), &value_node);
226 return Expression::Invalid;
227 }
228
229 let (min, min_node) = args.get(1).unwrap().clone();
230 let min = min.maybe_convert_to(ty.clone(), &min_node, diag, symbol_counters);
231 let (max, max_node) = args.get(2).unwrap().clone();
232 let max = max.maybe_convert_to(ty.clone(), &max_node, diag, symbol_counters);
233
234 let value = min_max_expression(value, max, MinMaxOp::Min);
235 min_max_expression(min, value, MinMaxOp::Max)
236}
237
238fn mod_macro(
239 node: &dyn Spanned,
240 args: Vec<(Expression, Option<NodeOrToken>)>,
241 diag: &mut BuildDiagnostics,
242 symbol_counters: &SymbolCounters,
243) -> Expression {
244 if args.len() != 2 {
245 diag.push_error("Needs 2 arguments".into(), node);
246 return Expression::Invalid;
247 }
248 let (lhs_ty, rhs_ty) = (args[0].0.ty(), args[1].0.ty());
249 let common_ty = if lhs_ty.default_unit().is_some() {
250 lhs_ty
251 } else if rhs_ty.default_unit().is_some() {
252 rhs_ty
253 } else if matches!(lhs_ty, Type::UnitProduct(_)) {
254 lhs_ty
255 } else if matches!(rhs_ty, Type::UnitProduct(_)) {
256 rhs_ty
257 } else {
258 Type::Float32
259 };
260
261 let source_location = Some(node.to_source_location());
262 let function = Callable::Builtin(BuiltinFunction::Mod);
263 let arguments = args
264 .into_iter()
265 .map(|(e, n)| e.maybe_convert_to(common_ty.clone(), &n, diag, symbol_counters));
266 if matches!(common_ty, Type::Float32) {
267 Expression::FunctionCall { function, arguments: arguments.collect(), source_location }
268 } else {
269 Expression::Cast {
270 from: Expression::FunctionCall {
271 function,
272 arguments: arguments
273 .map(|a| Expression::Cast { from: a.into(), to: Type::Float32 })
274 .collect(),
275 source_location,
276 }
277 .into(),
278 to: common_ty.clone(),
279 }
280 }
281}
282
283fn abs_macro(
284 node: &dyn Spanned,
285 args: Vec<(Expression, Option<NodeOrToken>)>,
286 diag: &mut BuildDiagnostics,
287 symbol_counters: &SymbolCounters,
288) -> Expression {
289 if args.len() != 1 {
290 diag.push_error("Needs 1 argument".into(), node);
291 return Expression::Invalid;
292 }
293 let ty = args[0].0.ty();
294 let ty = if ty.default_unit().is_some() || matches!(ty, Type::UnitProduct(_)) {
295 ty
296 } else {
297 Type::Float32
298 };
299
300 let source_location = Some(node.to_source_location());
301 let function = Callable::Builtin(BuiltinFunction::Abs);
302 if matches!(ty, Type::Float32) {
303 let arguments = args
304 .into_iter()
305 .map(|(e, n)| e.maybe_convert_to(ty.clone(), &n, diag, symbol_counters))
306 .collect();
307 Expression::FunctionCall { function, arguments, source_location }
308 } else {
309 Expression::Cast {
310 from: Expression::FunctionCall {
311 function,
312 arguments: args
313 .into_iter()
314 .map(|(a, _)| Expression::Cast { from: a.into(), to: Type::Float32 })
315 .collect(),
316 source_location,
317 }
318 .into(),
319 to: ty,
320 }
321 }
322}
323
324fn rgb_macro(
325 node: &dyn Spanned,
326 args: Vec<(Expression, Option<NodeOrToken>)>,
327 diag: &mut BuildDiagnostics,
328 symbol_counters: &SymbolCounters,
329) -> Expression {
330 if args.len() < 3 || args.len() > 4 {
331 diag.push_error(
332 format!("This function needs 3 or 4 arguments, but {} were provided", args.len()),
333 node,
334 );
335 return Expression::Invalid;
336 }
337 let mut arguments: Vec<_> = args
338 .into_iter()
339 .enumerate()
340 .map(|(i, (expr, n))| {
341 if i < 3 {
342 if expr.ty() == Type::Percent {
343 Expression::BinaryExpression {
344 lhs: Box::new(expr.maybe_convert_to(
345 Type::Float32,
346 &n,
347 diag,
348 symbol_counters,
349 )),
350 rhs: Box::new(Expression::NumberLiteral(255., Unit::None)),
351 op: '*',
352 source_location: None,
353 }
354 } else {
355 expr.maybe_convert_to(Type::Float32, &n, diag, symbol_counters)
356 }
357 } else {
358 expr.maybe_convert_to(Type::Float32, &n, diag, symbol_counters)
359 }
360 })
361 .collect();
362 if arguments.len() < 4 {
363 arguments.push(Expression::NumberLiteral(1., Unit::None))
364 }
365 Expression::FunctionCall {
366 function: BuiltinFunction::Rgb.into(),
367 arguments,
368 source_location: Some(node.to_source_location()),
369 }
370}
371
372fn hsv_macro(
373 node: &dyn Spanned,
374 args: Vec<(Expression, Option<NodeOrToken>)>,
375 diag: &mut BuildDiagnostics,
376 symbol_counters: &SymbolCounters,
377) -> Expression {
378 if args.len() < 3 || args.len() > 4 {
379 diag.push_error(
380 format!("This function needs 3 or 4 arguments, but {} were provided", args.len()),
381 node,
382 );
383 return Expression::Invalid;
384 }
385 let mut arguments: Vec<_> = args
386 .into_iter()
387 .enumerate()
388 .map(|(i, (expr, n))| {
389 if i == 0 && expr.ty() == Type::Angle {
391 Expression::BinaryExpression {
392 lhs: Box::new(expr),
393 rhs: Box::new(Expression::NumberLiteral(1., Unit::Deg)),
394 op: '/',
395 source_location: None,
396 }
397 } else {
398 expr.maybe_convert_to(Type::Float32, &n, diag, symbol_counters)
399 }
400 })
401 .collect();
402 if arguments.len() < 4 {
403 arguments.push(Expression::NumberLiteral(1., Unit::None))
404 }
405 Expression::FunctionCall {
406 function: BuiltinFunction::Hsv.into(),
407 arguments,
408 source_location: Some(node.to_source_location()),
409 }
410}
411
412fn oklch_macro(
413 node: &dyn Spanned,
414 args: Vec<(Expression, Option<NodeOrToken>)>,
415 diag: &mut BuildDiagnostics,
416 symbol_counters: &SymbolCounters,
417) -> Expression {
418 if args.len() < 3 || args.len() > 4 {
419 diag.push_error(
420 format!("This function needs 3 or 4 arguments, but {} were provided", args.len()),
421 node,
422 );
423 return Expression::Invalid;
424 }
425 let mut arguments: Vec<_> = args
426 .into_iter()
427 .enumerate()
428 .map(|(i, (expr, n))| {
429 if i == 1 && expr.ty() == Type::Percent {
431 Expression::BinaryExpression {
432 lhs: Box::new(expr),
433 rhs: Box::new(Expression::NumberLiteral(0.004, Unit::None)),
434 op: '*',
435 source_location: None,
436 }
437 } else if i == 2 && expr.ty() == Type::Angle {
439 Expression::BinaryExpression {
440 lhs: Box::new(expr),
441 rhs: Box::new(Expression::NumberLiteral(1., Unit::Deg)),
442 op: '/',
443 source_location: None,
444 }
445 } else {
446 expr.maybe_convert_to(Type::Float32, &n, diag, symbol_counters)
447 }
448 })
449 .collect();
450 if arguments.len() < 4 {
451 arguments.push(Expression::NumberLiteral(1., Unit::None))
452 }
453 Expression::FunctionCall {
454 function: BuiltinFunction::Oklch.into(),
455 arguments,
456 source_location: Some(node.to_source_location()),
457 }
458}
459
460fn debug_macro(
461 node: &dyn Spanned,
462 args: Vec<(Expression, Option<NodeOrToken>)>,
463 diag: &mut BuildDiagnostics,
464 symbol_counters: &SymbolCounters,
465) -> Expression {
466 let mut string = None;
467 for (expr, node) in args {
468 let val = to_debug_string(expr, &node, diag, symbol_counters);
469 string = Some(match string {
470 None => val,
471 Some(string) => Expression::BinaryExpression {
472 lhs: Box::new(string),
473 op: '+',
474 rhs: Box::new(Expression::BinaryExpression {
475 source_location: None,
476 lhs: Box::new(Expression::StringLiteral(" ".into())),
477 op: '+',
478 rhs: Box::new(val),
479 }),
480 source_location: None,
481 },
482 });
483 }
484 Expression::FunctionCall {
485 function: BuiltinFunction::Debug.into(),
486 arguments: vec![
487 string.unwrap_or_else(|| Expression::default_value_for_type(&Type::String)),
488 ],
489 source_location: Some(node.to_source_location()),
490 }
491}
492
493fn array_push_macro(
494 node: &dyn Spanned,
495 mut args: Vec<(Expression, Option<NodeOrToken>)>,
496 diag: &mut BuildDiagnostics,
497 symbol_counters: &SymbolCounters,
498) -> Expression {
499 if args.len() != 2 {
500 diag.push_error(
501 format!("This method needs 1 argument, but {} were provided", args.len() - 1),
502 node,
503 );
504 return Expression::Invalid;
505 }
506
507 let element_type =
508 if let Type::Array(t) = args[0].0.ty() { (*t).clone() } else { Type::Invalid };
509
510 let (model_expr, _) = args.remove(0);
511 let (value_expr, value_node) = args.remove(0);
512 let value = value_expr.maybe_convert_to(element_type, &value_node, diag, symbol_counters);
513 Expression::FunctionCall {
514 function: Callable::Builtin(BuiltinFunction::ArrayPush),
515 arguments: vec![model_expr, value],
516 source_location: Some(node.to_source_location()),
517 }
518}
519
520fn array_remove_macro(
521 node: &dyn Spanned,
522 mut args: Vec<(Expression, Option<NodeOrToken>)>,
523 diag: &mut BuildDiagnostics,
524 symbol_counters: &SymbolCounters,
525) -> Expression {
526 if args.len() != 2 {
527 diag.push_error(
528 format!("This method needs 1 argument, but {} were provided", args.len() - 1),
529 node,
530 );
531 return Expression::Invalid;
532 }
533
534 let (model_expr, _) = args.remove(0);
535 let (index_expr, index_node) = args.remove(0);
536 let index = index_expr.maybe_convert_to(Type::Int32, &index_node, diag, symbol_counters);
537 Expression::FunctionCall {
538 function: Callable::Builtin(BuiltinFunction::ArrayRemove),
539 arguments: vec![model_expr, index],
540 source_location: Some(node.to_source_location()),
541 }
542}
543
544fn array_insert_macro(
545 node: &dyn Spanned,
546 mut args: Vec<(Expression, Option<NodeOrToken>)>,
547 diag: &mut BuildDiagnostics,
548 symbol_counters: &SymbolCounters,
549) -> Expression {
550 if args.len() != 3 {
551 diag.push_error(
552 format!("This method needs 2 arguments, but {} were provided", args.len() - 1),
553 node,
554 );
555 return Expression::Invalid;
556 }
557
558 let element_type =
559 if let Type::Array(t) = args[0].0.ty() { (*t).clone() } else { Type::Invalid };
560
561 let (model_expr, _) = args.remove(0);
562 let (index_expr, index_node) = args.remove(0);
563 let (value_expr, value_node) = args.remove(0);
564 let index = index_expr.maybe_convert_to(Type::Int32, &index_node, diag, symbol_counters);
565 let value = value_expr.maybe_convert_to(element_type, &value_node, diag, symbol_counters);
566 Expression::FunctionCall {
567 function: Callable::Builtin(BuiltinFunction::ArrayInsert),
568 arguments: vec![model_expr, index, value],
569 source_location: Some(node.to_source_location()),
570 }
571}
572
573fn array_index_of_macro(
576 node: &dyn Spanned,
577 mut args: Vec<(Expression, Option<NodeOrToken>)>,
578 diag: &mut BuildDiagnostics,
579 symbol_counters: &SymbolCounters,
580) -> Expression {
581 if args.len() != 2 {
582 diag.push_error(
583 format!("This method needs 1 argument, but {} were provided", args.len() - 1),
584 node,
585 );
586 return Expression::Invalid;
587 }
588
589 let element_type =
590 if let Type::Array(t) = args[0].0.ty() { (*t).clone() } else { Type::Invalid };
591
592 let (model_expr, _) = args.remove(0);
593 let (value_expr, value_node) = args.remove(0);
594 let value =
595 value_expr.maybe_convert_to(element_type.clone(), &value_node, diag, symbol_counters);
596
597 let value_local = symbol_counters.generate_name("index_of_value_");
600 let arg_name = symbol_counters.generate_name("index_of_element_");
601 let predicate = Expression::Closure {
602 arg_name: arg_name.clone(),
603 expression: Box::new(Expression::BinaryExpression {
604 lhs: Box::new(Expression::ReadLocalVariable {
605 name: arg_name,
606 ty: element_type.clone(),
607 }),
608 rhs: Box::new(Expression::ReadLocalVariable {
609 name: value_local.clone(),
610 ty: element_type,
611 }),
612 op: '=',
613 source_location: None,
614 }),
615 };
616
617 Expression::CodeBlock(vec![
618 Expression::StoreLocalVariable { name: value_local, value: Box::new(value) },
619 Expression::FunctionCall {
620 function: Callable::Builtin(BuiltinFunction::ArrayFindIndex),
621 arguments: vec![model_expr, predicate],
622 source_location: Some(node.to_source_location()),
623 },
624 ])
625}
626
627fn to_debug_string(
628 expr: Expression,
629 node: &dyn Spanned,
630 diag: &mut BuildDiagnostics,
631 symbol_counters: &SymbolCounters,
632) -> Expression {
633 let ty = expr.ty();
634 match &ty {
635 Type::Invalid => Expression::Invalid,
636 Type::Void
637 | Type::InferredCallback
638 | Type::InferredProperty
639 | Type::Callback { .. }
640 | Type::ComponentFactory
641 | Type::Function { .. }
642 | Type::ElementReference
643 | Type::LayoutCache
644 | Type::ArrayOfU16
645 | Type::Model
646 | Type::PathData
647 | Type::Closure => {
648 diag.push_error("Cannot debug this expression".into(), node);
649 Expression::Invalid
650 }
651 Type::Float32 | Type::Int32 => {
652 expr.maybe_convert_to(Type::String, node, diag, symbol_counters)
653 }
654 Type::String => expr,
655 Type::Color
657 | Type::Brush
658 | Type::Image
659 | Type::Easing
660 | Type::MouseCursor
661 | Type::StyledText
662 | Type::Array(_)
663 | Type::DataTransfer => {
664 Expression::StringLiteral("<debug-of-this-type-not-yet-implemented>".into())
665 }
666 Type::Duration
667 | Type::PhysicalLength
668 | Type::LogicalLength
669 | Type::Rem
670 | Type::Angle
671 | Type::Percent
672 | Type::UnitProduct(_) => Expression::BinaryExpression {
673 lhs: Box::new(
674 Expression::Cast { from: Box::new(expr), to: Type::Float32 }.maybe_convert_to(
675 Type::String,
676 node,
677 diag,
678 symbol_counters,
679 ),
680 ),
681 op: '+',
682 rhs: Box::new(Expression::StringLiteral(
683 Type::UnitProduct(ty.as_unit_product().unwrap()).to_smolstr(),
684 )),
685 source_location: None,
686 },
687 Type::Bool => Expression::Condition {
688 condition: Box::new(expr),
689 true_expr: Box::new(Expression::StringLiteral("true".into())),
690 false_expr: Box::new(Expression::StringLiteral("false".into())),
691 source_location: None,
692 },
693 Type::Struct(s) => {
694 let local_object = symbol_counters.generate_name("debug_struct");
695 let mut string = None;
696 for k in s.fields.keys() {
697 let field_name = if string.is_some() {
698 format_smolstr!(", {}: ", k)
699 } else {
700 format_smolstr!("{{ {}: ", k)
701 };
702 let value = to_debug_string(
703 Expression::StructFieldAccess {
704 base: Box::new(Expression::ReadLocalVariable {
705 name: local_object.clone(),
706 ty: ty.clone(),
707 }),
708 name: k.clone(),
709 },
710 node,
711 diag,
712 symbol_counters,
713 );
714 let field = Expression::BinaryExpression {
715 lhs: Box::new(Expression::StringLiteral(field_name)),
716 op: '+',
717 rhs: Box::new(value),
718 source_location: None,
719 };
720 string = Some(match string {
721 None => field,
722 Some(x) => Expression::BinaryExpression {
723 lhs: Box::new(x),
724 op: '+',
725 rhs: Box::new(field),
726 source_location: None,
727 },
728 });
729 }
730 match string {
731 None => Expression::StringLiteral("{}".into()),
732 Some(string) => Expression::CodeBlock(vec![
733 Expression::StoreLocalVariable { name: local_object, value: Box::new(expr) },
734 Expression::BinaryExpression {
735 source_location: None,
736 lhs: Box::new(string),
737 op: '+',
738 rhs: Box::new(Expression::StringLiteral(" }".into())),
739 },
740 ]),
741 }
742 }
743 Type::Enumeration(_) | Type::Keys => {
744 Expression::Cast { from: Box::new(expr), to: (Type::String) }
745 }
746 }
747}
748
749pub fn min_max_expression(lhs: Expression, rhs: Expression, op: MinMaxOp) -> Expression {
753 let lhs_ty = lhs.ty();
754 let rhs_ty = rhs.ty();
755 let ty = match (lhs_ty, rhs_ty) {
756 (a, b) if a == b => a,
757 (Type::Int32, Type::Float32) | (Type::Float32, Type::Int32) => Type::Float32,
758 _ => Type::Invalid,
759 };
760 Expression::MinMax { ty, op, lhs: Box::new(lhs), rhs: Box::new(rhs) }
761}