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 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 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 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}