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    String(&'a str),
512    Regex(&'a str),
513    Field(Box<Expression<'a>>),
514    Identifier(&'a str),
515    ArrayAccess {
516        identifier: &'a str,
517        index: Box<Expression<'a>>,
518    },
519    Length(Option<Box<Expression<'a>>>),
520    Substr {
521        string: Box<Expression<'a>>,
522        start: Box<Expression<'a>>,
523        length: Option<Box<Expression<'a>>>,
524    },
525    Rand,
526    FunctionCall {
527        name: &'a str,
528        args: Vec<Expression<'a>>,
529    },
530    Not(Box<Expression<'a>>),
531    PreIncrement(Box<Expression<'a>>),
532    PreDecrement(Box<Expression<'a>>),
533    PostIncrement(Box<Expression<'a>>),
534    PostDecrement(Box<Expression<'a>>),
535    Ternary {
536        condition: Box<Expression<'a>>,
537        then_expr: Box<Expression<'a>>,
538        else_expr: Box<Expression<'a>>,
539    },
540    Concatenation {
541        left: Box<Expression<'a>>,
542        right: Box<Expression<'a>>,
543    },
544    // non_unary_expr
545    Infix {
546        left: Box<Expression<'a>>,
547        operator: Token<'a>,
548        right: Box<Expression<'a>>,
549    },
550}
551
552impl<'a> fmt::Display for Expression<'a> {
553    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
554        match self {
555            Expression::Number(n) => write!(f, "{}", n),
556            Expression::String(value) => write!(f, "\"{}\"", value),
557            Expression::Regex(value) => write!(f, "/{}/", value),
558            Expression::Field(expr) => write!(f, "${}", expr),
559            Expression::Identifier(ident) => write!(f, "{}", ident),
560            Expression::ArrayAccess { identifier, index } => write!(f, "{identifier}[{index}]"),
561            Expression::Length(None) => write!(f, "length"),
562            Expression::Length(Some(expr)) => write!(f, "length({})", expr),
563            Expression::Substr {
564                string,
565                start,
566                length,
567            } => {
568                if let Some(length) = length {
569                    write!(f, "substr({}, {}, {})", string, start, length)
570                } else {
571                    write!(f, "substr({}, {})", string, start)
572                }
573            }
574            Expression::Rand => write!(f, "rand()"),
575            Expression::FunctionCall { name, args } => write!(
576                f,
577                "{name}({})",
578                args.iter()
579                    .map(|arg| arg.to_string())
580                    .collect::<Vec<String>>()
581                    .join(", ")
582            ),
583            Expression::Not(expr) => write!(f, "!{expr}"),
584            Expression::PreIncrement(expr) => write!(f, "++{expr}"),
585            Expression::PreDecrement(expr) => write!(f, "--{expr}"),
586            Expression::PostIncrement(expr) => write!(f, "{expr}++"),
587            Expression::PostDecrement(expr) => write!(f, "{expr}--"),
588            Expression::Ternary {
589                condition,
590                then_expr,
591                else_expr,
592            } => write!(f, "({condition}) ? {then_expr} : {else_expr}"),
593            Expression::Concatenation { left, right } => write!(f, "{} {}", left, right),
594            Expression::Infix {
595                left,
596                operator,
597                right,
598            } => {
599                if operator.kind == crate::token::TokenKind::Comma {
600                    write!(f, "{left}, {right}")
601                } else {
602                    write!(f, "{} {} {}", left, operator.literal, right)
603                }
604            }
605        }
606    }
607}
608
609#[cfg(test)]
610mod tests {
611    use super::*;
612    use crate::token::TokenKind;
613
614    #[test]
615    fn test_empty_program_creation() {
616        let program = Program::default();
617
618        assert!(program.is_empty());
619    }
620
621    #[test]
622    fn test_add_block_to_program() {
623        let mut program = Program::new();
624
625        let action = Action {
626            statements: vec![Statement::Print(vec![])],
627        };
628        program.add_begin_block(action);
629
630        assert_eq!(program.begin_blocks.len(), 1);
631    }
632
633    #[test]
634    fn test_add_rule_to_program() {
635        let mut program = Program::new();
636
637        let rule = Rule::Action(Action {
638            statements: vec![Statement::Print(vec![])],
639        });
640        program.add_rule(rule);
641
642        assert_eq!(program.len(), 1);
643    }
644
645    #[test]
646    fn test_program_creation() {
647        let expected_string = "$3 > 5";
648        let program = Program {
649            begin_blocks: vec![],
650            rules: vec![Rule::PatternAction {
651                pattern: Some(Expression::Infix {
652                    left: Box::new(Expression::Field(Box::new(Expression::Number(3.0)))),
653                    operator: Token::new(TokenKind::GreaterThan, ">", 3),
654                    right: Box::new(Expression::Number(5.0)),
655                }),
656                action: None,
657            }],
658            end_blocks: vec![],
659            function_definitions: vec![],
660        };
661
662        assert_eq!(expected_string, program.to_string());
663    }
664
665    #[test]
666    fn test_begin_block_program_creation() {
667        let expected_string = "BEGIN { print }";
668        let program = Program {
669            begin_blocks: vec![Action {
670                statements: vec![Statement::Print(vec![])],
671            }],
672            rules: vec![],
673            end_blocks: vec![],
674            function_definitions: vec![],
675        };
676
677        assert!(program.len() == 1);
678        assert_eq!(expected_string, program.to_string());
679    }
680
681    #[test]
682    fn test_end_block_program_creation() {
683        let expected_string = "END { print }";
684        let program = Program {
685            begin_blocks: vec![],
686            rules: vec![],
687            end_blocks: vec![Action {
688                statements: vec![Statement::Print(vec![])],
689            }],
690            function_definitions: vec![],
691        };
692
693        assert!(program.len() == 1);
694        assert_eq!(expected_string, program.to_string());
695    }
696
697    #[test]
698    fn test_begin_rule_display() {
699        let rule = Rule::Begin(Action {
700            statements: vec![Statement::Print(vec![])],
701        });
702
703        assert_eq!("BEGIN { print }", rule.to_string());
704    }
705
706    #[test]
707    fn test_end_rule_display() {
708        let rule = Rule::End(Action {
709            statements: vec![Statement::Print(vec![])],
710        });
711
712        assert_eq!("END { print }", rule.to_string());
713    }
714
715    #[test]
716    fn test_action_without_pattern_program_creation() {
717        let expected_string = "{ print }";
718        let program = Program {
719            begin_blocks: vec![],
720            rules: vec![Rule::PatternAction {
721                pattern: None,
722                action: Some(Action {
723                    statements: vec![Statement::Print(vec![])],
724                }),
725            }],
726            end_blocks: vec![],
727            function_definitions: vec![],
728        };
729
730        assert!(program.len() == 1);
731        assert_eq!(expected_string, program.to_string());
732    }
733
734    #[test]
735    fn test_program_with_begin_body_and_end_blocks() {
736        let expected_string =
737            "BEGIN { print } $1 == 42 { print NF, $2, $3 } END { print \"hello\" }";
738        let program = Program {
739            begin_blocks: vec![Action {
740                statements: vec![Statement::Print(vec![])],
741            }],
742            rules: vec![Rule::PatternAction {
743                pattern: Some(Expression::Infix {
744                    left: Box::new(Expression::Field(Box::new(Expression::Number(1.0)))),
745                    operator: Token::new(TokenKind::Equal, "==", 7),
746                    right: Box::new(Expression::Number(42.0)),
747                }),
748                action: Some(Action {
749                    statements: vec![Statement::Print(vec![
750                        Expression::Identifier("NF"),
751                        Expression::String(" "),
752                        Expression::Field(Box::new(Expression::Number(2.0))),
753                        Expression::String(" "),
754                        Expression::Field(Box::new(Expression::Number(3.0))),
755                    ])],
756                }),
757            }],
758            end_blocks: vec![Action {
759                statements: vec![Statement::Print(vec![Expression::String("hello".into())])],
760            }],
761            function_definitions: vec![],
762        };
763
764        assert_eq!(expected_string, program.to_string());
765    }
766
767    #[test]
768    fn test_print_regex_expression() {
769        let expr = Expression::Regex("^[a-z]+$");
770
771        assert_eq!("/^[a-z]+$/", expr.to_string());
772    }
773
774    #[test]
775    fn test_assignment_statement_display() {
776        let statement = Statement::Assignment {
777            identifier: "pop",
778            value: Expression::Infix {
779                left: Box::new(Expression::Identifier("pop")),
780                operator: Token::new(TokenKind::Plus, "+", 0),
781                right: Box::new(Expression::Field(Box::new(Expression::Number(3.0)))),
782            },
783        };
784
785        assert_eq!("pop = pop + $3", statement.to_string());
786    }
787
788    #[test]
789    fn test_add_assignment_statement_display() {
790        let statement = Statement::AddAssignment {
791            identifier: "pop",
792            value: Expression::Field(Box::new(Expression::Number(3.0))),
793        };
794
795        assert_eq!("pop += $3", statement.to_string());
796    }
797
798    #[test]
799    fn test_pre_increment_statement_display() {
800        let statement = Statement::PreIncrement { identifier: "n" };
801
802        assert_eq!("++n", statement.to_string());
803    }
804
805    #[test]
806    fn test_pre_decrement_statement_display() {
807        let statement = Statement::PreDecrement { identifier: "n" };
808
809        assert_eq!("--n", statement.to_string());
810    }
811
812    #[test]
813    fn test_action_with_new_statements_display() {
814        let action = Action {
815            statements: vec![
816                Statement::AddAssignment {
817                    identifier: "pop",
818                    value: Expression::Field(Box::new(Expression::Number(3.0))),
819                },
820                Statement::PreIncrement { identifier: "n" },
821            ],
822        };
823
824        assert_eq!("{ pop += $3; ++n }", action.to_string());
825    }
826
827    #[test]
828    fn test_gsub_statement_display() {
829        let statement = Statement::Gsub {
830            pattern: Expression::Regex("USA"),
831            replacement: Expression::String("United States"),
832            target: None,
833        };
834
835        assert_eq!(r#"gsub(/USA/, "United States")"#, statement.to_string());
836    }
837
838    #[test]
839    fn test_system_statement_display() {
840        let statement = Statement::System(Expression::Concatenation {
841            left: Box::new(Expression::String("cat ")),
842            right: Box::new(Expression::Field(Box::new(Expression::Number(2.0)))),
843        });
844
845        assert_eq!(r#"system("cat " $2)"#, statement.to_string());
846    }
847
848    #[test]
849    fn test_length_expression_without_argument_display() {
850        let expression = Expression::Length(None);
851
852        assert_eq!("length", expression.to_string());
853    }
854
855    #[test]
856    fn test_length_expression_with_argument_display() {
857        let expression = Expression::Length(Some(Box::new(Expression::Field(Box::new(
858            Expression::Number(1.0),
859        )))));
860
861        assert_eq!("length($1)", expression.to_string());
862    }
863
864    #[test]
865    fn test_print_statement_with_length_expression_display() {
866        let statement = Statement::Print(vec![
867            Expression::Length(None),
868            Expression::String(" "),
869            Expression::Field(Box::new(Expression::Number(0.0))),
870        ]);
871
872        assert_eq!("print length, $0", statement.to_string());
873    }
874
875    #[test]
876    fn test_substr_expression_display() {
877        let expression = Expression::Substr {
878            string: Box::new(Expression::Field(Box::new(Expression::Number(1.0)))),
879            start: Box::new(Expression::Number(1.0)),
880            length: Some(Box::new(Expression::Number(3.0))),
881        };
882
883        assert_eq!("substr($1, 1, 3)", expression.to_string());
884    }
885
886    #[test]
887    fn test_field_assignment_statement_display() {
888        let statement = Statement::FieldAssignment {
889            field: Expression::Number(1.0),
890            value: Expression::Substr {
891                string: Box::new(Expression::Field(Box::new(Expression::Number(1.0)))),
892                start: Box::new(Expression::Number(1.0)),
893                length: Some(Box::new(Expression::Number(3.0))),
894            },
895        };
896
897        assert_eq!("$1 = substr($1, 1, 3)", statement.to_string());
898    }
899
900    #[test]
901    fn test_concatenation_expression_display() {
902        let expression = Expression::Concatenation {
903            left: Box::new(Expression::Identifier("s")),
904            right: Box::new(Expression::Substr {
905                string: Box::new(Expression::Field(Box::new(Expression::Number(1.0)))),
906                start: Box::new(Expression::Number(1.0)),
907                length: Some(Box::new(Expression::Number(3.0))),
908            }),
909        };
910
911        assert_eq!("s substr($1, 1, 3)", expression.to_string());
912    }
913
914    #[test]
915    fn test_expression_display_for_not_increment_decrement_and_ternary() {
916        let statement = Statement::Print(vec![
917            Expression::Not(Box::new(Expression::Identifier("x"))),
918            Expression::PreIncrement(Box::new(Expression::Identifier("x"))),
919            Expression::PreDecrement(Box::new(Expression::Identifier("x"))),
920            Expression::PostIncrement(Box::new(Expression::Identifier("x"))),
921            Expression::PostDecrement(Box::new(Expression::Identifier("x"))),
922            Expression::Ternary {
923                condition: Box::new(Expression::Identifier("x")),
924                then_expr: Box::new(Expression::Identifier("y")),
925                else_expr: Box::new(Expression::Identifier("z")),
926            },
927        ]);
928
929        assert_eq!("print !x, ++x, --x, x++, x--, (x) ? y : z", statement.to_string());
930    }
931
932    #[test]
933    fn test_if_statement_display() {
934        let statement = Statement::If {
935            condition: Expression::Infix {
936                left: Box::new(Expression::Identifier("maxpop")),
937                operator: Token::new(TokenKind::LessThan, "<", 0),
938                right: Box::new(Expression::Field(Box::new(Expression::Number(3.0)))),
939            },
940            then_statements: vec![Statement::Assignment {
941                identifier: "maxpop",
942                value: Expression::Field(Box::new(Expression::Number(3.0))),
943            }],
944        };
945
946        assert_eq!("if (maxpop < $3) { maxpop = $3 }", statement.to_string());
947    }
948
949    #[test]
950    fn test_while_statement_display() {
951        let statement = Statement::While {
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!("while (i <= NF) { print $i; i++ }", statement.to_string());
966    }
967
968    #[test]
969    fn test_do_while_statement_display() {
970        let statement = Statement::DoWhile {
971            condition: Expression::Infix {
972                left: Box::new(Expression::Identifier("i")),
973                operator: Token::new(TokenKind::LessThanOrEqual, "<=", 0),
974                right: Box::new(Expression::Identifier("NF")),
975            },
976            statements: vec![
977                Statement::Print(vec![Expression::Field(Box::new(Expression::Identifier(
978                    "i",
979                )))]),
980                Statement::PostIncrement { identifier: "i" },
981            ],
982        };
983
984        assert_eq!(
985            "do { print $i; i++ } while (i <= NF)",
986            statement.to_string()
987        );
988    }
989
990    #[test]
991    fn test_for_statement_display() {
992        let statement = Statement::For {
993            init: Box::new(Statement::Assignment {
994                identifier: "i",
995                value: Expression::Number(1.0),
996            }),
997            condition: Expression::Infix {
998                left: Box::new(Expression::Identifier("i")),
999                operator: Token::new(TokenKind::LessThanOrEqual, "<=", 0),
1000                right: Box::new(Expression::Identifier("NF")),
1001            },
1002            update: Box::new(Statement::PostIncrement { identifier: "i" }),
1003            statements: vec![Statement::Print(vec![Expression::Field(Box::new(
1004                Expression::Identifier("i"),
1005            ))])],
1006        };
1007
1008        assert_eq!(
1009            "for (i = 1; i <= NF; i++) { print $i }",
1010            statement.to_string()
1011        );
1012    }
1013
1014    #[test]
1015    fn test_post_decrement_statement_display() {
1016        let statement = Statement::PostDecrement { identifier: "n" };
1017
1018        assert_eq!("n--", statement.to_string());
1019    }
1020
1021    #[test]
1022    fn test_rand_expression_display() {
1023        let expression = Expression::Rand;
1024
1025        assert_eq!("rand()", expression.to_string());
1026    }
1027
1028    #[test]
1029    fn test_exit_statement_display() {
1030        let statement = Statement::Exit(None);
1031
1032        assert_eq!("exit", statement.to_string());
1033    }
1034
1035    #[test]
1036    fn test_exit_statement_with_status_display() {
1037        let statement = Statement::Exit(Some(Expression::Identifier("NR")));
1038
1039        assert_eq!("exit NR", statement.to_string());
1040    }
1041
1042    #[test]
1043    fn test_print_redirect_statement_display() {
1044        let statement = Statement::PrintRedirect {
1045            expressions: vec![],
1046            target: Expression::String("tempbig"),
1047            append: false,
1048        };
1049
1050        assert_eq!(r#"print > "tempbig""#, statement.to_string());
1051    }
1052
1053    #[test]
1054    fn test_print_pipe_statement_display() {
1055        let statement = Statement::PrintPipe {
1056            expressions: vec![
1057                Expression::Identifier("c"),
1058                Expression::String(":"),
1059                Expression::Number(1.0),
1060            ],
1061            target: Expression::String("sort"),
1062        };
1063
1064        assert_eq!(r#"print c, ":", 1 | "sort""#, statement.to_string());
1065    }
1066
1067    #[test]
1068    fn test_for_in_statement_display() {
1069        let statement = Statement::ForIn {
1070            variable: "name",
1071            array: "area",
1072            statements: vec![Statement::Print(vec![Expression::Concatenation {
1073                left: Box::new(Expression::Identifier("name")),
1074                right: Box::new(Expression::ArrayAccess {
1075                    identifier: "area",
1076                    index: Box::new(Expression::Identifier("name")),
1077                }),
1078            }])],
1079        };
1080
1081        assert_eq!(
1082            "for (name in area) { print name area[name] }",
1083            statement.to_string()
1084        );
1085    }
1086
1087    #[test]
1088    fn test_array_access_expression_display() {
1089        let expression = Expression::ArrayAccess {
1090            identifier: "pop",
1091            index: Box::new(Expression::String("Asia")),
1092        };
1093
1094        assert_eq!(r#"pop["Asia"]"#, expression.to_string());
1095    }
1096
1097    #[test]
1098    fn test_array_add_assignment_display() {
1099        let statement = Statement::ArrayAddAssignment {
1100            identifier: "pop",
1101            index: Expression::String("Asia"),
1102            value: Expression::Field(Box::new(Expression::Number(3.0))),
1103        };
1104
1105        assert_eq!(r#"pop["Asia"] += $3"#, statement.to_string());
1106    }
1107}