1use super::core::Parser;
20use super::core::Result;
21use super::error::Error;
22use super::error::SyntaxError;
23use super::lex::Operator::{GreaterOpenParen, LessLess, LessLessDash, LessOpenParen};
24use super::lex::TokenId::{EndOfInput, IoLocation, IoNumber, Operator, Token};
25use crate::source::Location;
26use crate::syntax::Fd;
27use crate::syntax::HereDoc;
28use crate::syntax::Redir;
29use crate::syntax::RedirBody;
30use crate::syntax::RedirOp;
31use crate::syntax::Word;
32use std::cell::OnceCell;
33use std::rc::Rc;
34
35impl Parser<'_, '_> {
36 async fn redirection_operand(&mut self) -> Result<std::result::Result<Word, Location>> {
38 let operand = self.take_token_auto(&[]).await?;
39 match operand.id {
40 Token(_) => (),
41 Operator(_) | EndOfInput => return Ok(Err(operand.word.location)),
42 IoNumber | IoLocation => (), }
44 Ok(Ok(operand.word))
45 }
46
47 async fn normal_redirection_body(&mut self, operator: RedirOp) -> Result<RedirBody> {
49 self.take_token_raw().await?;
51 let operand = self
52 .redirection_operand()
53 .await?
54 .map_err(|location| Error {
55 cause: SyntaxError::MissingRedirOperand.into(),
56 location,
57 })?;
58 Ok(RedirBody::Normal { operator, operand })
59 }
60
61 async fn here_doc_redirection_body(&mut self, remove_tabs: bool) -> Result<RedirBody> {
63 self.take_token_raw().await?;
64 let delimiter = self
65 .redirection_operand()
66 .await?
67 .map_err(|location| Error {
68 cause: SyntaxError::MissingHereDocDelimiter.into(),
69 location,
70 })?;
71 let here_doc = Rc::new(HereDoc {
72 delimiter,
73 remove_tabs,
74 content: OnceCell::new(),
75 });
76 self.memorize_unread_here_doc(Rc::clone(&here_doc));
77
78 Ok(RedirBody::HereDoc(here_doc))
79 }
80
81 async fn redirection_body(&mut self) -> Result<Option<RedirBody>> {
83 let operator = match self.peek_token().await?.id {
84 Operator(operator) => operator,
85 _ => return Ok(None),
86 };
87
88 if let Ok(operator) = RedirOp::try_from(operator) {
89 return Ok(Some(self.normal_redirection_body(operator).await?));
90 }
91 match operator {
92 LessLess => Ok(Some(self.here_doc_redirection_body(false).await?)),
93 LessLessDash => Ok(Some(self.here_doc_redirection_body(true).await?)),
94 LessOpenParen | GreaterOpenParen => {
95 let cause = SyntaxError::UnsupportedProcessRedirection.into();
96 let location = self.peek_token().await?.word.location.clone();
97 Err(Error { cause, location })
98 }
99 _ => Ok(None),
100 }
101 }
102
103 pub async fn redirection(&mut self) -> Result<Option<Redir>> {
110 let fd = match self.peek_token().await?.id {
111 IoNumber => {
112 let token = self.take_token_raw().await?;
113 if let Ok(fd) = token.word.to_string().parse() {
114 Some(Fd(fd))
115 } else {
116 return Err(Error {
117 cause: SyntaxError::FdOutOfRange.into(),
118 location: token.word.location,
119 });
120 }
121 }
122 IoLocation => {
123 let token = self.take_token_raw().await?;
124 return Err(Error {
126 cause: SyntaxError::InvalidIoLocation.into(),
127 location: token.word.location,
128 });
129 }
130 _ => None,
131 };
132
133 Ok(self
134 .redirection_body()
135 .await?
136 .map(|body| Redir { fd, body }))
137 }
138
139 pub async fn redirections(&mut self) -> Result<Vec<Redir>> {
141 let mut redirs = vec![];
143 while let Some(redir) = self.redirection().await? {
144 redirs.push(redir);
145 }
146 Ok(redirs)
147 }
148}
149
150#[allow(
151 clippy::bool_assert_comparison,
152 reason = "to make the expected values clearer"
153)]
154#[cfg(test)]
155mod tests {
156 use super::super::error::ErrorCause;
157 use super::super::lex::Lexer;
158 use super::super::lex::Operator::Newline;
159 use super::*;
160 use crate::source::Source;
161 use assert_matches::assert_matches;
162 use futures_util::FutureExt as _;
163
164 #[test]
165 fn parser_redirection_less() {
166 let mut lexer = Lexer::with_code("</dev/null\n");
167 let mut parser = Parser::new(&mut lexer);
168
169 let result = parser.redirection().now_or_never().unwrap();
170 let redir = result.unwrap().unwrap();
171 assert_eq!(redir.fd, None);
172 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
173 assert_eq!(operator, RedirOp::FileIn);
174 assert_eq!(operand.to_string(), "/dev/null")
175 });
176
177 let next = parser.peek_token().now_or_never().unwrap().unwrap();
178 assert_eq!(next.id, Operator(Newline));
179 }
180
181 #[test]
182 fn parser_redirection_less_greater() {
183 let mut lexer = Lexer::with_code("<> /dev/null\n");
184 let mut parser = Parser::new(&mut lexer);
185
186 let result = parser.redirection().now_or_never().unwrap();
187 let redir = result.unwrap().unwrap();
188 assert_eq!(redir.fd, None);
189 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
190 assert_eq!(operator, RedirOp::FileInOut);
191 assert_eq!(operand.to_string(), "/dev/null")
192 });
193 }
194
195 #[test]
196 fn parser_redirection_greater() {
197 let mut lexer = Lexer::with_code(">/dev/null\n");
198 let mut parser = Parser::new(&mut lexer);
199
200 let result = parser.redirection().now_or_never().unwrap();
201 let redir = result.unwrap().unwrap();
202 assert_eq!(redir.fd, None);
203 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
204 assert_eq!(operator, RedirOp::FileOut);
205 assert_eq!(operand.to_string(), "/dev/null")
206 });
207 }
208
209 #[test]
210 fn parser_redirection_greater_greater() {
211 let mut lexer = Lexer::with_code(" >> /dev/null\n");
212 let mut parser = Parser::new(&mut lexer);
213
214 let result = parser.redirection().now_or_never().unwrap();
215 let redir = result.unwrap().unwrap();
216 assert_eq!(redir.fd, None);
217 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
218 assert_eq!(operator, RedirOp::FileAppend);
219 assert_eq!(operand.to_string(), "/dev/null")
220 });
221 }
222
223 #[test]
224 fn parser_redirection_greater_bar() {
225 let mut lexer = Lexer::with_code(">| /dev/null\n");
226 let mut parser = Parser::new(&mut lexer);
227
228 let result = parser.redirection().now_or_never().unwrap();
229 let redir = result.unwrap().unwrap();
230 assert_eq!(redir.fd, None);
231 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
232 assert_eq!(operator, RedirOp::FileClobber);
233 assert_eq!(operand.to_string(), "/dev/null")
234 });
235 }
236
237 #[test]
238 fn parser_redirection_less_and() {
239 let mut lexer = Lexer::with_code("<& -\n");
240 let mut parser = Parser::new(&mut lexer);
241
242 let result = parser.redirection().now_or_never().unwrap();
243 let redir = result.unwrap().unwrap();
244 assert_eq!(redir.fd, None);
245 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
246 assert_eq!(operator, RedirOp::FdIn);
247 assert_eq!(operand.to_string(), "-")
248 });
249 }
250
251 #[test]
252 fn parser_redirection_greater_and() {
253 let mut lexer = Lexer::with_code(">& 3\n");
254 let mut parser = Parser::new(&mut lexer);
255
256 let result = parser.redirection().now_or_never().unwrap();
257 let redir = result.unwrap().unwrap();
258 assert_eq!(redir.fd, None);
259 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
260 assert_eq!(operator, RedirOp::FdOut);
261 assert_eq!(operand.to_string(), "3")
262 });
263 }
264
265 #[test]
266 fn parser_redirection_greater_greater_bar() {
267 let mut lexer = Lexer::with_code(">>| 3\n");
268 let mut parser = Parser::new(&mut lexer);
269
270 let result = parser.redirection().now_or_never().unwrap();
271 let redir = result.unwrap().unwrap();
272 assert_eq!(redir.fd, None);
273 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
274 assert_eq!(operator, RedirOp::Pipe);
275 assert_eq!(operand.to_string(), "3")
276 });
277 }
278
279 #[test]
280 fn parser_redirection_less_paren() {
281 let mut lexer = Lexer::with_code("<(foo)\n");
282 let mut parser = Parser::new(&mut lexer);
283
284 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
285 assert_eq!(
286 e.cause,
287 ErrorCause::Syntax(SyntaxError::UnsupportedProcessRedirection)
288 );
289 assert_eq!(*e.location.code.value.borrow(), "<(foo)\n");
290 assert_eq!(e.location.code.start_line_number.get(), 1);
291 assert_eq!(*e.location.code.source, Source::Unknown);
292 assert_eq!(e.location.range, 0..2);
293 }
294
295 #[test]
296 fn parser_redirection_greater_paren() {
297 let mut lexer = Lexer::with_code(">(foo)\n");
298 let mut parser = Parser::new(&mut lexer);
299
300 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
301 assert_eq!(
302 e.cause,
303 ErrorCause::Syntax(SyntaxError::UnsupportedProcessRedirection)
304 );
305 assert_eq!(*e.location.code.value.borrow(), ">(foo)\n");
306 assert_eq!(e.location.code.start_line_number.get(), 1);
307 assert_eq!(*e.location.code.source, Source::Unknown);
308 assert_eq!(e.location.range, 0..2);
309 }
310
311 #[test]
312 fn parser_redirection_less_less_less() {
313 let mut lexer = Lexer::with_code("<<< foo\n");
314 let mut parser = Parser::new(&mut lexer);
315
316 let result = parser.redirection().now_or_never().unwrap();
317 let redir = result.unwrap().unwrap();
318 assert_eq!(redir.fd, None);
319 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
320 assert_eq!(operator, RedirOp::String);
321 assert_eq!(operand.to_string(), "foo")
322 });
323 }
324
325 #[test]
326 fn parser_redirection_less_less() {
327 let mut lexer = Lexer::with_code("<<end \nend\n");
328 let mut parser = Parser::new(&mut lexer);
329
330 let result = parser.redirection().now_or_never().unwrap();
331 let redir = result.unwrap().unwrap();
332 assert_eq!(redir.fd, None);
333 let here_doc = assert_matches!(redir.body, RedirBody::HereDoc(here_doc) => here_doc);
334
335 parser
336 .newline_and_here_doc_contents()
337 .now_or_never()
338 .unwrap()
339 .unwrap();
340 assert_eq!(here_doc.delimiter.to_string(), "end");
341 assert_eq!(here_doc.remove_tabs, false);
342 assert_eq!(here_doc.content.get().unwrap().to_string(), "");
343 }
344
345 #[test]
346 fn parser_redirection_less_less_dash() {
347 let mut lexer = Lexer::with_code("<<-end \nend\n");
348 let mut parser = Parser::new(&mut lexer);
349
350 let result = parser.redirection().now_or_never().unwrap();
351 let redir = result.unwrap().unwrap();
352 assert_eq!(redir.fd, None);
353 let here_doc = assert_matches!(redir.body, RedirBody::HereDoc(here_doc) => here_doc);
354
355 parser
356 .newline_and_here_doc_contents()
357 .now_or_never()
358 .unwrap()
359 .unwrap();
360 assert_eq!(here_doc.delimiter.to_string(), "end");
361 assert_eq!(here_doc.remove_tabs, true);
362 assert_eq!(here_doc.content.get().unwrap().to_string(), "");
363 }
364
365 #[test]
366 fn parser_redirection_with_io_number() {
367 let mut lexer = Lexer::with_code("12< /dev/null\n");
368 let mut parser = Parser::new(&mut lexer);
369
370 let result = parser.redirection().now_or_never().unwrap();
371 let redir = result.unwrap().unwrap();
372 assert_eq!(redir.fd, Some(Fd(12)));
373 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
374 assert_eq!(operator, RedirOp::FileIn);
375 assert_eq!(operand.to_string(), "/dev/null")
376 });
377
378 let next = parser.peek_token().now_or_never().unwrap().unwrap();
379 assert_eq!(next.id, Operator(Newline));
380 }
381
382 #[test]
383 fn parser_redirection_fd_out_of_range() {
384 let mut lexer = Lexer::with_code("9999999999999999999999999999999999999999< x");
385 let mut parser = Parser::new(&mut lexer);
386
387 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
388 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::FdOutOfRange));
389 assert_eq!(
390 *e.location.code.value.borrow(),
391 "9999999999999999999999999999999999999999< x"
392 );
393 assert_eq!(e.location.code.start_line_number.get(), 1);
394 assert_eq!(*e.location.code.source, Source::Unknown);
395 assert_eq!(e.location.range, 0..40);
396 }
397
398 #[test]
399 fn parser_redirection_io_location() {
400 let mut lexer = Lexer::with_code("{n}< /dev/null\n");
401 let mut parser = Parser::new(&mut lexer);
402
403 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
404 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidIoLocation));
405 assert_eq!(*e.location.code.value.borrow(), "{n}< /dev/null\n");
406 assert_eq!(e.location.code.start_line_number.get(), 1);
407 assert_eq!(*e.location.code.source, Source::Unknown);
408 assert_eq!(e.location.range, 0..3);
409 }
410
411 #[test]
412 fn parser_redirection_not_operator() {
413 let mut lexer = Lexer::with_code("x");
414 let mut parser = Parser::new(&mut lexer);
415
416 let result = parser.redirection().now_or_never().unwrap();
417 assert_eq!(result, Ok(None));
418 }
419
420 #[test]
421 fn parser_redirection_non_word_operand() {
422 let mut lexer = Lexer::with_code(" < >");
423 let mut parser = Parser::new(&mut lexer);
424
425 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
426 assert_eq!(
427 e.cause,
428 ErrorCause::Syntax(SyntaxError::MissingRedirOperand)
429 );
430 assert_eq!(*e.location.code.value.borrow(), " < >");
431 assert_eq!(e.location.code.start_line_number.get(), 1);
432 assert_eq!(*e.location.code.source, Source::Unknown);
433 assert_eq!(e.location.range, 3..4);
434 }
435
436 #[test]
437 fn parser_redirection_eof_operand() {
438 let mut lexer = Lexer::with_code(" < ");
439 let mut parser = Parser::new(&mut lexer);
440
441 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
442 assert_eq!(
443 e.cause,
444 ErrorCause::Syntax(SyntaxError::MissingRedirOperand)
445 );
446 assert_eq!(*e.location.code.value.borrow(), " < ");
447 assert_eq!(e.location.code.start_line_number.get(), 1);
448 assert_eq!(*e.location.code.source, Source::Unknown);
449 assert_eq!(e.location.range, 4..4);
450 }
451
452 #[test]
453 fn parser_redirection_not_heredoc_delimiter() {
454 let mut lexer = Lexer::with_code("<< <<");
455 let mut parser = Parser::new(&mut lexer);
456
457 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
458 assert_eq!(
459 e.cause,
460 ErrorCause::Syntax(SyntaxError::MissingHereDocDelimiter)
461 );
462 assert_eq!(*e.location.code.value.borrow(), "<< <<");
463 assert_eq!(e.location.code.start_line_number.get(), 1);
464 assert_eq!(*e.location.code.source, Source::Unknown);
465 assert_eq!(e.location.range, 3..5);
466 }
467
468 #[test]
469 fn parser_redirection_eof_heredoc_delimiter() {
470 let mut lexer = Lexer::with_code("<<");
471 let mut parser = Parser::new(&mut lexer);
472
473 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
474 assert_eq!(
475 e.cause,
476 ErrorCause::Syntax(SyntaxError::MissingHereDocDelimiter)
477 );
478 assert_eq!(*e.location.code.value.borrow(), "<<");
479 assert_eq!(e.location.code.start_line_number.get(), 1);
480 assert_eq!(*e.location.code.source, Source::Unknown);
481 assert_eq!(e.location.range, 2..2);
482 }
483}