1use std::fmt;
2
3use crate::token::Token;
4
5#[derive(Debug, Clone, PartialEq)]
6pub struct Program<'a> {
7 begin_blocks: Vec<Item<'a>>,
8 items: Vec<Item<'a>>,
9}
10
11impl<'a> Program<'a> {
12 pub fn new() -> Self {
13 Program {
14 begin_blocks: vec![],
15 items: vec![],
16 }
17 }
18
19 pub fn len(&self) -> usize {
20 self.items.len() + self.begin_blocks.len()
21 }
22
23 pub fn is_empty(&self) -> bool {
24 self.items.is_empty()
25 }
26
27 pub fn add_begin_block(&mut self, item: Item<'a>) {
28 self.begin_blocks.push(item);
29 }
30
31 pub fn add_item(&mut self, item: Item<'a>) {
32 self.items.push(item);
33 }
34
35 pub fn iter(&self) -> std::slice::Iter<'_, Item<'a>> {
36 self.items.iter()
37 }
38}
39
40impl<'a> Default for Program<'a> {
41 fn default() -> Self {
42 Self::new()
43 }
44}
45
46impl<'a> fmt::Display for Program<'a> {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 for item in &self.begin_blocks {
49 write!(f, "{item}")?;
50 }
51
52 if !self.begin_blocks.is_empty() && !self.items.is_empty() {
54 write!(f, " ")?;
55 }
56
57 for item in &self.items {
58 write!(f, "{item}")?;
59 }
60 Ok(())
61 }
62}
63
64#[derive(Debug, Clone, PartialEq)]
65pub enum Statement<'a> {
66 Print(Vec<Expression<'a>>),
67}
68
69#[derive(Debug, Clone, PartialEq)]
70pub struct Action<'a> {
71 pub statements: Vec<Statement<'a>>,
72}
73
74#[derive(Debug, Clone, PartialEq)]
75pub enum Item<'a> {
76 Begin(Action<'a>),
77 Action(Action<'a>),
78 PatternAction {
79 pattern: Option<Expression<'a>>,
80 action: Option<Action<'a>>,
81 },
82}
83
84impl<'a> fmt::Display for Item<'a> {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 match self {
87 Item::Begin(action) => write!(f, "BEGIN {}", action),
88 Item::Action(action) => write!(f, "{}", action),
89 Item::PatternAction { pattern, action } => match (pattern, action) {
90 (Some(expr), Some(action)) => write!(f, "{} {}", expr, action),
91 (Some(expr), None) => write!(f, "{}", expr),
92 (None, Some(action)) => write!(f, "{}", action),
93 (None, None) => write!(f, ""),
94 },
95 }
96 }
97}
98
99impl<'a> fmt::Display for Action<'a> {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 if self.statements.is_empty() {
102 write!(f, "{{}}")
103 } else {
104 write!(
105 f,
106 "{{ {} }}",
107 self.statements
108 .iter()
109 .map(|stmt| stmt.to_string())
110 .collect::<Vec<String>>()
111 .join("; ")
112 )
113 }
114 }
115}
116
117impl<'a> fmt::Display for Statement<'a> {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 match self {
120 Statement::Print(expressions) => {
121 if expressions.is_empty() {
122 write!(f, "print")
123 } else {
124 write!(
125 f,
126 "print {}",
127 expressions
128 .iter()
129 .map(|expr| expr.to_string())
130 .collect::<Vec<String>>()
131 .join(", ")
132 )
133 }
134 }
135 }
136 }
137}
138
139#[derive(Debug, Clone, PartialEq)]
140pub enum Expression<'a> {
141 Number(f64),
142 Field(Box<Expression<'a>>),
143 Infix {
145 left: Box<Expression<'a>>,
146 operator: Token<'a>,
147 right: Box<Expression<'a>>,
148 },
149}
150
151impl<'a> fmt::Display for Expression<'a> {
152 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
153 match self {
154 Expression::Number(n) => write!(f, "{}", n),
155 Expression::Field(expr) => write!(f, "${}", expr),
156 Expression::Infix {
157 left,
158 operator,
159 right,
160 } => {
161 write!(f, "{} {} {}", left, operator.literal, right)
162 }
163 }
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use crate::token::TokenKind;
171
172 #[test]
173 fn test_empty_program_creation() {
174 let program = Program::default();
175
176 assert!(program.is_empty());
177 }
178
179 #[test]
180 fn test_add_block_to_program() {
181 let mut program = Program::new();
182
183 let item = Item::Action(Action {
184 statements: vec![Statement::Print(vec![])],
185 });
186 program.add_begin_block(item);
187
188 assert_eq!(program.begin_blocks.len(), 1);
189 }
190
191 #[test]
192 fn test_add_item_to_program() {
193 let mut program = Program::new();
194
195 let item = Item::Action(Action {
196 statements: vec![Statement::Print(vec![])],
197 });
198 program.add_item(item);
199
200 assert_eq!(program.len(), 1);
201 }
202
203 #[test]
204 fn test_program_creation() {
205 let expected_string = "$3 > 5";
206 let program = Program {
207 begin_blocks: vec![],
208 items: vec![Item::PatternAction {
209 pattern: Some(Expression::Infix {
210 left: Box::new(Expression::Field(Box::new(Expression::Number(3.0)))),
211 operator: Token::new(TokenKind::GreaterThan, ">", 3),
212 right: Box::new(Expression::Number(5.0)),
213 }),
214 action: None,
215 }],
216 };
217
218 assert_eq!(expected_string, program.to_string());
219 }
220
221 #[test]
222 fn test_begin_block_program_creation() {
223 let expected_string = "BEGIN { print }";
224 let program = Program {
225 begin_blocks: vec![Item::Begin(Action {
226 statements: vec![Statement::Print(vec![])],
227 })],
228 items: vec![],
229 };
230
231 assert_eq!(expected_string, program.to_string());
232 }
233
234 #[test]
235 fn test_action_without_pattern_program_creation() {
236 let expected_string = "{ print }";
237 let program = Program {
238 begin_blocks: vec![],
239 items: vec![Item::PatternAction {
240 pattern: None,
241 action: Some(Action {
242 statements: vec![Statement::Print(vec![])],
243 }),
244 }],
245 };
246
247 assert_eq!(expected_string, program.to_string());
248 }
249
250 #[test]
251 fn test_program_with_begin_and_body() {
252 let expected_string = "BEGIN { print } $1 == 42 { print $2 }";
253 let program = Program {
254 begin_blocks: vec![Item::Begin(Action {
255 statements: vec![Statement::Print(vec![])],
256 })],
257 items: vec![Item::PatternAction {
258 pattern: Some(Expression::Infix {
259 left: Box::new(Expression::Field(Box::new(Expression::Number(1.0)))),
260 operator: Token::new(TokenKind::Equal, "==", 7),
261 right: Box::new(Expression::Number(42.0)),
262 }),
263 action: Some(Action {
264 statements: vec![Statement::Print(vec![Expression::Field(Box::new(
265 Expression::Number(2.0),
266 ))])],
267 }),
268 }],
269 };
270
271 assert_eq!(expected_string, program.to_string());
272 }
273}