1use super::core::Parser;
20use super::core::Rec;
21use super::core::Result;
22use super::error::Error;
23use super::error::SyntaxError;
24use super::lex::Keyword::{Do, For, In};
25use super::lex::Operator::{Newline, Semicolon};
26use super::lex::TokenId::{EndOfInput, IoLocation, IoNumber, Operator, Token};
27use crate::source::Location;
28use crate::syntax::CompoundCommand;
29use crate::syntax::List;
30use crate::syntax::Word;
31
32impl Parser<'_, '_> {
33 async fn for_loop_name(&mut self) -> Result<Word> {
35 let name = self.take_token_auto(&[]).await?;
36
37 match name.id {
39 EndOfInput | Operator(Newline) | Operator(Semicolon) => {
40 let cause = SyntaxError::MissingForName.into();
41 let location = name.word.location;
42 return Err(Error { cause, location });
43 }
44 Operator(_) => {
45 let cause = SyntaxError::InvalidForName.into();
46 let location = name.word.location;
47 return Err(Error { cause, location });
48 }
49 Token(_) | IoNumber | IoLocation => (),
50 }
51
52 Ok(name.word)
55 }
56
57 async fn for_loop_values(
65 &mut self,
66 opening_location: Location,
67 ) -> Result<(Option<Vec<Word>>, Location)> {
68 let mut first_line = true;
70 loop {
71 match self.peek_token().await?.id {
72 Operator(Semicolon) if first_line => {
73 self.take_token_raw().await?;
74 return Ok((None, opening_location));
75 }
76 Token(Some(Do)) => {
77 return Ok((None, opening_location));
78 }
79 Operator(Newline) => {
80 assert!(self.newline_and_here_doc_contents().await?);
81 first_line = false;
82 }
83 Token(Some(In)) => {
84 self.take_token_raw().await?;
85 break;
86 }
87 _ => match self.take_token_manual(false).await? {
88 Rec::AliasSubstituted => (),
89 Rec::Parsed(token) => {
90 let cause = SyntaxError::MissingForBody { opening_location }.into();
91 let location = token.word.location;
92 return Err(Error { cause, location });
93 }
94 },
95 }
96 }
97
98 let mut values = Vec::new();
100 loop {
101 let next = self.take_token_auto(&[]).await?;
102 match next.id {
103 Token(_) | IoNumber | IoLocation => {
104 values.push(next.word);
105 }
106 Operator(Semicolon) | Operator(Newline) | EndOfInput => {
107 return Ok((Some(values), opening_location));
108 }
109 Operator(_) => {
110 let cause = SyntaxError::InvalidForValue.into();
111 let location = next.word.location;
112 return Err(Error { cause, location });
113 }
114 }
115 }
116 }
117
118 async fn for_loop_body(&mut self, opening_location: Location) -> Result<List> {
120 loop {
121 while self.newline_and_here_doc_contents().await? {}
122
123 if let Some(body) = self.do_clause().await? {
124 return Ok(body);
125 }
126
127 match self.take_token_manual(false).await? {
128 Rec::AliasSubstituted => (),
129 Rec::Parsed(token) => {
130 let cause = SyntaxError::MissingForBody { opening_location }.into();
131 let location = token.word.location;
132 return Err(Error { cause, location });
133 }
134 }
135 }
136 }
137
138 pub async fn for_loop(&mut self) -> Result<CompoundCommand> {
146 let open = self.take_token_raw().await?;
147 assert_eq!(open.id, Token(Some(For)));
148 let opening_location = open.word.location;
149
150 let name = self.for_loop_name().await?;
151 let (values, opening_location) = self.for_loop_values(opening_location).await?;
152 let body = self.for_loop_body(opening_location).await?;
153 Ok(CompoundCommand::For { name, values, body })
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::super::error::ErrorCause;
160 use super::super::lex::Lexer;
161 use super::*;
162 use crate::alias::{AliasSet, HashEntry};
163 use crate::source::Source;
164 use assert_matches::assert_matches;
165 use futures_util::FutureExt;
166
167 #[test]
168 fn parser_for_loop_short() {
169 let mut lexer = Lexer::with_code("for A do :; done");
170 let mut parser = Parser::new(&mut lexer);
171
172 let result = parser.compound_command().now_or_never().unwrap();
173 let compound_command = result.unwrap().unwrap();
174 assert_matches!(compound_command, CompoundCommand::For { name, values, body } => {
175 assert_eq!(name.to_string(), "A");
176 assert_eq!(values, None);
177 assert_eq!(body.to_string(), ":");
178 });
179
180 let next = parser.peek_token().now_or_never().unwrap().unwrap();
181 assert_eq!(next.id, EndOfInput);
182 }
183
184 #[test]
185 fn parser_for_loop_with_semicolon_before_do() {
186 let mut lexer = Lexer::with_code("for B ; do :; done");
187 let mut parser = Parser::new(&mut lexer);
188
189 let result = parser.compound_command().now_or_never().unwrap();
190 let compound_command = result.unwrap().unwrap();
191 assert_matches!(compound_command, CompoundCommand::For { name, values, body } => {
192 assert_eq!(name.to_string(), "B");
193 assert_eq!(values, None);
194 assert_eq!(body.to_string(), ":");
195 });
196
197 let next = parser.peek_token().now_or_never().unwrap().unwrap();
198 assert_eq!(next.id, EndOfInput);
199 }
200
201 #[test]
202 fn parser_for_loop_with_semicolon_and_newlines_before_do() {
203 let mut lexer = Lexer::with_code("for B ; \n\t\n do :; done");
204 let mut parser = Parser::new(&mut lexer);
205
206 let result = parser.compound_command().now_or_never().unwrap();
207 let compound_command = result.unwrap().unwrap();
208 assert_matches!(compound_command, CompoundCommand::For { name, values, body } => {
209 assert_eq!(name.to_string(), "B");
210 assert_eq!(values, None);
211 assert_eq!(body.to_string(), ":");
212 });
213
214 let next = parser.peek_token().now_or_never().unwrap().unwrap();
215 assert_eq!(next.id, EndOfInput);
216 }
217
218 #[test]
219 fn parser_for_loop_with_newlines_before_do() {
220 let mut lexer = Lexer::with_code("for B \n \\\n \n do :; done");
221 let mut parser = Parser::new(&mut lexer);
222
223 let result = parser.compound_command().now_or_never().unwrap();
224 let compound_command = result.unwrap().unwrap();
225 assert_matches!(compound_command, CompoundCommand::For { name, values, body } => {
226 assert_eq!(name.to_string(), "B");
227 assert_eq!(values, None);
228 assert_eq!(body.to_string(), ":");
229 });
230
231 let next = parser.peek_token().now_or_never().unwrap().unwrap();
232 assert_eq!(next.id, EndOfInput);
233 }
234
235 #[test]
236 fn parser_for_loop_with_zero_values_delimited_by_semicolon() {
237 let mut lexer = Lexer::with_code("for foo in; do :; done");
238 let mut parser = Parser::new(&mut lexer);
239
240 let result = parser.compound_command().now_or_never().unwrap();
241 let compound_command = result.unwrap().unwrap();
242 assert_matches!(compound_command, CompoundCommand::For { name, values, body } => {
243 assert_eq!(name.to_string(), "foo");
244 assert_eq!(values, Some(vec![]));
245 assert_eq!(body.to_string(), ":");
246 });
247
248 let next = parser.peek_token().now_or_never().unwrap().unwrap();
249 assert_eq!(next.id, EndOfInput);
250 }
251
252 #[test]
253 fn parser_for_loop_with_one_value_delimited_by_semicolon_and_newlines() {
254 let mut lexer = Lexer::with_code("for foo in bar; \n \n do :; done");
255 let mut parser = Parser::new(&mut lexer);
256
257 let result = parser.compound_command().now_or_never().unwrap();
258 let compound_command = result.unwrap().unwrap();
259 assert_matches!(compound_command, CompoundCommand::For { name, values, body } => {
260 assert_eq!(name.to_string(), "foo");
261 let values = values
262 .unwrap()
263 .iter()
264 .map(ToString::to_string)
265 .collect::<Vec<String>>();
266 assert_eq!(values, vec!["bar"]);
267 assert_eq!(body.to_string(), ":");
268 });
269
270 let next = parser.peek_token().now_or_never().unwrap().unwrap();
271 assert_eq!(next.id, EndOfInput);
272 }
273
274 #[test]
275 fn parser_for_loop_with_many_values_delimited_by_one_newline() {
276 let mut lexer = Lexer::with_code("for in in in a b c\ndo :; done");
277 let mut parser = Parser::new(&mut lexer);
278
279 let result = parser.compound_command().now_or_never().unwrap();
280 let compound_command = result.unwrap().unwrap();
281 assert_matches!(compound_command, CompoundCommand::For { name, values, body } => {
282 assert_eq!(name.to_string(), "in");
283 let values = values
284 .unwrap()
285 .iter()
286 .map(ToString::to_string)
287 .collect::<Vec<String>>();
288 assert_eq!(values, vec!["in", "a", "b", "c"]);
289 assert_eq!(body.to_string(), ":");
290 });
291
292 let next = parser.peek_token().now_or_never().unwrap().unwrap();
293 assert_eq!(next.id, EndOfInput);
294 }
295
296 #[test]
297 fn parser_for_loop_with_zero_values_delimited_by_many_newlines() {
298 let mut lexer = Lexer::with_code("for foo in \n \n \n do :; done");
299 let mut parser = Parser::new(&mut lexer);
300
301 let result = parser.compound_command().now_or_never().unwrap();
302 let compound_command = result.unwrap().unwrap();
303 assert_matches!(compound_command, CompoundCommand::For { name, values, body } => {
304 assert_eq!(name.to_string(), "foo");
305 assert_eq!(values, Some(vec![]));
306 assert_eq!(body.to_string(), ":");
307 });
308
309 let next = parser.peek_token().now_or_never().unwrap().unwrap();
310 assert_eq!(next.id, EndOfInput);
311 }
312
313 #[test]
314 fn parser_for_loop_newlines_before_in() {
315 let mut lexer = Lexer::with_code("for foo\n \n\nin\ndo :; done");
316 let mut parser = Parser::new(&mut lexer);
317
318 let result = parser.compound_command().now_or_never().unwrap();
319 let compound_command = result.unwrap().unwrap();
320 assert_matches!(compound_command, CompoundCommand::For { name, values, body } => {
321 assert_eq!(name.to_string(), "foo");
322 assert_eq!(values, Some(vec![]));
323 assert_eq!(body.to_string(), ":");
324 });
325
326 let next = parser.peek_token().now_or_never().unwrap().unwrap();
327 assert_eq!(next.id, EndOfInput);
328 }
329
330 #[test]
331 fn parser_for_loop_aliasing_on_semicolon() {
332 let mut lexer = Lexer::with_code(" FOR_A if :; done");
333 #[allow(clippy::mutable_key_type)]
334 let mut aliases = AliasSet::new();
335 let origin = Location::dummy("");
336 aliases.insert(HashEntry::new(
337 "if".to_string(),
338 " ;\n\ndo".to_string(),
339 false,
340 origin.clone(),
341 ));
342 aliases.insert(HashEntry::new(
343 "FOR_A".to_string(),
344 "for A ".to_string(),
345 false,
346 origin,
347 ));
348 let mut parser = Parser::config().aliases(&aliases).input(&mut lexer);
349
350 let result = parser.take_token_manual(true).now_or_never().unwrap();
351 assert_matches!(result, Ok(Rec::AliasSubstituted));
352
353 let result = parser.compound_command().now_or_never().unwrap();
354 let compound_command = result.unwrap().unwrap();
355 assert_eq!(compound_command.to_string(), "for A do :; done");
356
357 let next = parser.peek_token().now_or_never().unwrap().unwrap();
358 assert_eq!(next.id, EndOfInput);
359 }
360
361 #[test]
362 fn parser_for_loop_aliasing_on_do() {
363 let mut lexer = Lexer::with_code(" FOR_A if :; done");
364 #[allow(clippy::mutable_key_type)]
365 let mut aliases = AliasSet::new();
366 let origin = Location::dummy("");
367 aliases.insert(HashEntry::new(
368 "if".to_string(),
369 "\ndo".to_string(),
370 false,
371 origin.clone(),
372 ));
373 aliases.insert(HashEntry::new(
374 "FOR_A".to_string(),
375 "for A ".to_string(),
376 false,
377 origin,
378 ));
379 let mut parser = Parser::config().aliases(&aliases).input(&mut lexer);
380
381 let result = parser.take_token_manual(true).now_or_never().unwrap();
382 assert_matches!(result, Ok(Rec::AliasSubstituted));
383
384 let result = parser.compound_command().now_or_never().unwrap();
385 let compound_command = result.unwrap().unwrap();
386 assert_eq!(compound_command.to_string(), "for A do :; done");
387
388 let next = parser.peek_token().now_or_never().unwrap().unwrap();
389 assert_eq!(next.id, EndOfInput);
390 }
391
392 #[test]
393 fn parser_for_loop_missing_name_eof() {
394 let mut lexer = Lexer::with_code(" for ");
395 let mut parser = Parser::new(&mut lexer);
396
397 let result = parser.compound_command().now_or_never().unwrap();
398 let e = result.unwrap_err();
399 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::MissingForName));
400 assert_eq!(*e.location.code.value.borrow(), " for ");
401 assert_eq!(e.location.code.start_line_number.get(), 1);
402 assert_eq!(*e.location.code.source, Source::Unknown);
403 assert_eq!(e.location.range, 5..5);
404 }
405
406 #[test]
407 fn parser_for_loop_missing_name_newline() {
408 let mut lexer = Lexer::with_code(" for\ndo :; done");
409 let mut parser = Parser::new(&mut lexer);
410
411 let result = parser.compound_command().now_or_never().unwrap();
412 let e = result.unwrap_err();
413 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::MissingForName));
414 assert_eq!(*e.location.code.value.borrow(), " for\n");
415 assert_eq!(e.location.code.start_line_number.get(), 1);
416 assert_eq!(*e.location.code.source, Source::Unknown);
417 assert_eq!(e.location.range, 4..5);
418 }
419
420 #[test]
421 fn parser_for_loop_missing_name_semicolon() {
422 let mut lexer = Lexer::with_code("for; do :; done");
423 let mut parser = Parser::new(&mut lexer);
424
425 let result = parser.compound_command().now_or_never().unwrap();
426 let e = result.unwrap_err();
427 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::MissingForName));
428 assert_eq!(*e.location.code.value.borrow(), "for; do :; done");
429 assert_eq!(e.location.code.start_line_number.get(), 1);
430 assert_eq!(*e.location.code.source, Source::Unknown);
431 assert_eq!(e.location.range, 3..4);
432 }
433
434 #[test]
435 fn parser_for_loop_invalid_name() {
436 let mut lexer = Lexer::with_code("FOR if do :; done");
438 #[allow(clippy::mutable_key_type)]
439 let mut aliases = AliasSet::new();
440 let origin = Location::dummy("");
441 aliases.insert(HashEntry::new(
442 "FOR".to_string(),
443 "for ".to_string(),
444 false,
445 origin.clone(),
446 ));
447 aliases.insert(HashEntry::new(
448 "if".to_string(),
449 "&".to_string(),
450 false,
451 origin,
452 ));
453 let mut parser = Parser::config().aliases(&aliases).input(&mut lexer);
454
455 let result = parser.take_token_manual(true).now_or_never().unwrap();
456 assert_matches!(result, Ok(Rec::AliasSubstituted));
457
458 let result = parser.compound_command().now_or_never().unwrap();
459 let e = result.unwrap_err();
460 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidForName));
461 assert_eq!(*e.location.code.value.borrow(), "&");
462 assert_eq!(e.location.code.start_line_number.get(), 1);
463 assert_eq!(e.location.range, 0..1);
464 assert_matches!(&*e.location.code.source, Source::Alias { original, alias } => {
465 assert_eq!(*original.code.value.borrow(), "FOR if do :; done");
466 assert_eq!(original.code.start_line_number.get(), 1);
467 assert_eq!(*original.code.source, Source::Unknown);
468 assert_eq!(original.range, 4..6);
469 assert_eq!(alias.name, "if");
470 });
471 }
472
473 #[test]
474 fn parser_for_loop_semicolon_after_newline() {
475 let mut lexer = Lexer::with_code("for X\n; do :; done");
476 let mut parser = Parser::new(&mut lexer);
477
478 let result = parser.compound_command().now_or_never().unwrap();
479 let e = result.unwrap_err();
480 assert_matches!(&e.cause,
481 ErrorCause::Syntax(SyntaxError::MissingForBody { opening_location }) => {
482 assert_eq!(*opening_location.code.value.borrow(), "for X\n; do :; done");
483 assert_eq!(opening_location.code.start_line_number.get(), 1);
484 assert_eq!(*opening_location.code.source, Source::Unknown);
485 assert_eq!(opening_location.range, 0..3);
486 });
487 assert_eq!(*e.location.code.value.borrow(), "for X\n; do :; done");
488 assert_eq!(e.location.code.start_line_number.get(), 1);
489 assert_eq!(*e.location.code.source, Source::Unknown);
490 assert_eq!(e.location.range, 6..7);
491 }
492
493 #[test]
494 fn parser_for_loop_invalid_values_delimiter() {
495 let mut lexer = Lexer::with_code("for_A_in_a_b if c; do :; done");
497 #[allow(clippy::mutable_key_type)]
498 let mut aliases = AliasSet::new();
499 let origin = Location::dummy("");
500 aliases.insert(HashEntry::new(
501 "for_A_in_a_b".to_string(),
502 "for A in a b ".to_string(),
503 false,
504 origin.clone(),
505 ));
506 aliases.insert(HashEntry::new(
507 "if".to_string(),
508 "&".to_string(),
509 false,
510 origin,
511 ));
512 let mut parser = Parser::config().aliases(&aliases).input(&mut lexer);
513
514 let result = parser.take_token_manual(true).now_or_never().unwrap();
515 assert_matches!(result, Ok(Rec::AliasSubstituted));
516
517 let result = parser.compound_command().now_or_never().unwrap();
518 let e = result.unwrap_err();
519 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidForValue));
520 assert_eq!(*e.location.code.value.borrow(), "&");
521 assert_eq!(e.location.code.start_line_number.get(), 1);
522 assert_eq!(e.location.range, 0..1);
523 assert_matches!(&*e.location.code.source, Source::Alias { original, alias } => {
524 assert_eq!(*original.code.value.borrow(), "for_A_in_a_b if c; do :; done");
525 assert_eq!(original.code.start_line_number.get(), 1);
526 assert_eq!(*original.code.source, Source::Unknown);
527 assert_eq!(original.range, 13..15);
528 assert_eq!(alias.name, "if");
529 });
530 }
531
532 #[test]
533 fn parser_for_loop_invalid_token_after_semicolon() {
534 let mut lexer = Lexer::with_code(" for X; ! do :; done");
535 let mut parser = Parser::new(&mut lexer);
536
537 let result = parser.compound_command().now_or_never().unwrap();
538 let e = result.unwrap_err();
539 assert_matches!(&e.cause,
540 ErrorCause::Syntax(SyntaxError::MissingForBody { opening_location }) => {
541 assert_eq!(*opening_location.code.value.borrow(), " for X; ! do :; done");
542 assert_eq!(opening_location.code.start_line_number.get(), 1);
543 assert_eq!(*opening_location.code.source, Source::Unknown);
544 assert_eq!(opening_location.range, 1..4);
545 });
546 assert_eq!(*e.location.code.value.borrow(), " for X; ! do :; done");
547 assert_eq!(e.location.code.start_line_number.get(), 1);
548 assert_eq!(*e.location.code.source, Source::Unknown);
549 assert_eq!(e.location.range, 8..9);
550 }
551}