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