Skip to main content

mq_lang/ast/
code.rs

1use crate::Program;
2
3use super::node::{AccessTarget, Args, Expr, Literal, Node, Params, Pattern, StringSegment};
4use std::fmt::Write;
5
6impl Node {
7    /// Converts the AST node back to mq code code.
8    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                // Remove leading space from format_program_block output
134                let start_len = buf.len();
135                format_program_block(program, buf, indent);
136                // format_program_block adds " do", but Block doesn't need leading space
137                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) = &param.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    // Helper function to create a Node from an Expr
404    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    // Complex expression tests
928    #[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}