1use crate::ast::{BinaryOp, DoStatement, Expr, PostfixOp, RecordEntry, RecordKey, UnaryOp};
2use crate::values::{LambdaArg, SerializableValue};
3use indexmap::IndexMap;
4
5pub fn expr_to_source(expr: &Expr) -> String {
6 match expr {
7 Expr::Number(n) => {
8 if n.fract() == 0.0 && n.abs() < 1e15 {
9 format!("{:.0}", n)
10 } else {
11 n.to_string()
12 }
13 }
14 Expr::String(s) => format!("\"{}\"", s.replace("\\", "\\\\").replace("\"", "\\\"")),
15 Expr::Bool(b) => b.to_string(),
16 Expr::Null => "null".to_string(),
17 Expr::Identifier(name) => name.clone(),
18 Expr::InputReference(field) => format!("#{}", field),
19 Expr::BuiltIn(built_in) => built_in.name().to_string(),
20 Expr::List(items) => {
21 let items_str: Vec<String> = items.iter().map(expr_to_source).collect();
22 format!("[{}]", items_str.join(", "))
23 }
24 Expr::Record(entries) => {
25 let entries_str: Vec<String> = entries.iter().map(record_entry_to_source).collect();
26 format!("{{{}}}", entries_str.join(", "))
27 }
28 Expr::Lambda { args, body } => {
29 let args_str: Vec<String> = args.iter().map(lambda_arg_to_source).collect();
30 format!("({}) => {}", args_str.join(", "), expr_to_source(body))
31 }
32 Expr::Conditional {
33 condition,
34 then_expr,
35 else_expr,
36 } => format!(
37 "if {} then {} else {}",
38 expr_to_source(condition),
39 expr_to_source(then_expr),
40 expr_to_source(else_expr)
41 ),
42 Expr::DoBlock {
43 statements,
44 return_expr,
45 } => {
46 let mut result = "do {".to_string();
47 for stmt in statements {
48 match stmt {
49 DoStatement::Expression(e) => {
50 result.push_str(&format!("\n {}", expr_to_source(e)));
51 }
52 DoStatement::Comment(c) => {
53 result.push_str(&format!("\n {}", c));
54 }
55 }
56 }
57 result.push_str(&format!("\n return {}\n}}", expr_to_source(return_expr)));
58 result
59 }
60 Expr::Assignment { ident, value } => format!("{} = {}", ident, expr_to_source(value)),
61 Expr::Call { func, args } => {
62 let args_str: Vec<String> = args.iter().map(expr_to_source).collect();
63 format!("{}({})", expr_to_source(func), args_str.join(", "))
64 }
65 Expr::Access { expr, index } => {
66 format!("{}[{}]", expr_to_source(expr), expr_to_source(index))
67 }
68 Expr::DotAccess { expr, field } => format!("{}.{}", expr_to_source(expr), field),
69 Expr::BinaryOp { op, left, right } => {
70 let op_str = binary_op_to_source(op);
71 format!(
72 "{} {} {}",
73 expr_to_source(left),
74 op_str,
75 expr_to_source(right)
76 )
77 }
78 Expr::UnaryOp { op, expr } => {
79 let op_str = unary_op_to_source(op);
80 format!("{}{}", op_str, expr_to_source(expr))
81 }
82 Expr::PostfixOp { op, expr } => {
83 let op_str = postfix_op_to_source(op);
84 format!("{}{}", expr_to_source(expr), op_str)
85 }
86 Expr::Spread(expr) => format!("...{}", expr_to_source(expr)),
87 }
88}
89
90fn lambda_arg_to_source(arg: &LambdaArg) -> String {
91 match arg {
92 LambdaArg::Required(name) => name.clone(),
93 LambdaArg::Optional(name) => format!("{}?", name),
94 LambdaArg::Rest(name) => format!("...{}", name),
95 }
96}
97
98fn record_entry_to_source(entry: &RecordEntry) -> String {
99 match &entry.key {
100 RecordKey::Static(key) => format!("{}: {}", key, expr_to_source(&entry.value)),
101 RecordKey::Dynamic(key_expr) => {
102 format!(
103 "[{}]: {}",
104 expr_to_source(key_expr),
105 expr_to_source(&entry.value)
106 )
107 }
108 RecordKey::Shorthand(name) => name.clone(),
109 RecordKey::Spread(expr) => expr_to_source(expr),
110 }
111}
112
113fn binary_op_to_source(op: &BinaryOp) -> &'static str {
114 match op {
115 BinaryOp::Add => "+",
116 BinaryOp::Subtract => "-",
117 BinaryOp::Multiply => "*",
118 BinaryOp::Divide => "/",
119 BinaryOp::Modulo => "%",
120 BinaryOp::Power => "^",
121 BinaryOp::Equal => "==",
122 BinaryOp::NotEqual => "!=",
123 BinaryOp::Less => "<",
124 BinaryOp::LessEq => "<=",
125 BinaryOp::Greater => ">",
126 BinaryOp::GreaterEq => ">=",
127 BinaryOp::DotEqual => ".==",
128 BinaryOp::DotNotEqual => ".!=",
129 BinaryOp::DotLess => ".<",
130 BinaryOp::DotLessEq => ".<=",
131 BinaryOp::DotGreater => ".>",
132 BinaryOp::DotGreaterEq => ".>=",
133 BinaryOp::And => "&&",
134 BinaryOp::NaturalAnd => "and",
135 BinaryOp::Or => "||",
136 BinaryOp::NaturalOr => "or",
137 BinaryOp::Via => "via",
138 BinaryOp::Into => "into",
139 BinaryOp::Coalesce => "??",
140 }
141}
142
143fn unary_op_to_source(op: &UnaryOp) -> &'static str {
144 match op {
145 UnaryOp::Negate => "-",
146 UnaryOp::Not => "!",
147 UnaryOp::Invert => "~",
148 }
149}
150
151fn postfix_op_to_source(op: &PostfixOp) -> &'static str {
152 match op {
153 PostfixOp::Factorial => "!",
154 }
155}
156
157pub fn expr_to_source_with_scope(
159 expr: &Expr,
160 scope: &IndexMap<String, SerializableValue>,
161) -> String {
162 match expr {
163 Expr::Identifier(name) => {
164 if let Some(value) = scope.get(name) {
166 serializable_value_to_source(value)
167 } else {
168 name.clone()
169 }
170 }
171 Expr::InputReference(field) => format!("#{}", field),
172 Expr::Number(n) => {
174 if n.fract() == 0.0 && n.abs() < 1e15 {
175 format!("{:.0}", n)
176 } else {
177 n.to_string()
178 }
179 }
180 Expr::String(s) => format!("\"{}\"", s.replace("\\", "\\\\").replace("\"", "\\\"")),
181 Expr::Bool(b) => b.to_string(),
182 Expr::Null => "null".to_string(),
183 Expr::BuiltIn(built_in) => built_in.name().to_string(),
184 Expr::List(items) => {
185 let items_str: Vec<String> = items
186 .iter()
187 .map(|e| expr_to_source_with_scope(e, scope))
188 .collect();
189 format!("[{}]", items_str.join(", "))
190 }
191 Expr::Record(entries) => {
192 let entries_str: Vec<String> = entries
193 .iter()
194 .map(|e| record_entry_to_source_with_scope(e, scope))
195 .collect();
196 format!("{{{}}}", entries_str.join(", "))
197 }
198 Expr::Lambda { args, body } => {
199 let args_str: Vec<String> = args.iter().map(lambda_arg_to_source).collect();
200 format!(
202 "({}) => {}",
203 args_str.join(", "),
204 expr_to_source_with_scope(body, scope)
205 )
206 }
207 Expr::Conditional {
208 condition,
209 then_expr,
210 else_expr,
211 } => format!(
212 "if {} then {} else {}",
213 expr_to_source_with_scope(condition, scope),
214 expr_to_source_with_scope(then_expr, scope),
215 expr_to_source_with_scope(else_expr, scope)
216 ),
217 Expr::DoBlock {
218 statements,
219 return_expr,
220 } => {
221 let mut result = "do {".to_string();
222 for stmt in statements {
223 match stmt {
224 DoStatement::Expression(e) => {
225 result.push_str(&format!("\n {}", expr_to_source_with_scope(e, scope)));
226 }
227 DoStatement::Comment(c) => {
228 result.push_str(&format!("\n // {}", c));
229 }
230 }
231 }
232 result.push_str(&format!(
233 "\n return {}",
234 expr_to_source_with_scope(return_expr, scope)
235 ));
236 result.push_str("\n}");
237 result
238 }
239 Expr::BinaryOp { op, left, right } => {
240 let op_str = binary_op_to_source(op);
241 format!(
242 "{} {} {}",
243 expr_to_source_with_scope(left, scope),
244 op_str,
245 expr_to_source_with_scope(right, scope)
246 )
247 }
248 Expr::UnaryOp { op, expr } => {
249 let op_str = match op {
250 UnaryOp::Negate => "-",
251 UnaryOp::Not => "!",
252 UnaryOp::Invert => "~",
253 };
254 format!("{}{}", op_str, expr_to_source_with_scope(expr, scope))
255 }
256 Expr::PostfixOp { op, expr } => {
257 let op_str = postfix_op_to_source(op);
258 format!("{}{}", expr_to_source_with_scope(expr, scope), op_str)
259 }
260 Expr::Spread(expr) => format!("...{}", expr_to_source_with_scope(expr, scope)),
261 Expr::Assignment { ident, value } => {
262 format!("{} = {}", ident, expr_to_source_with_scope(value, scope))
263 }
264 Expr::Call { func, args } => {
265 let args_str: Vec<String> = args
266 .iter()
267 .map(|e| expr_to_source_with_scope(e, scope))
268 .collect();
269 format!(
270 "{}({})",
271 expr_to_source_with_scope(func, scope),
272 args_str.join(", ")
273 )
274 }
275 Expr::Access { expr, index } => {
276 format!(
277 "{}[{}]",
278 expr_to_source_with_scope(expr, scope),
279 expr_to_source_with_scope(index, scope)
280 )
281 }
282 Expr::DotAccess { expr, field } => {
283 format!("{}.{}", expr_to_source_with_scope(expr, scope), field)
284 }
285 }
286}
287
288fn record_entry_to_source_with_scope(
289 entry: &RecordEntry,
290 scope: &IndexMap<String, SerializableValue>,
291) -> String {
292 match &entry.key {
293 RecordKey::Static(key) => {
294 format!(
295 "{}: {}",
296 key,
297 expr_to_source_with_scope(&entry.value, scope)
298 )
299 }
300 RecordKey::Dynamic(key_expr) => {
301 format!(
302 "[{}]: {}",
303 expr_to_source_with_scope(key_expr, scope),
304 expr_to_source_with_scope(&entry.value, scope)
305 )
306 }
307 RecordKey::Shorthand(name) => {
308 if let Some(value) = scope.get(name) {
310 format!("{}: {}", name, serializable_value_to_source(value))
311 } else {
312 name.clone()
313 }
314 }
315 RecordKey::Spread(expr) => expr_to_source_with_scope(expr, scope),
316 }
317}
318
319fn serializable_value_to_source(value: &SerializableValue) -> String {
321 match value {
322 SerializableValue::Number(n) => {
323 if n.fract() == 0.0 && n.abs() < 1e15 {
324 format!("{:.0}", n)
325 } else {
326 n.to_string()
327 }
328 }
329 SerializableValue::Bool(b) => b.to_string(),
330 SerializableValue::Null => "null".to_string(),
331 SerializableValue::String(s) => {
332 format!("\"{}\"", s.replace("\\", "\\\\").replace("\"", "\\\""))
333 }
334 SerializableValue::List(items) => {
335 let items_str: Vec<String> = items.iter().map(serializable_value_to_source).collect();
336 format!("[{}]", items_str.join(", "))
337 }
338 SerializableValue::Record(fields) => {
339 let entries_str: Vec<String> = fields
340 .iter()
341 .map(|(k, v)| format!("{}: {}", k, serializable_value_to_source(v)))
342 .collect();
343 format!("{{{}}}", entries_str.join(", "))
344 }
345 SerializableValue::Lambda(_) => "<function>".to_string(),
346 SerializableValue::BuiltIn(name) => name.clone(),
347 }
348}