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(clippy::bool_assert_comparison)]
151#[cfg(test)]
152mod tests {
153 use super::super::error::ErrorCause;
154 use super::super::lex::Lexer;
155 use super::super::lex::Operator::Newline;
156 use super::*;
157 use crate::source::Source;
158 use assert_matches::assert_matches;
159 use futures_util::FutureExt;
160
161 #[test]
162 fn parser_redirection_less() {
163 let mut lexer = Lexer::with_code("</dev/null\n");
164 let mut parser = Parser::new(&mut lexer);
165
166 let result = parser.redirection().now_or_never().unwrap();
167 let redir = result.unwrap().unwrap();
168 assert_eq!(redir.fd, None);
169 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
170 assert_eq!(operator, RedirOp::FileIn);
171 assert_eq!(operand.to_string(), "/dev/null")
172 });
173
174 let next = parser.peek_token().now_or_never().unwrap().unwrap();
175 assert_eq!(next.id, Operator(Newline));
176 }
177
178 #[test]
179 fn parser_redirection_less_greater() {
180 let mut lexer = Lexer::with_code("<> /dev/null\n");
181 let mut parser = Parser::new(&mut lexer);
182
183 let result = parser.redirection().now_or_never().unwrap();
184 let redir = result.unwrap().unwrap();
185 assert_eq!(redir.fd, None);
186 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
187 assert_eq!(operator, RedirOp::FileInOut);
188 assert_eq!(operand.to_string(), "/dev/null")
189 });
190 }
191
192 #[test]
193 fn parser_redirection_greater() {
194 let mut lexer = Lexer::with_code(">/dev/null\n");
195 let mut parser = Parser::new(&mut lexer);
196
197 let result = parser.redirection().now_or_never().unwrap();
198 let redir = result.unwrap().unwrap();
199 assert_eq!(redir.fd, None);
200 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
201 assert_eq!(operator, RedirOp::FileOut);
202 assert_eq!(operand.to_string(), "/dev/null")
203 });
204 }
205
206 #[test]
207 fn parser_redirection_greater_greater() {
208 let mut lexer = Lexer::with_code(" >> /dev/null\n");
209 let mut parser = Parser::new(&mut lexer);
210
211 let result = parser.redirection().now_or_never().unwrap();
212 let redir = result.unwrap().unwrap();
213 assert_eq!(redir.fd, None);
214 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
215 assert_eq!(operator, RedirOp::FileAppend);
216 assert_eq!(operand.to_string(), "/dev/null")
217 });
218 }
219
220 #[test]
221 fn parser_redirection_greater_bar() {
222 let mut lexer = Lexer::with_code(">| /dev/null\n");
223 let mut parser = Parser::new(&mut lexer);
224
225 let result = parser.redirection().now_or_never().unwrap();
226 let redir = result.unwrap().unwrap();
227 assert_eq!(redir.fd, None);
228 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
229 assert_eq!(operator, RedirOp::FileClobber);
230 assert_eq!(operand.to_string(), "/dev/null")
231 });
232 }
233
234 #[test]
235 fn parser_redirection_less_and() {
236 let mut lexer = Lexer::with_code("<& -\n");
237 let mut parser = Parser::new(&mut lexer);
238
239 let result = parser.redirection().now_or_never().unwrap();
240 let redir = result.unwrap().unwrap();
241 assert_eq!(redir.fd, None);
242 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
243 assert_eq!(operator, RedirOp::FdIn);
244 assert_eq!(operand.to_string(), "-")
245 });
246 }
247
248 #[test]
249 fn parser_redirection_greater_and() {
250 let mut lexer = Lexer::with_code(">& 3\n");
251 let mut parser = Parser::new(&mut lexer);
252
253 let result = parser.redirection().now_or_never().unwrap();
254 let redir = result.unwrap().unwrap();
255 assert_eq!(redir.fd, None);
256 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
257 assert_eq!(operator, RedirOp::FdOut);
258 assert_eq!(operand.to_string(), "3")
259 });
260 }
261
262 #[test]
263 fn parser_redirection_greater_greater_bar() {
264 let mut lexer = Lexer::with_code(">>| 3\n");
265 let mut parser = Parser::new(&mut lexer);
266
267 let result = parser.redirection().now_or_never().unwrap();
268 let redir = result.unwrap().unwrap();
269 assert_eq!(redir.fd, None);
270 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
271 assert_eq!(operator, RedirOp::Pipe);
272 assert_eq!(operand.to_string(), "3")
273 });
274 }
275
276 #[test]
277 fn parser_redirection_less_paren() {
278 let mut lexer = Lexer::with_code("<(foo)\n");
279 let mut parser = Parser::new(&mut lexer);
280
281 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
282 assert_eq!(
283 e.cause,
284 ErrorCause::Syntax(SyntaxError::UnsupportedProcessRedirection)
285 );
286 assert_eq!(*e.location.code.value.borrow(), "<(foo)\n");
287 assert_eq!(e.location.code.start_line_number.get(), 1);
288 assert_eq!(*e.location.code.source, Source::Unknown);
289 assert_eq!(e.location.range, 0..2);
290 }
291
292 #[test]
293 fn parser_redirection_greater_paren() {
294 let mut lexer = Lexer::with_code(">(foo)\n");
295 let mut parser = Parser::new(&mut lexer);
296
297 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
298 assert_eq!(
299 e.cause,
300 ErrorCause::Syntax(SyntaxError::UnsupportedProcessRedirection)
301 );
302 assert_eq!(*e.location.code.value.borrow(), ">(foo)\n");
303 assert_eq!(e.location.code.start_line_number.get(), 1);
304 assert_eq!(*e.location.code.source, Source::Unknown);
305 assert_eq!(e.location.range, 0..2);
306 }
307
308 #[test]
309 fn parser_redirection_less_less_less() {
310 let mut lexer = Lexer::with_code("<<< foo\n");
311 let mut parser = Parser::new(&mut lexer);
312
313 let result = parser.redirection().now_or_never().unwrap();
314 let redir = result.unwrap().unwrap();
315 assert_eq!(redir.fd, None);
316 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
317 assert_eq!(operator, RedirOp::String);
318 assert_eq!(operand.to_string(), "foo")
319 });
320 }
321
322 #[test]
323 fn parser_redirection_less_less() {
324 let mut lexer = Lexer::with_code("<<end \nend\n");
325 let mut parser = Parser::new(&mut lexer);
326
327 let result = parser.redirection().now_or_never().unwrap();
328 let redir = result.unwrap().unwrap();
329 assert_eq!(redir.fd, None);
330 let here_doc = assert_matches!(redir.body, RedirBody::HereDoc(here_doc) => here_doc);
331
332 parser
333 .newline_and_here_doc_contents()
334 .now_or_never()
335 .unwrap()
336 .unwrap();
337 assert_eq!(here_doc.delimiter.to_string(), "end");
338 assert_eq!(here_doc.remove_tabs, false);
339 assert_eq!(here_doc.content.get().unwrap().to_string(), "");
340 }
341
342 #[test]
343 fn parser_redirection_less_less_dash() {
344 let mut lexer = Lexer::with_code("<<-end \nend\n");
345 let mut parser = Parser::new(&mut lexer);
346
347 let result = parser.redirection().now_or_never().unwrap();
348 let redir = result.unwrap().unwrap();
349 assert_eq!(redir.fd, None);
350 let here_doc = assert_matches!(redir.body, RedirBody::HereDoc(here_doc) => here_doc);
351
352 parser
353 .newline_and_here_doc_contents()
354 .now_or_never()
355 .unwrap()
356 .unwrap();
357 assert_eq!(here_doc.delimiter.to_string(), "end");
358 assert_eq!(here_doc.remove_tabs, true);
359 assert_eq!(here_doc.content.get().unwrap().to_string(), "");
360 }
361
362 #[test]
363 fn parser_redirection_with_io_number() {
364 let mut lexer = Lexer::with_code("12< /dev/null\n");
365 let mut parser = Parser::new(&mut lexer);
366
367 let result = parser.redirection().now_or_never().unwrap();
368 let redir = result.unwrap().unwrap();
369 assert_eq!(redir.fd, Some(Fd(12)));
370 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
371 assert_eq!(operator, RedirOp::FileIn);
372 assert_eq!(operand.to_string(), "/dev/null")
373 });
374
375 let next = parser.peek_token().now_or_never().unwrap().unwrap();
376 assert_eq!(next.id, Operator(Newline));
377 }
378
379 #[test]
380 fn parser_redirection_fd_out_of_range() {
381 let mut lexer = Lexer::with_code("9999999999999999999999999999999999999999< x");
382 let mut parser = Parser::new(&mut lexer);
383
384 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
385 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::FdOutOfRange));
386 assert_eq!(
387 *e.location.code.value.borrow(),
388 "9999999999999999999999999999999999999999< x"
389 );
390 assert_eq!(e.location.code.start_line_number.get(), 1);
391 assert_eq!(*e.location.code.source, Source::Unknown);
392 assert_eq!(e.location.range, 0..40);
393 }
394
395 #[test]
396 fn parser_redirection_io_location() {
397 let mut lexer = Lexer::with_code("{n}< /dev/null\n");
398 let mut parser = Parser::new(&mut lexer);
399
400 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
401 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidIoLocation));
402 assert_eq!(*e.location.code.value.borrow(), "{n}< /dev/null\n");
403 assert_eq!(e.location.code.start_line_number.get(), 1);
404 assert_eq!(*e.location.code.source, Source::Unknown);
405 assert_eq!(e.location.range, 0..3);
406 }
407
408 #[test]
409 fn parser_redirection_not_operator() {
410 let mut lexer = Lexer::with_code("x");
411 let mut parser = Parser::new(&mut lexer);
412
413 let result = parser.redirection().now_or_never().unwrap();
414 assert_eq!(result, Ok(None));
415 }
416
417 #[test]
418 fn parser_redirection_non_word_operand() {
419 let mut lexer = Lexer::with_code(" < >");
420 let mut parser = Parser::new(&mut lexer);
421
422 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
423 assert_eq!(
424 e.cause,
425 ErrorCause::Syntax(SyntaxError::MissingRedirOperand)
426 );
427 assert_eq!(*e.location.code.value.borrow(), " < >");
428 assert_eq!(e.location.code.start_line_number.get(), 1);
429 assert_eq!(*e.location.code.source, Source::Unknown);
430 assert_eq!(e.location.range, 3..4);
431 }
432
433 #[test]
434 fn parser_redirection_eof_operand() {
435 let mut lexer = Lexer::with_code(" < ");
436 let mut parser = Parser::new(&mut lexer);
437
438 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
439 assert_eq!(
440 e.cause,
441 ErrorCause::Syntax(SyntaxError::MissingRedirOperand)
442 );
443 assert_eq!(*e.location.code.value.borrow(), " < ");
444 assert_eq!(e.location.code.start_line_number.get(), 1);
445 assert_eq!(*e.location.code.source, Source::Unknown);
446 assert_eq!(e.location.range, 4..4);
447 }
448
449 #[test]
450 fn parser_redirection_not_heredoc_delimiter() {
451 let mut lexer = Lexer::with_code("<< <<");
452 let mut parser = Parser::new(&mut lexer);
453
454 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
455 assert_eq!(
456 e.cause,
457 ErrorCause::Syntax(SyntaxError::MissingHereDocDelimiter)
458 );
459 assert_eq!(*e.location.code.value.borrow(), "<< <<");
460 assert_eq!(e.location.code.start_line_number.get(), 1);
461 assert_eq!(*e.location.code.source, Source::Unknown);
462 assert_eq!(e.location.range, 3..5);
463 }
464
465 #[test]
466 fn parser_redirection_eof_heredoc_delimiter() {
467 let mut lexer = Lexer::with_code("<<");
468 let mut parser = Parser::new(&mut lexer);
469
470 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
471 assert_eq!(
472 e.cause,
473 ErrorCause::Syntax(SyntaxError::MissingHereDocDelimiter)
474 );
475 assert_eq!(*e.location.code.value.borrow(), "<<");
476 assert_eq!(e.location.code.start_line_number.get(), 1);
477 assert_eq!(*e.location.code.source, Source::Unknown);
478 assert_eq!(e.location.range, 2..2);
479 }
480}