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