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