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