1use std::fmt;
2
3use crate::token::Token;
4
5#[derive(Debug, Clone, PartialEq)]
6pub struct Program<'a> {
7 begin_blocks: Vec<Rule<'a>>,
8 rules: Vec<Rule<'a>>,
9 end_blocks: Vec<Rule<'a>>,
10}
11
12impl<'a> Program<'a> {
13 pub fn new() -> Self {
14 Program {
15 begin_blocks: vec![],
16 rules: vec![],
17 end_blocks: vec![],
18 }
19 }
20
21 pub fn len(&self) -> usize {
22 self.rules.len() + self.begin_blocks.len() + self.end_blocks.len()
23 }
24
25 pub fn is_empty(&self) -> bool {
26 self.rules.is_empty()
27 }
28
29 pub fn add_begin_block(&mut self, rule: Rule<'a>) {
30 self.begin_blocks.push(rule);
31 }
32
33 pub fn add_end_block(&mut self, rule: Rule<'a>) {
34 self.end_blocks.push(rule);
35 }
36
37 pub fn add_rule(&mut self, rule: Rule<'a>) {
38 self.rules.push(rule);
39 }
40
41 pub fn begin_blocks_iter(&self) -> std::slice::Iter<'_, Rule<'a>> {
42 self.begin_blocks.iter()
43 }
44
45 pub fn end_blocks_iter(&self) -> std::slice::Iter<'_, Rule<'a>> {
46 self.end_blocks.iter()
47 }
48
49 pub fn rules_iter(&self) -> std::slice::Iter<'_, Rule<'a>> {
50 self.rules.iter()
51 }
52}
53
54impl<'a> Default for Program<'a> {
55 fn default() -> Self {
56 Self::new()
57 }
58}
59
60impl<'a> fmt::Display for Program<'a> {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 for rule in &self.begin_blocks {
63 write!(f, "{rule}")?;
64 }
65
66 if !self.begin_blocks.is_empty() && !self.rules.is_empty() {
68 write!(f, " ")?;
69 }
70
71 for rule in &self.rules {
72 write!(f, "{rule}")?;
73 }
74
75 if !self.rules.is_empty() && !self.end_blocks.is_empty() {
77 write!(f, " ")?;
78 }
79
80 for rule in &self.end_blocks {
81 write!(f, "{rule}")?;
82 }
83
84 Ok(())
85 }
86}
87
88#[derive(Debug, Clone, PartialEq)]
89pub enum Statement<'a> {
90 Print(Vec<Expression<'a>>),
91 PrintRedirect {
92 expressions: Vec<Expression<'a>>,
93 target: Expression<'a>,
94 append: bool,
95 },
96 PrintPipe {
97 expressions: Vec<Expression<'a>>,
98 target: Expression<'a>,
99 },
100 Printf(Vec<Expression<'a>>),
101 System(Expression<'a>),
102 Gsub {
103 pattern: Expression<'a>,
104 replacement: Expression<'a>,
105 },
106 Assignment {
107 identifier: &'a str,
108 value: Expression<'a>,
109 },
110 SplitAssignment {
111 identifier: &'a str,
112 string: Expression<'a>,
113 array: &'a str,
114 },
115 ArrayAssignment {
116 identifier: &'a str,
117 index: Expression<'a>,
118 value: Expression<'a>,
119 },
120 FieldAssignment {
121 field: Expression<'a>,
122 value: Expression<'a>,
123 },
124 AddAssignment {
125 identifier: &'a str,
126 value: Expression<'a>,
127 },
128 ArrayAddAssignment {
129 identifier: &'a str,
130 index: Expression<'a>,
131 value: Expression<'a>,
132 },
133 PreIncrement {
134 identifier: &'a str,
135 },
136 PreDecrement {
137 identifier: &'a str,
138 },
139 If {
140 condition: Expression<'a>,
141 then_statements: Vec<Statement<'a>>,
142 },
143 IfElse {
144 condition: Expression<'a>,
145 then_statements: Vec<Statement<'a>>,
146 else_statements: Vec<Statement<'a>>,
147 },
148 While {
149 condition: Expression<'a>,
150 statements: Vec<Statement<'a>>,
151 },
152 For {
153 init: Box<Statement<'a>>,
154 condition: Expression<'a>,
155 update: Box<Statement<'a>>,
156 statements: Vec<Statement<'a>>,
157 },
158 ForIn {
159 variable: &'a str,
160 array: &'a str,
161 statements: Vec<Statement<'a>>,
162 },
163 Exit,
164 PostIncrement {
165 identifier: &'a str,
166 },
167 PostDecrement {
168 identifier: &'a str,
169 },
170}
171
172#[derive(Debug, Clone, PartialEq)]
173pub struct Action<'a> {
174 pub statements: Vec<Statement<'a>>,
175}
176
177#[derive(Debug, Clone, PartialEq)]
178pub enum Rule<'a> {
179 Begin(Action<'a>),
180 Action(Action<'a>),
181 PatternAction {
182 pattern: Option<Expression<'a>>,
183 action: Option<Action<'a>>,
184 },
185 End(Action<'a>),
186}
187
188impl<'a> fmt::Display for Rule<'a> {
189 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190 match self {
191 Rule::Begin(action) => write!(f, "BEGIN {}", action),
192 Rule::Action(action) => write!(f, "{}", action),
193 Rule::PatternAction { pattern, action } => match (pattern, action) {
194 (Some(expr), Some(action)) => write!(f, "{} {}", expr, action),
195 (Some(expr), None) => write!(f, "{}", expr),
196 (None, Some(action)) => write!(f, "{}", action),
197 (None, None) => write!(f, ""),
198 },
199 Rule::End(action) => write!(f, "END {}", action),
200 }
201 }
202}
203
204impl<'a> fmt::Display for Action<'a> {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 if self.statements.is_empty() {
207 write!(f, "{{}}")
208 } else {
209 write!(
210 f,
211 "{{ {} }}",
212 self.statements
213 .iter()
214 .map(|stmt| stmt.to_string())
215 .collect::<Vec<String>>()
216 .join("; ")
217 )
218 }
219 }
220}
221
222impl<'a> fmt::Display for Statement<'a> {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 match self {
225 Statement::Print(expressions) => {
226 if expressions.is_empty() {
227 write!(f, "print")
228 } else {
229 write!(
230 f,
231 "print {}",
232 expressions
233 .iter()
234 .filter(|expr| *expr != &Expression::String(" "))
235 .map(|expr| expr.to_string())
236 .collect::<Vec<String>>()
237 .join(", ")
238 )
239 }
240 }
241 Statement::PrintRedirect {
242 expressions,
243 target,
244 append,
245 } => {
246 let operator = if *append { ">>" } else { ">" };
247 if expressions.is_empty() {
248 write!(f, "print {operator} {target}")
249 } else {
250 write!(
251 f,
252 "print {} {operator} {target}",
253 expressions
254 .iter()
255 .map(|expr| expr.to_string())
256 .collect::<Vec<String>>()
257 .join(", ")
258 )
259 }
260 }
261 Statement::PrintPipe {
262 expressions,
263 target,
264 } => {
265 if expressions.is_empty() {
266 write!(f, "print | {target}")
267 } else {
268 write!(
269 f,
270 "print {} | {target}",
271 expressions
272 .iter()
273 .map(|expr| expr.to_string())
274 .collect::<Vec<String>>()
275 .join(", ")
276 )
277 }
278 }
279 Statement::Printf(expressions) => {
280 if expressions.is_empty() {
281 write!(f, "printf")
282 } else {
283 write!(
284 f,
285 "printf {}",
286 expressions
287 .iter()
288 .map(|expr| expr.to_string())
289 .collect::<Vec<String>>()
290 .join(", ")
291 )
292 }
293 }
294 Statement::System(command) => write!(f, "system({command})"),
295 Statement::Gsub {
296 pattern,
297 replacement,
298 } => write!(f, "gsub({}, {})", pattern, replacement),
299 Statement::Assignment { identifier, value } => write!(f, "{identifier} = {value}"),
300 Statement::SplitAssignment {
301 identifier,
302 string,
303 array,
304 } => write!(f, "{identifier} = split({string}, {array})"),
305 Statement::ArrayAssignment {
306 identifier,
307 index,
308 value,
309 } => write!(f, "{identifier}[{index}] = {value}"),
310 Statement::FieldAssignment { field, value } => write!(f, "${field} = {value}"),
311 Statement::AddAssignment { identifier, value } => {
312 write!(f, "{identifier} += {value}")
313 }
314 Statement::ArrayAddAssignment {
315 identifier,
316 index,
317 value,
318 } => write!(f, "{identifier}[{index}] += {value}"),
319 Statement::PreIncrement { identifier } => write!(f, "++{identifier}"),
320 Statement::PreDecrement { identifier } => write!(f, "--{identifier}"),
321 Statement::If {
322 condition,
323 then_statements,
324 } => {
325 let rendered = then_statements
326 .iter()
327 .map(|stmt| stmt.to_string())
328 .collect::<Vec<String>>()
329 .join("; ");
330 write!(f, "if ({condition}) {{ {rendered} }}")
331 }
332 Statement::IfElse {
333 condition,
334 then_statements,
335 else_statements,
336 } => {
337 let then_rendered = then_statements
338 .iter()
339 .map(|stmt| stmt.to_string())
340 .collect::<Vec<String>>()
341 .join("; ");
342 let else_rendered = else_statements
343 .iter()
344 .map(|stmt| stmt.to_string())
345 .collect::<Vec<String>>()
346 .join("; ");
347 write!(
348 f,
349 "if ({condition}) {{ {then_rendered} }} else {{ {else_rendered} }}"
350 )
351 }
352 Statement::While {
353 condition,
354 statements,
355 } => {
356 let rendered = statements
357 .iter()
358 .map(|stmt| stmt.to_string())
359 .collect::<Vec<String>>()
360 .join("; ");
361 write!(f, "while ({condition}) {{ {rendered} }}")
362 }
363 Statement::For {
364 init,
365 condition,
366 update,
367 statements,
368 } => {
369 let rendered = statements
370 .iter()
371 .map(|stmt| stmt.to_string())
372 .collect::<Vec<String>>()
373 .join("; ");
374 write!(f, "for ({init}; {condition}; {update}) {{ {rendered} }}")
375 }
376 Statement::ForIn {
377 variable,
378 array,
379 statements,
380 } => {
381 let rendered = statements
382 .iter()
383 .map(|stmt| stmt.to_string())
384 .collect::<Vec<String>>()
385 .join("; ");
386 write!(f, "for ({variable} in {array}) {{ {rendered} }}")
387 }
388 Statement::Exit => write!(f, "exit"),
389 Statement::PostIncrement { identifier } => write!(f, "{identifier}++"),
390 Statement::PostDecrement { identifier } => write!(f, "{identifier}--"),
391 }
392 }
393}
394
395#[derive(Debug, Clone, PartialEq)]
396pub enum Expression<'a> {
397 Number(f64),
398 String(&'a str),
399 Regex(&'a str),
400 Field(Box<Expression<'a>>),
401 Identifier(&'a str),
402 ArrayAccess {
403 identifier: &'a str,
404 index: Box<Expression<'a>>,
405 },
406 Length(Option<Box<Expression<'a>>>),
407 Substr {
408 string: Box<Expression<'a>>,
409 start: Box<Expression<'a>>,
410 length: Option<Box<Expression<'a>>>,
411 },
412 Rand,
413 FunctionCall {
414 name: &'a str,
415 args: Vec<Expression<'a>>,
416 },
417 Ternary {
418 condition: Box<Expression<'a>>,
419 then_expr: Box<Expression<'a>>,
420 else_expr: Box<Expression<'a>>,
421 },
422 Concatenation {
423 left: Box<Expression<'a>>,
424 right: Box<Expression<'a>>,
425 },
426 Infix {
428 left: Box<Expression<'a>>,
429 operator: Token<'a>,
430 right: Box<Expression<'a>>,
431 },
432}
433
434impl<'a> fmt::Display for Expression<'a> {
435 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
436 match self {
437 Expression::Number(n) => write!(f, "{}", n),
438 Expression::String(value) => write!(f, "\"{}\"", value),
439 Expression::Regex(value) => write!(f, "/{}/", value),
440 Expression::Field(expr) => write!(f, "${}", expr),
441 Expression::Identifier(ident) => write!(f, "{}", ident),
442 Expression::ArrayAccess { identifier, index } => write!(f, "{identifier}[{index}]"),
443 Expression::Length(None) => write!(f, "length"),
444 Expression::Length(Some(expr)) => write!(f, "length({})", expr),
445 Expression::Substr {
446 string,
447 start,
448 length,
449 } => {
450 if let Some(length) = length {
451 write!(f, "substr({}, {}, {})", string, start, length)
452 } else {
453 write!(f, "substr({}, {})", string, start)
454 }
455 }
456 Expression::Rand => write!(f, "rand()"),
457 Expression::FunctionCall { name, args } => write!(
458 f,
459 "{name}({})",
460 args.iter()
461 .map(|arg| arg.to_string())
462 .collect::<Vec<String>>()
463 .join(", ")
464 ),
465 Expression::Ternary {
466 condition,
467 then_expr,
468 else_expr,
469 } => write!(f, "({condition}) ? {then_expr} : {else_expr}"),
470 Expression::Concatenation { left, right } => write!(f, "{} {}", left, right),
471 Expression::Infix {
472 left,
473 operator,
474 right,
475 } => {
476 write!(f, "{} {} {}", left, operator.literal, right)
477 }
478 }
479 }
480}
481
482#[cfg(test)]
483mod tests {
484 use super::*;
485 use crate::token::TokenKind;
486
487 #[test]
488 fn test_empty_program_creation() {
489 let program = Program::default();
490
491 assert!(program.is_empty());
492 }
493
494 #[test]
495 fn test_add_block_to_program() {
496 let mut program = Program::new();
497
498 let rule = Rule::Action(Action {
499 statements: vec![Statement::Print(vec![])],
500 });
501 program.add_begin_block(rule);
502
503 assert_eq!(program.begin_blocks.len(), 1);
504 }
505
506 #[test]
507 fn test_add_rule_to_program() {
508 let mut program = Program::new();
509
510 let rule = Rule::Action(Action {
511 statements: vec![Statement::Print(vec![])],
512 });
513 program.add_rule(rule);
514
515 assert_eq!(program.len(), 1);
516 }
517
518 #[test]
519 fn test_program_creation() {
520 let expected_string = "$3 > 5";
521 let program = Program {
522 begin_blocks: vec![],
523 rules: vec![Rule::PatternAction {
524 pattern: Some(Expression::Infix {
525 left: Box::new(Expression::Field(Box::new(Expression::Number(3.0)))),
526 operator: Token::new(TokenKind::GreaterThan, ">", 3),
527 right: Box::new(Expression::Number(5.0)),
528 }),
529 action: None,
530 }],
531 end_blocks: vec![],
532 };
533
534 assert_eq!(expected_string, program.to_string());
535 }
536
537 #[test]
538 fn test_begin_block_program_creation() {
539 let expected_string = "BEGIN { print }";
540 let program = Program {
541 begin_blocks: vec![Rule::Begin(Action {
542 statements: vec![Statement::Print(vec![])],
543 })],
544 rules: vec![],
545 end_blocks: vec![],
546 };
547
548 assert!(program.len() == 1);
549 assert_eq!(expected_string, program.to_string());
550 }
551
552 #[test]
553 fn test_end_block_program_creation() {
554 let expected_string = "END { print }";
555 let program = Program {
556 begin_blocks: vec![],
557 rules: vec![],
558 end_blocks: vec![Rule::End(Action {
559 statements: vec![Statement::Print(vec![])],
560 })],
561 };
562
563 assert!(program.len() == 1);
564 assert_eq!(expected_string, program.to_string());
565 }
566
567 #[test]
568 fn test_action_without_pattern_program_creation() {
569 let expected_string = "{ print }";
570 let program = Program {
571 begin_blocks: vec![],
572 rules: vec![Rule::PatternAction {
573 pattern: None,
574 action: Some(Action {
575 statements: vec![Statement::Print(vec![])],
576 }),
577 }],
578 end_blocks: vec![],
579 };
580
581 assert!(program.len() == 1);
582 assert_eq!(expected_string, program.to_string());
583 }
584
585 #[test]
586 fn test_program_with_begin_body_and_end_blocks() {
587 let expected_string =
588 "BEGIN { print } $1 == 42 { print NF, $2, $3 } END { print \"hello\" }";
589 let program = Program {
590 begin_blocks: vec![Rule::Begin(Action {
591 statements: vec![Statement::Print(vec![])],
592 })],
593 rules: vec![Rule::PatternAction {
594 pattern: Some(Expression::Infix {
595 left: Box::new(Expression::Field(Box::new(Expression::Number(1.0)))),
596 operator: Token::new(TokenKind::Equal, "==", 7),
597 right: Box::new(Expression::Number(42.0)),
598 }),
599 action: Some(Action {
600 statements: vec![Statement::Print(vec![
601 Expression::Identifier("NF"),
602 Expression::String(" "),
603 Expression::Field(Box::new(Expression::Number(2.0))),
604 Expression::String(" "),
605 Expression::Field(Box::new(Expression::Number(3.0))),
606 ])],
607 }),
608 }],
609 end_blocks: vec![Rule::End(Action {
610 statements: vec![Statement::Print(vec![Expression::String("hello".into())])],
611 })],
612 };
613
614 assert_eq!(expected_string, program.to_string());
615 }
616
617 #[test]
618 fn test_print_regex_expression() {
619 let expr = Expression::Regex("^[a-z]+$");
620
621 assert_eq!("/^[a-z]+$/", expr.to_string());
622 }
623
624 #[test]
625 fn test_assignment_statement_display() {
626 let statement = Statement::Assignment {
627 identifier: "pop",
628 value: Expression::Infix {
629 left: Box::new(Expression::Identifier("pop")),
630 operator: Token::new(TokenKind::Plus, "+", 0),
631 right: Box::new(Expression::Field(Box::new(Expression::Number(3.0)))),
632 },
633 };
634
635 assert_eq!("pop = pop + $3", statement.to_string());
636 }
637
638 #[test]
639 fn test_add_assignment_statement_display() {
640 let statement = Statement::AddAssignment {
641 identifier: "pop",
642 value: Expression::Field(Box::new(Expression::Number(3.0))),
643 };
644
645 assert_eq!("pop += $3", statement.to_string());
646 }
647
648 #[test]
649 fn test_pre_increment_statement_display() {
650 let statement = Statement::PreIncrement { identifier: "n" };
651
652 assert_eq!("++n", statement.to_string());
653 }
654
655 #[test]
656 fn test_pre_decrement_statement_display() {
657 let statement = Statement::PreDecrement { identifier: "n" };
658
659 assert_eq!("--n", statement.to_string());
660 }
661
662 #[test]
663 fn test_action_with_new_statements_display() {
664 let action = Action {
665 statements: vec![
666 Statement::AddAssignment {
667 identifier: "pop",
668 value: Expression::Field(Box::new(Expression::Number(3.0))),
669 },
670 Statement::PreIncrement { identifier: "n" },
671 ],
672 };
673
674 assert_eq!("{ pop += $3; ++n }", action.to_string());
675 }
676
677 #[test]
678 fn test_gsub_statement_display() {
679 let statement = Statement::Gsub {
680 pattern: Expression::Regex("USA"),
681 replacement: Expression::String("United States"),
682 };
683
684 assert_eq!(r#"gsub(/USA/, "United States")"#, statement.to_string());
685 }
686
687 #[test]
688 fn test_system_statement_display() {
689 let statement = Statement::System(Expression::Concatenation {
690 left: Box::new(Expression::String("cat ")),
691 right: Box::new(Expression::Field(Box::new(Expression::Number(2.0)))),
692 });
693
694 assert_eq!(r#"system("cat " $2)"#, statement.to_string());
695 }
696
697 #[test]
698 fn test_length_expression_without_argument_display() {
699 let expression = Expression::Length(None);
700
701 assert_eq!("length", expression.to_string());
702 }
703
704 #[test]
705 fn test_length_expression_with_argument_display() {
706 let expression = Expression::Length(Some(Box::new(Expression::Field(Box::new(
707 Expression::Number(1.0),
708 )))));
709
710 assert_eq!("length($1)", expression.to_string());
711 }
712
713 #[test]
714 fn test_print_statement_with_length_expression_display() {
715 let statement = Statement::Print(vec![
716 Expression::Length(None),
717 Expression::String(" "),
718 Expression::Field(Box::new(Expression::Number(0.0))),
719 ]);
720
721 assert_eq!("print length, $0", statement.to_string());
722 }
723
724 #[test]
725 fn test_substr_expression_display() {
726 let expression = Expression::Substr {
727 string: Box::new(Expression::Field(Box::new(Expression::Number(1.0)))),
728 start: Box::new(Expression::Number(1.0)),
729 length: Some(Box::new(Expression::Number(3.0))),
730 };
731
732 assert_eq!("substr($1, 1, 3)", expression.to_string());
733 }
734
735 #[test]
736 fn test_field_assignment_statement_display() {
737 let statement = Statement::FieldAssignment {
738 field: Expression::Number(1.0),
739 value: Expression::Substr {
740 string: Box::new(Expression::Field(Box::new(Expression::Number(1.0)))),
741 start: Box::new(Expression::Number(1.0)),
742 length: Some(Box::new(Expression::Number(3.0))),
743 },
744 };
745
746 assert_eq!("$1 = substr($1, 1, 3)", statement.to_string());
747 }
748
749 #[test]
750 fn test_concatenation_expression_display() {
751 let expression = Expression::Concatenation {
752 left: Box::new(Expression::Identifier("s")),
753 right: Box::new(Expression::Substr {
754 string: Box::new(Expression::Field(Box::new(Expression::Number(1.0)))),
755 start: Box::new(Expression::Number(1.0)),
756 length: Some(Box::new(Expression::Number(3.0))),
757 }),
758 };
759
760 assert_eq!("s substr($1, 1, 3)", expression.to_string());
761 }
762
763 #[test]
764 fn test_if_statement_display() {
765 let statement = Statement::If {
766 condition: Expression::Infix {
767 left: Box::new(Expression::Identifier("maxpop")),
768 operator: Token::new(TokenKind::LessThan, "<", 0),
769 right: Box::new(Expression::Field(Box::new(Expression::Number(3.0)))),
770 },
771 then_statements: vec![Statement::Assignment {
772 identifier: "maxpop",
773 value: Expression::Field(Box::new(Expression::Number(3.0))),
774 }],
775 };
776
777 assert_eq!("if (maxpop < $3) { maxpop = $3 }", statement.to_string());
778 }
779
780 #[test]
781 fn test_while_statement_display() {
782 let statement = Statement::While {
783 condition: Expression::Infix {
784 left: Box::new(Expression::Identifier("i")),
785 operator: Token::new(TokenKind::LessThanOrEqual, "<=", 0),
786 right: Box::new(Expression::Identifier("NF")),
787 },
788 statements: vec![
789 Statement::Print(vec![Expression::Field(Box::new(Expression::Identifier("i")))]),
790 Statement::PostIncrement { identifier: "i" },
791 ],
792 };
793
794 assert_eq!("while (i <= NF) { print $i; i++ }", statement.to_string());
795 }
796
797 #[test]
798 fn test_for_statement_display() {
799 let statement = Statement::For {
800 init: Box::new(Statement::Assignment {
801 identifier: "i",
802 value: Expression::Number(1.0),
803 }),
804 condition: Expression::Infix {
805 left: Box::new(Expression::Identifier("i")),
806 operator: Token::new(TokenKind::LessThanOrEqual, "<=", 0),
807 right: Box::new(Expression::Identifier("NF")),
808 },
809 update: Box::new(Statement::PostIncrement { identifier: "i" }),
810 statements: vec![Statement::Print(vec![Expression::Field(Box::new(
811 Expression::Identifier("i"),
812 ))])],
813 };
814
815 assert_eq!("for (i = 1; i <= NF; i++) { print $i }", statement.to_string());
816 }
817
818 #[test]
819 fn test_post_decrement_statement_display() {
820 let statement = Statement::PostDecrement { identifier: "n" };
821
822 assert_eq!("n--", statement.to_string());
823 }
824
825 #[test]
826 fn test_rand_expression_display() {
827 let expression = Expression::Rand;
828
829 assert_eq!("rand()", expression.to_string());
830 }
831
832 #[test]
833 fn test_exit_statement_display() {
834 let statement = Statement::Exit;
835
836 assert_eq!("exit", statement.to_string());
837 }
838
839 #[test]
840 fn test_print_redirect_statement_display() {
841 let statement = Statement::PrintRedirect {
842 expressions: vec![],
843 target: Expression::String("tempbig"),
844 append: false,
845 };
846
847 assert_eq!(r#"print > "tempbig""#, statement.to_string());
848 }
849
850 #[test]
851 fn test_print_pipe_statement_display() {
852 let statement = Statement::PrintPipe {
853 expressions: vec![Expression::Identifier("c"), Expression::String(":"), Expression::Number(1.0)],
854 target: Expression::String("sort"),
855 };
856
857 assert_eq!(r#"print c, ":", 1 | "sort""#, statement.to_string());
858 }
859
860 #[test]
861 fn test_for_in_statement_display() {
862 let statement = Statement::ForIn {
863 variable: "name",
864 array: "area",
865 statements: vec![Statement::Print(vec![Expression::Concatenation {
866 left: Box::new(Expression::Identifier("name")),
867 right: Box::new(Expression::ArrayAccess {
868 identifier: "area",
869 index: Box::new(Expression::Identifier("name")),
870 }),
871 }])],
872 };
873
874 assert_eq!("for (name in area) { print name area[name] }", statement.to_string());
875 }
876
877 #[test]
878 fn test_array_access_expression_display() {
879 let expression = Expression::ArrayAccess {
880 identifier: "pop",
881 index: Box::new(Expression::String("Asia")),
882 };
883
884 assert_eq!(r#"pop["Asia"]"#, expression.to_string());
885 }
886
887 #[test]
888 fn test_array_add_assignment_display() {
889 let statement = Statement::ArrayAddAssignment {
890 identifier: "pop",
891 index: Expression::String("Asia"),
892 value: Expression::Field(Box::new(Expression::Number(3.0))),
893 };
894
895 assert_eq!(r#"pop["Asia"] += $3"#, statement.to_string());
896 }
897}