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