1use crate::diagnostics::{BuildDiagnostics, Spanned};
8use crate::expression_tree::{
9 BuiltinFunction, BuiltinMacroFunction, EasingCurve, Expression, Unit,
10};
11use crate::langtype::{EnumerationValue, Type};
12use crate::parser::NodeOrToken;
13
14static COUNTER: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(1);
16
17pub fn lower_macro(
19 mac: BuiltinMacroFunction,
20 n: Option<NodeOrToken>,
21 mut sub_expr: impl Iterator<Item = (Expression, Option<NodeOrToken>)>,
22 diag: &mut BuildDiagnostics,
23) -> Expression {
24 match mac {
25 BuiltinMacroFunction::Min => min_max_macro(n, '<', sub_expr.collect(), diag),
26 BuiltinMacroFunction::Max => min_max_macro(n, '>', sub_expr.collect(), diag),
27 BuiltinMacroFunction::Debug => debug_macro(n, sub_expr.collect(), diag),
28 BuiltinMacroFunction::CubicBezier => {
29 let mut has_error = None;
30 let mut a = || match sub_expr.next() {
33 None => {
34 has_error.get_or_insert((n.clone(), "Not enough arguments"));
35 0.
36 }
37 Some((Expression::NumberLiteral(val, Unit::None), _)) => val as f32,
38 Some((_, n)) => {
39 has_error.get_or_insert((
40 n,
41 "Arguments to cubic bezier curve must be number literal",
42 ));
43 0.
44 }
45 };
46 let expr = Expression::EasingCurve(EasingCurve::CubicBezier(a(), a(), a(), a()));
47 if let Some((_, n)) = sub_expr.next() {
48 has_error.get_or_insert((n, "Too many argument for bezier curve"));
49 }
50 if let Some((n, msg)) = has_error {
51 diag.push_error(msg.into(), &n);
52 }
53
54 expr
55 }
56 BuiltinMacroFunction::Rgb => rgb_macro(n, sub_expr.collect(), diag),
57 }
58}
59
60fn min_max_macro(
61 node: Option<NodeOrToken>,
62 op: char,
63 args: Vec<(Expression, Option<NodeOrToken>)>,
64 diag: &mut BuildDiagnostics,
65) -> Expression {
66 if args.is_empty() {
67 diag.push_error("Needs at least one argument".into(), &node);
68 return Expression::Invalid;
69 }
70 let mut args = args.into_iter();
71 let (mut base, arg_node) = args.next().unwrap();
72 let ty = match base.ty() {
73 Type::Float32 => Type::Float32,
74 Type::Int32 => Type::Float32,
76 Type::PhysicalLength => Type::PhysicalLength,
77 Type::LogicalLength => Type::LogicalLength,
78 Type::Duration => Type::Duration,
79 Type::Angle => Type::Angle,
80 Type::Percent => Type::Float32,
81 _ => {
82 diag.push_error("Invalid argument type".into(), &arg_node);
83 return Expression::Invalid;
84 }
85 };
86 for (next, arg_node) in args {
87 let rhs = next.maybe_convert_to(ty.clone(), &arg_node, diag);
88 base = min_max_expression(base, rhs, op);
89 }
90 base
91}
92
93fn rgb_macro(
94 node: Option<NodeOrToken>,
95 args: Vec<(Expression, Option<NodeOrToken>)>,
96 diag: &mut BuildDiagnostics,
97) -> Expression {
98 if args.len() < 3 {
99 diag.push_error("Needs 3 or 4 argument".into(), &node);
100 return Expression::Invalid;
101 }
102 let mut arguments: Vec<_> = args
103 .into_iter()
104 .enumerate()
105 .map(|(i, (expr, n))| {
106 if i < 3 {
107 if expr.ty() == Type::Percent {
108 Expression::BinaryExpression {
109 lhs: Box::new(expr.maybe_convert_to(Type::Float32, &n, diag)),
110 rhs: Box::new(Expression::NumberLiteral(255., Unit::None)),
111 op: '*',
112 }
113 } else {
114 expr.maybe_convert_to(Type::Int32, &n, diag)
115 }
116 } else {
117 expr.maybe_convert_to(Type::Float32, &n, diag)
118 }
119 })
120 .collect();
121 if arguments.len() < 4 {
122 arguments.push(Expression::NumberLiteral(1., Unit::None))
123 }
124 Expression::FunctionCall {
125 function: Box::new(Expression::BuiltinFunctionReference(
126 BuiltinFunction::Rgb,
127 node.as_ref().map(|t| t.to_source_location()),
128 )),
129 arguments,
130 source_location: Some(node.to_source_location()),
131 }
132}
133
134fn debug_macro(
135 node: Option<NodeOrToken>,
136 args: Vec<(Expression, Option<NodeOrToken>)>,
137 diag: &mut BuildDiagnostics,
138) -> Expression {
139 let mut string = None;
140 for (expr, node) in args {
141 let val = to_debug_string(expr, node, diag);
142 string = Some(match string {
143 None => val,
144 Some(string) => Expression::BinaryExpression {
145 lhs: Box::new(string),
146 op: '+',
147 rhs: Box::new(Expression::BinaryExpression {
148 lhs: Box::new(Expression::StringLiteral(", ".into())),
149 op: '+',
150 rhs: Box::new(val),
151 }),
152 },
153 });
154 }
155 let sl = node.map(|node| node.to_source_location());
156 Expression::FunctionCall {
157 function: Box::new(Expression::BuiltinFunctionReference(
158 BuiltinFunction::Debug,
159 sl.clone(),
160 )),
161 arguments: vec![string.unwrap_or_else(|| Expression::default_value_for_type(&Type::String))],
162 source_location: sl,
163 }
164}
165
166fn to_debug_string(
167 expr: Expression,
168 node: Option<NodeOrToken>,
169 diag: &mut BuildDiagnostics,
170) -> Expression {
171 let ty = expr.ty();
172 match &ty {
173 Type::Invalid => Expression::Invalid,
174 Type::Void
175 | Type::InferredCallback
176 | Type::InferredProperty
177 | Type::Component(_)
178 | Type::Builtin(_)
179 | Type::Native(_)
180 | Type::Callback { .. }
181 | Type::Function { .. }
182 | Type::ElementReference
183 | Type::LayoutCache
184 | Type::Model
185 | Type::PathData => {
186 diag.push_error("Cannot debug this expression".into(), &node);
187 Expression::Invalid
188 }
189 Type::Float32 | Type::Int32 => expr.maybe_convert_to(Type::String, &node, diag),
190 Type::String => expr,
191 Type::Color | Type::Brush | Type::Image | Type::Easing | Type::Array(_) => {
193 Expression::StringLiteral("<debug-of-this-type-not-yet-implemented>".into())
194 }
195 Type::Duration
196 | Type::PhysicalLength
197 | Type::LogicalLength
198 | Type::Angle
199 | Type::Percent
200 | Type::UnitProduct(_) => Expression::BinaryExpression {
201 lhs: Box::new(
202 Expression::Cast { from: Box::new(expr), to: Type::Float32 }.maybe_convert_to(
203 Type::String,
204 &node,
205 diag,
206 ),
207 ),
208 op: '+',
209 rhs: Box::new(Expression::StringLiteral(
210 Type::UnitProduct(ty.as_unit_product().unwrap()).to_string(),
211 )),
212 },
213 Type::Bool => Expression::Condition {
214 condition: Box::new(expr),
215 true_expr: Box::new(Expression::StringLiteral("true".into())),
216 false_expr: Box::new(Expression::StringLiteral("false".into())),
217 },
218 Type::Struct { fields, .. } => {
219 let local_object = format!(
220 "debug_struct{}",
221 COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
222 );
223 let mut string = None;
224 for k in fields.keys() {
225 let field_name =
226 if string.is_some() { format!(", {}: ", k) } else { format!("{{ {}: ", k) };
227 let value = to_debug_string(
228 Expression::StructFieldAccess {
229 base: Box::new(Expression::ReadLocalVariable {
230 name: local_object.clone(),
231 ty: ty.clone(),
232 }),
233 name: k.clone(),
234 },
235 node.clone(),
236 diag,
237 );
238 let field = Expression::BinaryExpression {
239 lhs: Box::new(Expression::StringLiteral(field_name)),
240 op: '+',
241 rhs: Box::new(value),
242 };
243 string = Some(match string {
244 None => field,
245 Some(x) => Expression::BinaryExpression {
246 lhs: Box::new(x),
247 op: '+',
248 rhs: Box::new(field),
249 },
250 });
251 }
252 match string {
253 None => Expression::StringLiteral("{}".into()),
254 Some(string) => Expression::CodeBlock(vec![
255 Expression::StoreLocalVariable { name: local_object, value: Box::new(expr) },
256 Expression::BinaryExpression {
257 lhs: Box::new(string),
258 op: '+',
259 rhs: Box::new(Expression::StringLiteral(" }".into())),
260 },
261 ]),
262 }
263 }
264 Type::Enumeration(enu) => {
265 let local_object = "debug_enum";
266 let mut v = vec![Expression::StoreLocalVariable {
267 name: local_object.into(),
268 value: Box::new(expr),
269 }];
270 let mut cond = Expression::StringLiteral(format!("Error: invalid value for {}", ty));
271 for (idx, val) in enu.values.iter().enumerate() {
272 cond = Expression::Condition {
273 condition: Box::new(Expression::BinaryExpression {
274 lhs: Box::new(Expression::ReadLocalVariable {
275 name: local_object.into(),
276 ty: ty.clone(),
277 }),
278 rhs: Box::new(Expression::EnumerationValue(EnumerationValue {
279 value: idx,
280 enumeration: enu.clone(),
281 })),
282 op: '=',
283 }),
284 true_expr: Box::new(Expression::StringLiteral(val.clone())),
285 false_expr: Box::new(cond),
286 };
287 }
288 v.push(cond);
289 Expression::CodeBlock(v)
290 }
291 }
292}
293
294pub fn min_max_expression(lhs: Expression, rhs: Expression, op: char) -> Expression {
298 let ty = lhs.ty();
299 let id = COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
300 let n1 = format!("minmax_lhs{}", id);
301 let n2 = format!("minmax_rhs{}", id);
302 let a1 = Box::new(Expression::ReadLocalVariable { name: n1.clone(), ty: ty.clone() });
303 let a2 = Box::new(Expression::ReadLocalVariable { name: n2.clone(), ty });
304 Expression::CodeBlock(vec![
305 Expression::StoreLocalVariable { name: n1, value: Box::new(lhs) },
306 Expression::StoreLocalVariable { name: n2, value: Box::new(rhs) },
307 Expression::Condition {
308 condition: Box::new(Expression::BinaryExpression {
309 lhs: a1.clone(),
310 rhs: a2.clone(),
311 op,
312 }),
313 true_expr: a1,
314 false_expr: a2,
315 },
316 ])
317}