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