Skip to main content

rawk_core/
ast.rs

1use std::fmt;
2
3use crate::token::Token;
4
5#[derive(Debug, Clone, PartialEq)]
6pub struct Program<'a> {
7    begin_blocks: Vec<Rule<'a>>,
8    rules: Vec<Rule<'a>>,
9    end_blocks: Vec<Rule<'a>>,
10}
11
12impl<'a> Program<'a> {
13    pub fn new() -> Self {
14        Program {
15            begin_blocks: vec![],
16            rules: vec![],
17            end_blocks: vec![],
18        }
19    }
20
21    pub fn len(&self) -> usize {
22        self.rules.len() + self.begin_blocks.len() + self.end_blocks.len()
23    }
24
25    pub fn is_empty(&self) -> bool {
26        self.rules.is_empty()
27    }
28
29    pub fn add_begin_block(&mut self, rule: Rule<'a>) {
30        self.begin_blocks.push(rule);
31    }
32
33    pub fn add_end_block(&mut self, rule: Rule<'a>) {
34        self.end_blocks.push(rule);
35    }
36
37    pub fn add_rule(&mut self, rule: Rule<'a>) {
38        self.rules.push(rule);
39    }
40
41    pub fn begin_blocks_iter(&self) -> std::slice::Iter<'_, Rule<'a>> {
42        self.begin_blocks.iter()
43    }
44
45    pub fn end_blocks_iter(&self) -> std::slice::Iter<'_, Rule<'a>> {
46        self.end_blocks.iter()
47    }
48
49    pub fn rules_iter(&self) -> std::slice::Iter<'_, Rule<'a>> {
50        self.rules.iter()
51    }
52}
53
54impl<'a> Default for Program<'a> {
55    fn default() -> Self {
56        Self::new()
57    }
58}
59
60impl<'a> fmt::Display for Program<'a> {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        for rule in &self.begin_blocks {
63            write!(f, "{rule}")?;
64        }
65
66        // Add space between begin blocks and rules if both exist
67        if !self.begin_blocks.is_empty() && !self.rules.is_empty() {
68            write!(f, " ")?;
69        }
70
71        for rule in &self.rules {
72            write!(f, "{rule}")?;
73        }
74
75        // Add space between rules and end blocks if both exist
76        if !self.rules.is_empty() && !self.end_blocks.is_empty() {
77            write!(f, " ")?;
78        }
79
80        for rule in &self.end_blocks {
81            write!(f, "{rule}")?;
82        }
83
84        Ok(())
85    }
86}
87
88#[derive(Debug, Clone, PartialEq)]
89pub enum Statement<'a> {
90    Print(Vec<Expression<'a>>),
91    PrintRedirect {
92        expressions: Vec<Expression<'a>>,
93        target: Expression<'a>,
94        append: bool,
95    },
96    PrintPipe {
97        expressions: Vec<Expression<'a>>,
98        target: Expression<'a>,
99    },
100    Printf(Vec<Expression<'a>>),
101    System(Expression<'a>),
102    Gsub {
103        pattern: Expression<'a>,
104        replacement: Expression<'a>,
105    },
106    Assignment {
107        identifier: &'a str,
108        value: Expression<'a>,
109    },
110    SplitAssignment {
111        identifier: &'a str,
112        string: Expression<'a>,
113        array: &'a str,
114    },
115    ArrayAssignment {
116        identifier: &'a str,
117        index: Expression<'a>,
118        value: Expression<'a>,
119    },
120    FieldAssignment {
121        field: Expression<'a>,
122        value: Expression<'a>,
123    },
124    AddAssignment {
125        identifier: &'a str,
126        value: Expression<'a>,
127    },
128    ArrayAddAssignment {
129        identifier: &'a str,
130        index: Expression<'a>,
131        value: Expression<'a>,
132    },
133    PreIncrement {
134        identifier: &'a str,
135    },
136    PreDecrement {
137        identifier: &'a str,
138    },
139    If {
140        condition: Expression<'a>,
141        then_statements: Vec<Statement<'a>>,
142    },
143    IfElse {
144        condition: Expression<'a>,
145        then_statements: Vec<Statement<'a>>,
146        else_statements: Vec<Statement<'a>>,
147    },
148    While {
149        condition: Expression<'a>,
150        statements: Vec<Statement<'a>>,
151    },
152    For {
153        init: Box<Statement<'a>>,
154        condition: Expression<'a>,
155        update: Box<Statement<'a>>,
156        statements: Vec<Statement<'a>>,
157    },
158    ForIn {
159        variable: &'a str,
160        array: &'a str,
161        statements: Vec<Statement<'a>>,
162    },
163    Exit,
164    PostIncrement {
165        identifier: &'a str,
166    },
167    PostDecrement {
168        identifier: &'a str,
169    },
170}
171
172#[derive(Debug, Clone, PartialEq)]
173pub struct Action<'a> {
174    pub statements: Vec<Statement<'a>>,
175}
176
177#[derive(Debug, Clone, PartialEq)]
178pub enum Rule<'a> {
179    Begin(Action<'a>),
180    Action(Action<'a>),
181    PatternAction {
182        pattern: Option<Expression<'a>>,
183        action: Option<Action<'a>>,
184    },
185    End(Action<'a>),
186}
187
188impl<'a> fmt::Display for Rule<'a> {
189    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190        match self {
191            Rule::Begin(action) => write!(f, "BEGIN {}", action),
192            Rule::Action(action) => write!(f, "{}", action),
193            Rule::PatternAction { pattern, action } => match (pattern, action) {
194                (Some(expr), Some(action)) => write!(f, "{} {}", expr, action),
195                (Some(expr), None) => write!(f, "{}", expr),
196                (None, Some(action)) => write!(f, "{}", action),
197                (None, None) => write!(f, ""),
198            },
199            Rule::End(action) => write!(f, "END {}", action),
200        }
201    }
202}
203
204impl<'a> fmt::Display for Action<'a> {
205    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206        if self.statements.is_empty() {
207            write!(f, "{{}}")
208        } else {
209            write!(
210                f,
211                "{{ {} }}",
212                self.statements
213                    .iter()
214                    .map(|stmt| stmt.to_string())
215                    .collect::<Vec<String>>()
216                    .join("; ")
217            )
218        }
219    }
220}
221
222impl<'a> fmt::Display for Statement<'a> {
223    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224        match self {
225            Statement::Print(expressions) => {
226                if expressions.is_empty() {
227                    write!(f, "print")
228                } else {
229                    write!(
230                        f,
231                        "print {}",
232                        expressions
233                            .iter()
234                            .filter(|expr| *expr != &Expression::String(" "))
235                            .map(|expr| expr.to_string())
236                            .collect::<Vec<String>>()
237                            .join(", ")
238                    )
239                }
240            }
241            Statement::PrintRedirect {
242                expressions,
243                target,
244                append,
245            } => {
246                let operator = if *append { ">>" } else { ">" };
247                if expressions.is_empty() {
248                    write!(f, "print {operator} {target}")
249                } else {
250                    write!(
251                        f,
252                        "print {} {operator} {target}",
253                        expressions
254                            .iter()
255                            .map(|expr| expr.to_string())
256                            .collect::<Vec<String>>()
257                            .join(", ")
258                    )
259                }
260            }
261            Statement::PrintPipe {
262                expressions,
263                target,
264            } => {
265                if expressions.is_empty() {
266                    write!(f, "print | {target}")
267                } else {
268                    write!(
269                        f,
270                        "print {} | {target}",
271                        expressions
272                            .iter()
273                            .map(|expr| expr.to_string())
274                            .collect::<Vec<String>>()
275                            .join(", ")
276                    )
277                }
278            }
279            Statement::Printf(expressions) => {
280                if expressions.is_empty() {
281                    write!(f, "printf")
282                } else {
283                    write!(
284                        f,
285                        "printf {}",
286                        expressions
287                            .iter()
288                            .map(|expr| expr.to_string())
289                            .collect::<Vec<String>>()
290                            .join(", ")
291                    )
292                }
293            }
294            Statement::System(command) => write!(f, "system({command})"),
295            Statement::Gsub {
296                pattern,
297                replacement,
298            } => write!(f, "gsub({}, {})", pattern, replacement),
299            Statement::Assignment { identifier, value } => write!(f, "{identifier} = {value}"),
300            Statement::SplitAssignment {
301                identifier,
302                string,
303                array,
304            } => write!(f, "{identifier} = split({string}, {array})"),
305            Statement::ArrayAssignment {
306                identifier,
307                index,
308                value,
309            } => write!(f, "{identifier}[{index}] = {value}"),
310            Statement::FieldAssignment { field, value } => write!(f, "${field} = {value}"),
311            Statement::AddAssignment { identifier, value } => {
312                write!(f, "{identifier} += {value}")
313            }
314            Statement::ArrayAddAssignment {
315                identifier,
316                index,
317                value,
318            } => write!(f, "{identifier}[{index}] += {value}"),
319            Statement::PreIncrement { identifier } => write!(f, "++{identifier}"),
320            Statement::PreDecrement { identifier } => write!(f, "--{identifier}"),
321            Statement::If {
322                condition,
323                then_statements,
324            } => {
325                let rendered = then_statements
326                    .iter()
327                    .map(|stmt| stmt.to_string())
328                    .collect::<Vec<String>>()
329                    .join("; ");
330                write!(f, "if ({condition}) {{ {rendered} }}")
331            }
332            Statement::IfElse {
333                condition,
334                then_statements,
335                else_statements,
336            } => {
337                let then_rendered = then_statements
338                    .iter()
339                    .map(|stmt| stmt.to_string())
340                    .collect::<Vec<String>>()
341                    .join("; ");
342                let else_rendered = else_statements
343                    .iter()
344                    .map(|stmt| stmt.to_string())
345                    .collect::<Vec<String>>()
346                    .join("; ");
347                write!(
348                    f,
349                    "if ({condition}) {{ {then_rendered} }} else {{ {else_rendered} }}"
350                )
351            }
352            Statement::While {
353                condition,
354                statements,
355            } => {
356                let rendered = statements
357                    .iter()
358                    .map(|stmt| stmt.to_string())
359                    .collect::<Vec<String>>()
360                    .join("; ");
361                write!(f, "while ({condition}) {{ {rendered} }}")
362            }
363            Statement::For {
364                init,
365                condition,
366                update,
367                statements,
368            } => {
369                let rendered = statements
370                    .iter()
371                    .map(|stmt| stmt.to_string())
372                    .collect::<Vec<String>>()
373                    .join("; ");
374                write!(f, "for ({init}; {condition}; {update}) {{ {rendered} }}")
375            }
376            Statement::ForIn {
377                variable,
378                array,
379                statements,
380            } => {
381                let rendered = statements
382                    .iter()
383                    .map(|stmt| stmt.to_string())
384                    .collect::<Vec<String>>()
385                    .join("; ");
386                write!(f, "for ({variable} in {array}) {{ {rendered} }}")
387            }
388            Statement::Exit => write!(f, "exit"),
389            Statement::PostIncrement { identifier } => write!(f, "{identifier}++"),
390            Statement::PostDecrement { identifier } => write!(f, "{identifier}--"),
391        }
392    }
393}
394
395#[derive(Debug, Clone, PartialEq)]
396pub enum Expression<'a> {
397    Number(f64),
398    String(&'a str),
399    Regex(&'a str),
400    Field(Box<Expression<'a>>),
401    Identifier(&'a str),
402    ArrayAccess {
403        identifier: &'a str,
404        index: Box<Expression<'a>>,
405    },
406    Length(Option<Box<Expression<'a>>>),
407    Substr {
408        string: Box<Expression<'a>>,
409        start: Box<Expression<'a>>,
410        length: Option<Box<Expression<'a>>>,
411    },
412    Rand,
413    FunctionCall {
414        name: &'a str,
415        args: Vec<Expression<'a>>,
416    },
417    Ternary {
418        condition: Box<Expression<'a>>,
419        then_expr: Box<Expression<'a>>,
420        else_expr: Box<Expression<'a>>,
421    },
422    Concatenation {
423        left: Box<Expression<'a>>,
424        right: Box<Expression<'a>>,
425    },
426    // non_unary_expr
427    Infix {
428        left: Box<Expression<'a>>,
429        operator: Token<'a>,
430        right: Box<Expression<'a>>,
431    },
432}
433
434impl<'a> fmt::Display for Expression<'a> {
435    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
436        match self {
437            Expression::Number(n) => write!(f, "{}", n),
438            Expression::String(value) => write!(f, "\"{}\"", value),
439            Expression::Regex(value) => write!(f, "/{}/", value),
440            Expression::Field(expr) => write!(f, "${}", expr),
441            Expression::Identifier(ident) => write!(f, "{}", ident),
442            Expression::ArrayAccess { identifier, index } => write!(f, "{identifier}[{index}]"),
443            Expression::Length(None) => write!(f, "length"),
444            Expression::Length(Some(expr)) => write!(f, "length({})", expr),
445            Expression::Substr {
446                string,
447                start,
448                length,
449            } => {
450                if let Some(length) = length {
451                    write!(f, "substr({}, {}, {})", string, start, length)
452                } else {
453                    write!(f, "substr({}, {})", string, start)
454                }
455            }
456            Expression::Rand => write!(f, "rand()"),
457            Expression::FunctionCall { name, args } => write!(
458                f,
459                "{name}({})",
460                args.iter()
461                    .map(|arg| arg.to_string())
462                    .collect::<Vec<String>>()
463                    .join(", ")
464            ),
465            Expression::Ternary {
466                condition,
467                then_expr,
468                else_expr,
469            } => write!(f, "({condition}) ? {then_expr} : {else_expr}"),
470            Expression::Concatenation { left, right } => write!(f, "{} {}", left, right),
471            Expression::Infix {
472                left,
473                operator,
474                right,
475            } => {
476                write!(f, "{} {} {}", left, operator.literal, right)
477            }
478        }
479    }
480}
481
482#[cfg(test)]
483mod tests {
484    use super::*;
485    use crate::token::TokenKind;
486
487    #[test]
488    fn test_empty_program_creation() {
489        let program = Program::default();
490
491        assert!(program.is_empty());
492    }
493
494    #[test]
495    fn test_add_block_to_program() {
496        let mut program = Program::new();
497
498        let rule = Rule::Action(Action {
499            statements: vec![Statement::Print(vec![])],
500        });
501        program.add_begin_block(rule);
502
503        assert_eq!(program.begin_blocks.len(), 1);
504    }
505
506    #[test]
507    fn test_add_rule_to_program() {
508        let mut program = Program::new();
509
510        let rule = Rule::Action(Action {
511            statements: vec![Statement::Print(vec![])],
512        });
513        program.add_rule(rule);
514
515        assert_eq!(program.len(), 1);
516    }
517
518    #[test]
519    fn test_program_creation() {
520        let expected_string = "$3 > 5";
521        let program = Program {
522            begin_blocks: vec![],
523            rules: vec![Rule::PatternAction {
524                pattern: Some(Expression::Infix {
525                    left: Box::new(Expression::Field(Box::new(Expression::Number(3.0)))),
526                    operator: Token::new(TokenKind::GreaterThan, ">", 3),
527                    right: Box::new(Expression::Number(5.0)),
528                }),
529                action: None,
530            }],
531            end_blocks: vec![],
532        };
533
534        assert_eq!(expected_string, program.to_string());
535    }
536
537    #[test]
538    fn test_begin_block_program_creation() {
539        let expected_string = "BEGIN { print }";
540        let program = Program {
541            begin_blocks: vec![Rule::Begin(Action {
542                statements: vec![Statement::Print(vec![])],
543            })],
544            rules: vec![],
545            end_blocks: vec![],
546        };
547
548        assert!(program.len() == 1);
549        assert_eq!(expected_string, program.to_string());
550    }
551
552    #[test]
553    fn test_end_block_program_creation() {
554        let expected_string = "END { print }";
555        let program = Program {
556            begin_blocks: vec![],
557            rules: vec![],
558            end_blocks: vec![Rule::End(Action {
559                statements: vec![Statement::Print(vec![])],
560            })],
561        };
562
563        assert!(program.len() == 1);
564        assert_eq!(expected_string, program.to_string());
565    }
566
567    #[test]
568    fn test_action_without_pattern_program_creation() {
569        let expected_string = "{ print }";
570        let program = Program {
571            begin_blocks: vec![],
572            rules: vec![Rule::PatternAction {
573                pattern: None,
574                action: Some(Action {
575                    statements: vec![Statement::Print(vec![])],
576                }),
577            }],
578            end_blocks: vec![],
579        };
580
581        assert!(program.len() == 1);
582        assert_eq!(expected_string, program.to_string());
583    }
584
585    #[test]
586    fn test_program_with_begin_body_and_end_blocks() {
587        let expected_string =
588            "BEGIN { print } $1 == 42 { print NF, $2, $3 } END { print \"hello\" }";
589        let program = Program {
590            begin_blocks: vec![Rule::Begin(Action {
591                statements: vec![Statement::Print(vec![])],
592            })],
593            rules: vec![Rule::PatternAction {
594                pattern: Some(Expression::Infix {
595                    left: Box::new(Expression::Field(Box::new(Expression::Number(1.0)))),
596                    operator: Token::new(TokenKind::Equal, "==", 7),
597                    right: Box::new(Expression::Number(42.0)),
598                }),
599                action: Some(Action {
600                    statements: vec![Statement::Print(vec![
601                        Expression::Identifier("NF"),
602                        Expression::String(" "),
603                        Expression::Field(Box::new(Expression::Number(2.0))),
604                        Expression::String(" "),
605                        Expression::Field(Box::new(Expression::Number(3.0))),
606                    ])],
607                }),
608            }],
609            end_blocks: vec![Rule::End(Action {
610                statements: vec![Statement::Print(vec![Expression::String("hello".into())])],
611            })],
612        };
613
614        assert_eq!(expected_string, program.to_string());
615    }
616
617    #[test]
618    fn test_print_regex_expression() {
619        let expr = Expression::Regex("^[a-z]+$");
620
621        assert_eq!("/^[a-z]+$/", expr.to_string());
622    }
623
624    #[test]
625    fn test_assignment_statement_display() {
626        let statement = Statement::Assignment {
627            identifier: "pop",
628            value: Expression::Infix {
629                left: Box::new(Expression::Identifier("pop")),
630                operator: Token::new(TokenKind::Plus, "+", 0),
631                right: Box::new(Expression::Field(Box::new(Expression::Number(3.0)))),
632            },
633        };
634
635        assert_eq!("pop = pop + $3", statement.to_string());
636    }
637
638    #[test]
639    fn test_add_assignment_statement_display() {
640        let statement = Statement::AddAssignment {
641            identifier: "pop",
642            value: Expression::Field(Box::new(Expression::Number(3.0))),
643        };
644
645        assert_eq!("pop += $3", statement.to_string());
646    }
647
648    #[test]
649    fn test_pre_increment_statement_display() {
650        let statement = Statement::PreIncrement { identifier: "n" };
651
652        assert_eq!("++n", statement.to_string());
653    }
654
655    #[test]
656    fn test_pre_decrement_statement_display() {
657        let statement = Statement::PreDecrement { identifier: "n" };
658
659        assert_eq!("--n", statement.to_string());
660    }
661
662    #[test]
663    fn test_action_with_new_statements_display() {
664        let action = Action {
665            statements: vec![
666                Statement::AddAssignment {
667                    identifier: "pop",
668                    value: Expression::Field(Box::new(Expression::Number(3.0))),
669                },
670                Statement::PreIncrement { identifier: "n" },
671            ],
672        };
673
674        assert_eq!("{ pop += $3; ++n }", action.to_string());
675    }
676
677    #[test]
678    fn test_gsub_statement_display() {
679        let statement = Statement::Gsub {
680            pattern: Expression::Regex("USA"),
681            replacement: Expression::String("United States"),
682        };
683
684        assert_eq!(r#"gsub(/USA/, "United States")"#, statement.to_string());
685    }
686
687    #[test]
688    fn test_system_statement_display() {
689        let statement = Statement::System(Expression::Concatenation {
690            left: Box::new(Expression::String("cat ")),
691            right: Box::new(Expression::Field(Box::new(Expression::Number(2.0)))),
692        });
693
694        assert_eq!(r#"system("cat " $2)"#, statement.to_string());
695    }
696
697    #[test]
698    fn test_length_expression_without_argument_display() {
699        let expression = Expression::Length(None);
700
701        assert_eq!("length", expression.to_string());
702    }
703
704    #[test]
705    fn test_length_expression_with_argument_display() {
706        let expression = Expression::Length(Some(Box::new(Expression::Field(Box::new(
707            Expression::Number(1.0),
708        )))));
709
710        assert_eq!("length($1)", expression.to_string());
711    }
712
713    #[test]
714    fn test_print_statement_with_length_expression_display() {
715        let statement = Statement::Print(vec![
716            Expression::Length(None),
717            Expression::String(" "),
718            Expression::Field(Box::new(Expression::Number(0.0))),
719        ]);
720
721        assert_eq!("print length, $0", statement.to_string());
722    }
723
724    #[test]
725    fn test_substr_expression_display() {
726        let expression = Expression::Substr {
727            string: Box::new(Expression::Field(Box::new(Expression::Number(1.0)))),
728            start: Box::new(Expression::Number(1.0)),
729            length: Some(Box::new(Expression::Number(3.0))),
730        };
731
732        assert_eq!("substr($1, 1, 3)", expression.to_string());
733    }
734
735    #[test]
736    fn test_field_assignment_statement_display() {
737        let statement = Statement::FieldAssignment {
738            field: Expression::Number(1.0),
739            value: Expression::Substr {
740                string: Box::new(Expression::Field(Box::new(Expression::Number(1.0)))),
741                start: Box::new(Expression::Number(1.0)),
742                length: Some(Box::new(Expression::Number(3.0))),
743            },
744        };
745
746        assert_eq!("$1 = substr($1, 1, 3)", statement.to_string());
747    }
748
749    #[test]
750    fn test_concatenation_expression_display() {
751        let expression = Expression::Concatenation {
752            left: Box::new(Expression::Identifier("s")),
753            right: Box::new(Expression::Substr {
754                string: Box::new(Expression::Field(Box::new(Expression::Number(1.0)))),
755                start: Box::new(Expression::Number(1.0)),
756                length: Some(Box::new(Expression::Number(3.0))),
757            }),
758        };
759
760        assert_eq!("s substr($1, 1, 3)", expression.to_string());
761    }
762
763    #[test]
764    fn test_if_statement_display() {
765        let statement = Statement::If {
766            condition: Expression::Infix {
767                left: Box::new(Expression::Identifier("maxpop")),
768                operator: Token::new(TokenKind::LessThan, "<", 0),
769                right: Box::new(Expression::Field(Box::new(Expression::Number(3.0)))),
770            },
771            then_statements: vec![Statement::Assignment {
772                identifier: "maxpop",
773                value: Expression::Field(Box::new(Expression::Number(3.0))),
774            }],
775        };
776
777        assert_eq!("if (maxpop < $3) { maxpop = $3 }", statement.to_string());
778    }
779
780    #[test]
781    fn test_while_statement_display() {
782        let statement = Statement::While {
783            condition: Expression::Infix {
784                left: Box::new(Expression::Identifier("i")),
785                operator: Token::new(TokenKind::LessThanOrEqual, "<=", 0),
786                right: Box::new(Expression::Identifier("NF")),
787            },
788            statements: vec![
789                Statement::Print(vec![Expression::Field(Box::new(Expression::Identifier("i")))]),
790                Statement::PostIncrement { identifier: "i" },
791            ],
792        };
793
794        assert_eq!("while (i <= NF) { print $i; i++ }", statement.to_string());
795    }
796
797    #[test]
798    fn test_for_statement_display() {
799        let statement = Statement::For {
800            init: Box::new(Statement::Assignment {
801                identifier: "i",
802                value: Expression::Number(1.0),
803            }),
804            condition: Expression::Infix {
805                left: Box::new(Expression::Identifier("i")),
806                operator: Token::new(TokenKind::LessThanOrEqual, "<=", 0),
807                right: Box::new(Expression::Identifier("NF")),
808            },
809            update: Box::new(Statement::PostIncrement { identifier: "i" }),
810            statements: vec![Statement::Print(vec![Expression::Field(Box::new(
811                Expression::Identifier("i"),
812            ))])],
813        };
814
815        assert_eq!("for (i = 1; i <= NF; i++) { print $i }", statement.to_string());
816    }
817
818    #[test]
819    fn test_post_decrement_statement_display() {
820        let statement = Statement::PostDecrement { identifier: "n" };
821
822        assert_eq!("n--", statement.to_string());
823    }
824
825    #[test]
826    fn test_rand_expression_display() {
827        let expression = Expression::Rand;
828
829        assert_eq!("rand()", expression.to_string());
830    }
831
832    #[test]
833    fn test_exit_statement_display() {
834        let statement = Statement::Exit;
835
836        assert_eq!("exit", statement.to_string());
837    }
838
839    #[test]
840    fn test_print_redirect_statement_display() {
841        let statement = Statement::PrintRedirect {
842            expressions: vec![],
843            target: Expression::String("tempbig"),
844            append: false,
845        };
846
847        assert_eq!(r#"print > "tempbig""#, statement.to_string());
848    }
849
850    #[test]
851    fn test_print_pipe_statement_display() {
852        let statement = Statement::PrintPipe {
853            expressions: vec![Expression::Identifier("c"), Expression::String(":"), Expression::Number(1.0)],
854            target: Expression::String("sort"),
855        };
856
857        assert_eq!(r#"print c, ":", 1 | "sort""#, statement.to_string());
858    }
859
860    #[test]
861    fn test_for_in_statement_display() {
862        let statement = Statement::ForIn {
863            variable: "name",
864            array: "area",
865            statements: vec![Statement::Print(vec![Expression::Concatenation {
866                left: Box::new(Expression::Identifier("name")),
867                right: Box::new(Expression::ArrayAccess {
868                    identifier: "area",
869                    index: Box::new(Expression::Identifier("name")),
870                }),
871            }])],
872        };
873
874        assert_eq!("for (name in area) { print name area[name] }", statement.to_string());
875    }
876
877    #[test]
878    fn test_array_access_expression_display() {
879        let expression = Expression::ArrayAccess {
880            identifier: "pop",
881            index: Box::new(Expression::String("Asia")),
882        };
883
884        assert_eq!(r#"pop["Asia"]"#, expression.to_string());
885    }
886
887    #[test]
888    fn test_array_add_assignment_display() {
889        let statement = Statement::ArrayAddAssignment {
890            identifier: "pop",
891            index: Expression::String("Asia"),
892            value: Expression::Field(Box::new(Expression::Number(3.0))),
893        };
894
895        assert_eq!(r#"pop["Asia"] += $3"#, statement.to_string());
896    }
897}