1use crate::Program;
2
3use super::node::{AccessTarget, Args, Expr, Literal, Node, Params, Pattern, StringSegment};
4use std::fmt::Write;
5
6impl Node {
7 pub fn to_code(&self) -> String {
9 let mut output = String::new();
10 self.format_to_code(&mut output, 0);
11 output
12 }
13
14 fn format_to_code(&self, buf: &mut String, indent: usize) {
15 match &*self.expr {
16 Expr::Literal(lit) => {
17 format_literal(lit, buf);
18 }
19 Expr::Ident(ident) => {
20 write!(buf, "{}", ident).unwrap();
21 }
22 Expr::Self_ => {
23 buf.push_str("self");
24 }
25 Expr::Nodes => {
26 buf.push_str("nodes");
27 }
28 Expr::Selector(selector) => {
29 write!(buf, "{}", selector).unwrap();
30 }
31 Expr::Break(None) => {
32 buf.push_str("break");
33 }
34 Expr::Break(Some(value)) => {
35 buf.push_str("break: ");
36 value.format_to_code(buf, indent);
37 }
38 Expr::Continue => {
39 buf.push_str("continue");
40 }
41 Expr::Paren(node) => {
42 buf.push('(');
43 node.format_to_code(buf, indent);
44 buf.push(')');
45 }
46 Expr::And(left, right) => {
47 left.format_to_code(buf, indent);
48 buf.push_str(" && ");
49 right.format_to_code(buf, indent);
50 }
51 Expr::Or(left, right) => {
52 left.format_to_code(buf, indent);
53 buf.push_str(" || ");
54 right.format_to_code(buf, indent);
55 }
56 Expr::Call(func, args) => {
57 write!(buf, "{}", func).unwrap();
58 buf.push('(');
59 format_args(args, buf, indent);
60 buf.push(')');
61 }
62 Expr::CallDynamic(func, args) => {
63 func.format_to_code(buf, indent);
64 buf.push('(');
65 format_args(args, buf, indent);
66 buf.push(')');
67 }
68 Expr::Let(pattern, value) => {
69 buf.push_str("let ");
70 format_pattern(pattern, buf);
71 buf.push_str(" = ");
72 value.format_to_code(buf, indent);
73 }
74 Expr::Var(pattern, value) => {
75 buf.push_str("var ");
76 format_pattern(pattern, buf);
77 buf.push_str(" = ");
78 value.format_to_code(buf, indent);
79 }
80 Expr::Assign(ident, value) => {
81 write!(buf, "{} = ", ident).unwrap();
82 value.format_to_code(buf, indent);
83 }
84 Expr::If(branches) => {
85 for (i, (cond_opt, body)) in branches.iter().enumerate() {
86 if i == 0 {
87 buf.push_str("if ");
88 if let Some(cond) = cond_opt {
89 buf.push('(');
90 cond.format_to_code(buf, indent);
91 buf.push(')');
92 }
93 buf.push_str(": ");
94 body.format_to_code(buf, indent);
95 } else if let Some(cond) = cond_opt {
96 buf.push_str(" elif (");
97 cond.format_to_code(buf, indent);
98 buf.push_str("): ");
99 body.format_to_code(buf, indent);
100 } else {
101 buf.push_str(" else: ");
102 body.format_to_code(buf, indent);
103 }
104 }
105 }
106 Expr::While(cond, program) => {
107 buf.push_str("while (");
108 cond.format_to_code(buf, indent);
109 buf.push(')');
110 if needs_block_syntax(program) {
111 format_program_block(program, buf, indent);
112 } else if let Some(stmt) = program.first() {
113 buf.push_str(": ");
114 stmt.format_to_code(buf, indent);
115 }
116 }
117 Expr::Loop(program) => {
118 buf.push_str("loop");
119 format_program_block(program, buf, indent);
120 }
121 Expr::Foreach(item, iter, program) => {
122 write!(buf, "foreach({}, ", item).unwrap();
123 iter.format_to_code(buf, indent);
124 buf.push(')');
125 if needs_block_syntax(program) {
126 format_program_block(program, buf, indent);
127 } else if let Some(stmt) = program.first() {
128 buf.push_str(": ");
129 stmt.format_to_code(buf, indent);
130 }
131 }
132 Expr::Block(program) => {
133 let start_len = buf.len();
135 format_program_block(program, buf, indent);
136 if buf[start_len..].starts_with(' ') {
138 buf.replace_range(start_len..start_len + 1, "");
139 }
140 }
141 Expr::Def(name, params, program) => {
142 write!(buf, "def {}(", name).unwrap();
143 format_params(params, buf, indent);
144 buf.push(')');
145 if needs_block_syntax(program) {
146 format_program_block(program, buf, indent);
147 } else if let Some(stmt) = program.first() {
148 buf.push_str(": ");
149 stmt.format_to_code(buf, indent);
150 }
151 }
152 Expr::Fn(params, program) => {
153 buf.push_str("fn(");
154 format_params(params, buf, indent);
155 buf.push(')');
156 if needs_block_syntax(program) {
157 format_program_block(program, buf, indent);
158 } else if let Some(stmt) = program.first() {
159 buf.push_str(": ");
160 stmt.format_to_code(buf, indent);
161 }
162 }
163 Expr::Match(value, arms) => {
164 buf.push_str("match (");
165 value.format_to_code(buf, indent);
166 buf.push_str(") do");
167 for arm in arms.iter() {
168 buf.push('\n');
169 buf.push_str(&" ".repeat(indent + 1));
170 buf.push_str("| ");
171 format_pattern(&arm.pattern, buf);
172 if let Some(guard) = &arm.guard {
173 buf.push_str(" if ");
174 guard.format_to_code(buf, indent + 1);
175 }
176 buf.push_str(": ");
177 arm.body.format_to_code(buf, indent + 1);
178 }
179 buf.push('\n');
180 buf.push_str(&" ".repeat(indent));
181 buf.push_str("end");
182 }
183 Expr::InterpolatedString(segments) => {
184 buf.push_str("s\"");
185 for segment in segments.iter() {
186 match segment {
187 StringSegment::Text(text) => {
188 buf.push_str(&escape_string(text));
189 }
190 StringSegment::Expr(expr) => {
191 buf.push_str("${");
192 expr.format_to_code(buf, indent);
193 buf.push('}');
194 }
195 StringSegment::Env(name) => {
196 write!(buf, "${{{}}}", name).unwrap();
197 }
198 StringSegment::Self_ => {
199 buf.push_str("${self}");
200 }
201 }
202 }
203 buf.push('"');
204 }
205 Expr::Macro(name, params, body) => {
206 write!(buf, "macro {}(", name).unwrap();
207 format_params(params, buf, indent);
208 buf.push_str("): ");
209 body.format_to_code(buf, indent);
210 }
211 Expr::Quote(node) => {
212 buf.push_str("quote: ");
213 node.format_to_code(buf, indent);
214 }
215 Expr::Unquote(node) => {
216 buf.push_str("unquote(");
217 node.format_to_code(buf, indent);
218 buf.push(')');
219 }
220 Expr::Try(try_expr, catch_expr) => {
221 buf.push_str("try ");
222 try_expr.format_to_code(buf, indent);
223 buf.push_str(" catch: ");
224 catch_expr.format_to_code(buf, indent);
225 }
226 Expr::Module(name, program) => {
227 write!(buf, "module {}", name).unwrap();
228 format_program_block(program, buf, indent);
229 }
230 Expr::QualifiedAccess(path, target) => {
231 for (i, part) in path.iter().enumerate() {
232 if i > 0 {
233 buf.push_str("::");
234 }
235 write!(buf, "{}", part).unwrap();
236 }
237 match target {
238 AccessTarget::Call(func, args) => {
239 write!(buf, "::{}", func).unwrap();
240 buf.push('(');
241 format_args(args, buf, indent);
242 buf.push(')');
243 }
244 AccessTarget::Ident(ident) => {
245 write!(buf, "::{}", ident).unwrap();
246 }
247 }
248 }
249 Expr::Include(path) => {
250 buf.push_str("include ");
251 format_literal(path, buf);
252 }
253 Expr::Import(path) => {
254 buf.push_str("import ");
255 format_literal(path, buf);
256 }
257 }
258 }
259}
260
261fn escape_string(s: &str) -> String {
262 let mut result = String::with_capacity(s.len());
263 for ch in s.chars() {
264 match ch {
265 '\\' => result.push_str("\\\\"),
266 '"' => result.push_str("\\\""),
267 '\n' => result.push_str("\\n"),
268 '\t' => result.push_str("\\t"),
269 '\r' => result.push_str("\\r"),
270 _ => result.push(ch),
271 }
272 }
273 result
274}
275
276fn format_literal(literal: &Literal, buf: &mut String) {
277 match literal {
278 Literal::String(s) => {
279 buf.push('"');
280 buf.push_str(&escape_string(s));
281 buf.push('"');
282 }
283 Literal::Number(n) => {
284 write!(buf, "{}", n).unwrap();
285 }
286 Literal::Symbol(ident) => {
287 write!(buf, ":{}", ident).unwrap();
288 }
289 Literal::Bool(b) => {
290 buf.push_str(if *b { "true" } else { "false" });
291 }
292 Literal::None => {
293 buf.push_str("none");
294 }
295 }
296}
297
298fn format_args(args: &Args, buf: &mut String, indent: usize) {
299 for (i, arg) in args.iter().enumerate() {
300 if i > 0 {
301 buf.push_str(", ");
302 }
303 arg.format_to_code(buf, indent);
304 }
305}
306
307fn format_params(params: &Params, buf: &mut String, indent: usize) {
308 for (i, param) in params.iter().enumerate() {
309 if i > 0 {
310 buf.push_str(", ");
311 }
312
313 write!(buf, "{}", param.ident).unwrap();
314 if let Some(default) = ¶m.default {
315 buf.push_str(" = ");
316 default.format_to_code(buf, indent);
317 }
318 }
319}
320
321fn format_program_block(program: &Program, buf: &mut String, indent: usize) {
322 buf.push_str(" do\n");
323 buf.push_str(&" ".repeat(indent + 1));
324 for (i, stmt) in program.iter().enumerate() {
325 if i > 0 {
326 buf.push_str(" | ");
327 }
328 stmt.format_to_code(buf, indent + 1);
329 }
330 buf.push('\n');
331 buf.push_str(&" ".repeat(indent));
332 buf.push_str("end");
333}
334
335fn format_pattern(pattern: &Pattern, buf: &mut String) {
336 match pattern {
337 Pattern::Literal(lit) => {
338 format_literal(lit, buf);
339 }
340 Pattern::Ident(ident) => {
341 write!(buf, "{}", ident).unwrap();
342 }
343 Pattern::Wildcard => {
344 buf.push('_');
345 }
346 Pattern::Array(patterns) => {
347 buf.push('[');
348 for (i, p) in patterns.iter().enumerate() {
349 if i > 0 {
350 buf.push_str(", ");
351 }
352 format_pattern(p, buf);
353 }
354 buf.push(']');
355 }
356 Pattern::ArrayRest(patterns, rest) => {
357 buf.push('[');
358 for (i, p) in patterns.iter().enumerate() {
359 if i > 0 {
360 buf.push_str(", ");
361 }
362 format_pattern(p, buf);
363 }
364 if !patterns.is_empty() {
365 buf.push_str(", ");
366 }
367 write!(buf, "..{}", rest).unwrap();
368 buf.push(']');
369 }
370 Pattern::Dict(entries) => {
371 buf.push('{');
372 for (i, (key, value)) in entries.iter().enumerate() {
373 if i > 0 {
374 buf.push_str(", ");
375 }
376 write!(buf, "{}: ", key).unwrap();
377 format_pattern(value, buf);
378 }
379 buf.push('}');
380 }
381 Pattern::Type(type_name) => {
382 write!(buf, ":{}", type_name).unwrap();
383 }
384 }
385}
386
387fn needs_block_syntax(program: &Program) -> bool {
388 program.len() > 1
389}
390
391#[cfg(test)]
392mod tests {
393 use super::*;
394 use crate::{
395 Ident, IdentWithToken, Shared,
396 arena::ArenaId,
397 ast::node::{MatchArm, Param, Pattern},
398 number::Number,
399 };
400 use rstest::rstest;
401 use smallvec::smallvec;
402
403 fn create_node(expr: Expr) -> Node {
405 Node {
406 token_id: ArenaId::new(0),
407 expr: Shared::new(expr),
408 }
409 }
410
411 #[rstest]
412 #[case::string(Literal::String("hello".to_string()), r#""hello""#)]
413 #[case::string_with_quote(Literal::String(r#"hello"world"#.to_string()), r#""hello\"world""#)]
414 #[case::string_with_backslash(Literal::String(r"hello\world".to_string()), r#""hello\\world""#)]
415 #[case::string_with_newline(Literal::String("hello\nworld".to_string()), r#""hello\nworld""#)]
416 #[case::string_with_tab(Literal::String("hello\tworld".to_string()), r#""hello\tworld""#)]
417 #[case::number_int(Literal::Number(Number::new(42.0)), "42")]
418 #[case::number_float(Literal::Number(Number::new(42.5)), "42.5")]
419 #[case::symbol(Literal::Symbol(Ident::new("test")), ":test")]
420 #[case::bool_true(Literal::Bool(true), "true")]
421 #[case::bool_false(Literal::Bool(false), "false")]
422 #[case::none(Literal::None, "none")]
423 fn test_to_code_literals(#[case] literal: Literal, #[case] expected: &str) {
424 let node = create_node(Expr::Literal(literal));
425 assert_eq!(node.to_code(), expected);
426 }
427
428 #[rstest]
429 #[case::ident(Expr::Ident(IdentWithToken::new("foo")), "foo")]
430 #[case::self_(Expr::Self_, "self")]
431 #[case::nodes(Expr::Nodes, "nodes")]
432 #[case::break_(Expr::Break(None), "break")]
433 #[case::continue_(Expr::Continue, "continue")]
434 fn test_to_code_simple_expressions(#[case] expr: Expr, #[case] expected: &str) {
435 let node = create_node(expr);
436 assert_eq!(node.to_code(), expected);
437 }
438
439 #[rstest]
440 #[case::simple(Expr::Literal(Literal::Number(Number::new(42.0))), "(42)")]
441 #[case::ident(Expr::Ident(IdentWithToken::new("x")), "(x)")]
442 fn test_to_code_paren(#[case] inner_expr: Expr, #[case] expected: &str) {
443 let inner_node = Shared::new(create_node(inner_expr));
444 let node = create_node(Expr::Paren(inner_node));
445 assert_eq!(node.to_code(), expected);
446 }
447
448 #[rstest]
449 #[case::and(
450 Expr::And(
451 Shared::new(create_node(Expr::Ident(IdentWithToken::new("x")))),
452 Shared::new(create_node(Expr::Ident(IdentWithToken::new("y"))))
453 ),
454 "x && y"
455 )]
456 #[case::or(
457 Expr::Or(
458 Shared::new(create_node(Expr::Ident(IdentWithToken::new("a")))),
459 Shared::new(create_node(Expr::Ident(IdentWithToken::new("b"))))
460 ),
461 "a || b"
462 )]
463 fn test_to_code_operators(#[case] expr: Expr, #[case] expected: &str) {
464 let node = create_node(expr);
465 assert_eq!(node.to_code(), expected);
466 }
467
468 #[rstest]
469 #[case::no_args(
470 Expr::Call(IdentWithToken::new("test"), smallvec![]),
471 "test()"
472 )]
473 #[case::one_arg(
474 Expr::Call(
475 IdentWithToken::new("add"),
476 smallvec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
477 ),
478 "add(1)"
479 )]
480 #[case::two_args(
481 Expr::Call(
482 IdentWithToken::new("add"),
483 smallvec![
484 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0))))),
485 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0)))))
486 ]
487 ),
488 "add(1, 2)"
489 )]
490 fn test_to_code_call(#[case] expr: Expr, #[case] expected: &str) {
491 let node = create_node(expr);
492 assert_eq!(node.to_code(), expected);
493 }
494
495 #[rstest]
496 #[case::simple(
497 Expr::CallDynamic(
498 Shared::new(create_node(Expr::Ident(IdentWithToken::new("func")))),
499 smallvec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(42.0)))))]
500 ),
501 "func(42)"
502 )]
503 fn test_to_code_call_dynamic(#[case] expr: Expr, #[case] expected: &str) {
504 let node = create_node(expr);
505 assert_eq!(node.to_code(), expected);
506 }
507
508 #[rstest]
509 #[case::let_simple(
510 Expr::Let(
511 Pattern::Ident(IdentWithToken::new("x")),
512 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(42.0)))))
513 ),
514 "let x = 42"
515 )]
516 #[case::let_array_pattern(
517 Expr::Let(
518 Pattern::Array(vec![
519 Pattern::Ident(IdentWithToken::new("a")),
520 Pattern::Ident(IdentWithToken::new("b")),
521 ]),
522 Shared::new(create_node(Expr::Ident(IdentWithToken::new("arr"))))
523 ),
524 "let [a, b] = arr"
525 )]
526 #[case::let_dict_pattern(
527 Expr::Let(
528 Pattern::Dict(vec![
529 (IdentWithToken::new("x"), Pattern::Ident(IdentWithToken::new("x"))),
530 (IdentWithToken::new("y"), Pattern::Ident(IdentWithToken::new("y"))),
531 ]),
532 Shared::new(create_node(Expr::Ident(IdentWithToken::new("d"))))
533 ),
534 "let {x: x, y: y} = d"
535 )]
536 #[case::var_simple(
537 Expr::Var(
538 Pattern::Ident(IdentWithToken::new("y")),
539 Shared::new(create_node(Expr::Literal(Literal::String("hello".to_string()))))
540 ),
541 r#"var y = "hello""#
542 )]
543 #[case::assign_simple(
544 Expr::Assign(
545 IdentWithToken::new("z"),
546 Shared::new(create_node(Expr::Ident(IdentWithToken::new("value"))))
547 ),
548 "z = value"
549 )]
550 fn test_to_code_variables(#[case] expr: Expr, #[case] expected: &str) {
551 let node = create_node(expr);
552 assert_eq!(node.to_code(), expected);
553 }
554
555 #[rstest]
556 #[case::if_simple(
557 Expr::If(smallvec![
558 (
559 Some(Shared::new(create_node(Expr::Ident(IdentWithToken::new("x"))))),
560 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))
561 )
562 ]),
563 "if (x): 1"
564 )]
565 #[case::if_else(
566 Expr::If(smallvec![
567 (
568 Some(Shared::new(create_node(Expr::Ident(IdentWithToken::new("x"))))),
569 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))
570 ),
571 (
572 None,
573 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0)))))
574 )
575 ]),
576 "if (x): 1 else: 2"
577 )]
578 #[case::if_elif_else(
579 Expr::If(smallvec![
580 (
581 Some(Shared::new(create_node(Expr::Ident(IdentWithToken::new("x"))))),
582 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))
583 ),
584 (
585 Some(Shared::new(create_node(Expr::Ident(IdentWithToken::new("y"))))),
586 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0)))))
587 ),
588 (
589 None,
590 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(3.0)))))
591 )
592 ]),
593 "if (x): 1 elif (y): 2 else: 3"
594 )]
595 fn test_to_code_if(#[case] expr: Expr, #[case] expected: &str) {
596 let node = create_node(expr);
597 assert_eq!(node.to_code(), expected);
598 }
599
600 #[rstest]
601 #[case::while_inline(
602 Expr::While(
603 Shared::new(create_node(Expr::Ident(IdentWithToken::new("x")))),
604 vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
605 ),
606 "while (x): 1"
607 )]
608 #[case::while_block(
609 Expr::While(
610 Shared::new(create_node(Expr::Ident(IdentWithToken::new("x")))),
611 vec![
612 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0))))),
613 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0)))))
614 ]
615 ),
616 "while (x) do\n 1 | 2\nend"
617 )]
618 fn test_to_code_while(#[case] expr: Expr, #[case] expected: &str) {
619 let node = create_node(expr);
620 assert_eq!(node.to_code(), expected);
621 }
622
623 #[rstest]
624 #[case::loop_single(
625 Expr::Loop(vec![Shared::new(create_node(Expr::Break(None)))]),
626 "loop do\n break\nend"
627 )]
628 fn test_to_code_loop(#[case] expr: Expr, #[case] expected: &str) {
629 let node = create_node(expr);
630 assert_eq!(node.to_code(), expected);
631 }
632
633 #[rstest]
634 #[case::foreach_inline(
635 Expr::Foreach(
636 IdentWithToken::new("item"),
637 Shared::new(create_node(Expr::Ident(IdentWithToken::new("items")))),
638 vec![Shared::new(create_node(Expr::Ident(IdentWithToken::new("item"))))]
639 ),
640 "foreach(item, items): item"
641 )]
642 #[case::foreach_block(
643 Expr::Foreach(
644 IdentWithToken::new("x"),
645 Shared::new(create_node(Expr::Ident(IdentWithToken::new("arr")))),
646 vec![
647 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0))))),
648 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0)))))
649 ]
650 ),
651 "foreach(x, arr) do\n 1 | 2\nend"
652 )]
653 fn test_to_code_foreach(#[case] expr: Expr, #[case] expected: &str) {
654 let node = create_node(expr);
655 assert_eq!(node.to_code(), expected);
656 }
657
658 #[rstest]
659 #[case::single(
660 Expr::Block(vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]),
661 "do\n 1\nend"
662 )]
663 #[case::multiple(
664 Expr::Block(vec![
665 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0))))),
666 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0))))),
667 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(3.0)))))
668 ]),
669 "do\n 1 | 2 | 3\nend"
670 )]
671 fn test_to_code_block(#[case] expr: Expr, #[case] expected: &str) {
672 let node = create_node(expr);
673 assert_eq!(node.to_code(), expected);
674 }
675
676 #[rstest]
677 #[case::no_params_inline(
678 Expr::Def(
679 IdentWithToken::new("test"),
680 smallvec![],
681 vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(42.0)))))]
682 ),
683 "def test(): 42"
684 )]
685 #[case::with_params_inline(
686 Expr::Def(
687 IdentWithToken::new("add"),
688 smallvec![
689 Param::new(IdentWithToken::new("x")),
690 Param::new(IdentWithToken::new("y"))
691 ],
692 vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
693 ),
694 "def add(x, y): 1"
695 )]
696 #[case::with_default_value(
697 Expr::Def(
698 IdentWithToken::new("greet"),
699 smallvec![
700 Param::new(IdentWithToken::new("name")),
701 Param::with_default(
702 IdentWithToken::new("greeting"),
703 Some(Shared::new(create_node(Expr::Literal(Literal::String("Hello".to_string())))))
704 )
705 ],
706 vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
707 ),
708 r#"def greet(name, greeting = "Hello"): 1"#
709 )]
710 #[case::with_multiple_defaults(
711 Expr::Def(
712 IdentWithToken::new("foo"),
713 smallvec![
714 Param::new(IdentWithToken::new("a")),
715 Param::with_default(
716 IdentWithToken::new("b"),
717 Some(Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0))))))
718 ),
719 Param::with_default(
720 IdentWithToken::new("c"),
721 Some(Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(3.0))))))
722 )
723 ],
724 vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
725 ),
726 "def foo(a, b = 2, c = 3): 1"
727 )]
728 #[case::block(
729 Expr::Def(
730 IdentWithToken::new("test"),
731 smallvec![Param::new(IdentWithToken::new("x"))],
732 vec![
733 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0))))),
734 Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0)))))
735 ]
736 ),
737 "def test(x) do\n 1 | 2\nend"
738 )]
739 fn test_to_code_def(#[case] expr: Expr, #[case] expected: &str) {
740 let node = create_node(expr);
741 assert_eq!(node.to_code(), expected);
742 }
743
744 #[rstest]
745 #[case::no_params_inline(
746 Expr::Fn(
747 smallvec![],
748 vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(42.0)))))]
749 ),
750 "fn(): 42"
751 )]
752 #[case::with_params_inline(
753 Expr::Fn(
754 smallvec![
755 Param::new(IdentWithToken::new("x")),
756 Param::new(IdentWithToken::new("y"))
757 ],
758 vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
759 ),
760 "fn(x, y): 1"
761 )]
762 #[case::with_default_value(
763 Expr::Fn(
764 smallvec![
765 Param::new(IdentWithToken::new("x")),
766 Param::with_default(
767 IdentWithToken::new("y"),
768 Some(Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(10.0))))))
769 )
770 ],
771 vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
772 ),
773 "fn(x, y = 10): 1"
774 )]
775 fn test_to_code_fn(#[case] expr: Expr, #[case] expected: &str) {
776 let node = create_node(expr);
777 assert_eq!(node.to_code(), expected);
778 }
779
780 #[rstest]
781 #[case::simple(
782 Expr::Match(
783 Shared::new(create_node(Expr::Ident(IdentWithToken::new("x")))),
784 smallvec![
785 MatchArm {
786 pattern: Pattern::Literal(Literal::Number(Number::new(1.0))),
787 guard: None,
788 body: Shared::new(create_node(Expr::Literal(Literal::String("one".to_string()))))
789 },
790 MatchArm {
791 pattern: Pattern::Wildcard,
792 guard: None,
793 body: Shared::new(create_node(Expr::Literal(Literal::String("other".to_string()))))
794 }
795 ]
796 ),
797 "match (x) do\n | 1: \"one\"\n | _: \"other\"\nend"
798 )]
799 fn test_to_code_match(#[case] expr: Expr, #[case] expected: &str) {
800 let node = create_node(expr);
801 assert_eq!(node.to_code(), expected);
802 }
803
804 #[rstest]
805 #[case::text_only(
806 Expr::InterpolatedString(vec![StringSegment::Text("hello".to_string())]),
807 r#"s"hello""#
808 )]
809 #[case::with_expr(
810 Expr::InterpolatedString(vec![
811 StringSegment::Text("Hello ".to_string()),
812 StringSegment::Expr(Shared::new(create_node(Expr::Ident(IdentWithToken::new("name"))))),
813 StringSegment::Text("!".to_string())
814 ]),
815 r#"s"Hello ${name}!""#
816 )]
817 fn test_to_code_interpolated_string(#[case] expr: Expr, #[case] expected: &str) {
818 let node = create_node(expr);
819 assert_eq!(node.to_code(), expected);
820 }
821
822 #[rstest]
823 #[case::simple(
824 Expr::Macro(
825 IdentWithToken::new("double"),
826 smallvec![Param::new(IdentWithToken::new("x"))],
827 Shared::new(create_node(Expr::Ident(IdentWithToken::new("x"))))
828 ),
829 "macro double(x): x"
830 )]
831 #[case::with_default_value(
832 Expr::Macro(
833 IdentWithToken::new("greet_macro"),
834 smallvec![
835 Param::new(IdentWithToken::new("name")),
836 Param::with_default(
837 IdentWithToken::new("prefix"),
838 Some(Shared::new(create_node(Expr::Literal(Literal::String("Hi".to_string())))))
839 )
840 ],
841 Shared::new(create_node(Expr::Ident(IdentWithToken::new("name"))))
842 ),
843 r#"macro greet_macro(name, prefix = "Hi"): name"#
844 )]
845 fn test_to_code_macro(#[case] expr: Expr, #[case] expected: &str) {
846 let node = create_node(expr);
847 assert_eq!(node.to_code(), expected);
848 }
849
850 #[rstest]
851 #[case::quote(
852 Expr::Quote(Shared::new(create_node(Expr::Ident(IdentWithToken::new("x"))))),
853 "quote: x"
854 )]
855 #[case::unquote(
856 Expr::Unquote(Shared::new(create_node(Expr::Ident(IdentWithToken::new("x"))))),
857 "unquote(x)"
858 )]
859 fn test_to_code_quote_unquote(#[case] expr: Expr, #[case] expected: &str) {
860 let node = create_node(expr);
861 assert_eq!(node.to_code(), expected);
862 }
863
864 #[rstest]
865 #[case::simple(
866 Expr::Try(
867 Shared::new(create_node(Expr::Ident(IdentWithToken::new("risky")))),
868 Shared::new(create_node(Expr::Literal(Literal::String("error".to_string()))))
869 ),
870 r#"try risky catch: "error""#
871 )]
872 fn test_to_code_try(#[case] expr: Expr, #[case] expected: &str) {
873 let node = create_node(expr);
874 assert_eq!(node.to_code(), expected);
875 }
876
877 #[rstest]
878 #[case::simple(
879 Expr::Module(
880 IdentWithToken::new("math"),
881 vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
882 ),
883 "module math do\n 1\nend"
884 )]
885 fn test_to_code_module(#[case] expr: Expr, #[case] expected: &str) {
886 let node = create_node(expr);
887 assert_eq!(node.to_code(), expected);
888 }
889
890 #[rstest]
891 #[case::ident(
892 Expr::QualifiedAccess(
893 vec![IdentWithToken::new("module"), IdentWithToken::new("submodule")],
894 AccessTarget::Ident(IdentWithToken::new("func"))
895 ),
896 "module::submodule::func"
897 )]
898 #[case::call(
899 Expr::QualifiedAccess(
900 vec![IdentWithToken::new("module")],
901 AccessTarget::Call(
902 IdentWithToken::new("func"),
903 smallvec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
904 )
905 ),
906 "module::func(1)"
907 )]
908 fn test_to_code_qualified_access(#[case] expr: Expr, #[case] expected: &str) {
909 let node = create_node(expr);
910 assert_eq!(node.to_code(), expected);
911 }
912
913 #[rstest]
914 #[case::include(
915 Expr::Include(Literal::String("file.mq".to_string())),
916 r#"include "file.mq""#
917 )]
918 #[case::import(
919 Expr::Import(Literal::String("module.mq".to_string())),
920 r#"import "module.mq""#
921 )]
922 fn test_to_code_include_import(#[case] expr: Expr, #[case] expected: &str) {
923 let node = create_node(expr);
924 assert_eq!(node.to_code(), expected);
925 }
926
927 #[rstest]
929 #[case::nested_call(
930 Expr::Call(
931 IdentWithToken::new("map"),
932 smallvec![
933 Shared::new(create_node(Expr::Call(
934 IdentWithToken::new("filter"),
935 smallvec![Shared::new(create_node(Expr::Ident(IdentWithToken::new("items"))))]
936 )))
937 ]
938 ),
939 "map(filter(items))"
940 )]
941 fn test_to_code_complex(#[case] expr: Expr, #[case] expected: &str) {
942 let node = create_node(expr);
943 assert_eq!(node.to_code(), expected);
944 }
945}