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(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                // Remove leading space from format_program_block output
130                let start_len = buf.len();
131                format_program_block(program, buf, indent);
132                // format_program_block adds " do", but Block doesn't need leading space
133                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) = &param.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    // Helper function to create a Node from an Expr
400    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    // Complex expression tests
904    #[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}