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