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