1use super::core::Parser;
20use super::core::Result;
21use super::error::Error;
22use super::error::SyntaxError;
23use super::lex::Operator::{LessLess, LessLessDash};
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 _ => Ok(None),
96 }
97 }
98
99 pub async fn redirection(&mut self) -> Result<Option<Redir>> {
106 let fd = match self.peek_token().await?.id {
107 IoNumber => {
108 let token = self.take_token_raw().await?;
109 if let Ok(fd) = token.word.to_string().parse() {
110 Some(Fd(fd))
111 } else {
112 return Err(Error {
113 cause: SyntaxError::FdOutOfRange.into(),
114 location: token.word.location,
115 });
116 }
117 }
118 IoLocation => {
119 let token = self.take_token_raw().await?;
120 return Err(Error {
122 cause: SyntaxError::InvalidIoLocation.into(),
123 location: token.word.location,
124 });
125 }
126 _ => None,
127 };
128
129 Ok(self
130 .redirection_body()
131 .await?
132 .map(|body| Redir { fd, body }))
133 }
134
135 pub async fn redirections(&mut self) -> Result<Vec<Redir>> {
137 let mut redirs = vec![];
139 while let Some(redir) = self.redirection().await? {
140 redirs.push(redir);
141 }
142 Ok(redirs)
143 }
144}
145
146#[allow(clippy::bool_assert_comparison)]
147#[cfg(test)]
148mod tests {
149 use super::super::error::ErrorCause;
150 use super::super::lex::Lexer;
151 use super::super::lex::Operator::Newline;
152 use super::*;
153 use crate::source::Source;
154 use assert_matches::assert_matches;
155 use futures_util::FutureExt;
156
157 #[test]
158 fn parser_redirection_less() {
159 let mut lexer = Lexer::with_code("</dev/null\n");
160 let mut parser = Parser::new(&mut lexer);
161
162 let result = parser.redirection().now_or_never().unwrap();
163 let redir = result.unwrap().unwrap();
164 assert_eq!(redir.fd, None);
165 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
166 assert_eq!(operator, RedirOp::FileIn);
167 assert_eq!(operand.to_string(), "/dev/null")
168 });
169
170 let next = parser.peek_token().now_or_never().unwrap().unwrap();
171 assert_eq!(next.id, Operator(Newline));
172 }
173
174 #[test]
175 fn parser_redirection_less_greater() {
176 let mut lexer = Lexer::with_code("<> /dev/null\n");
177 let mut parser = Parser::new(&mut lexer);
178
179 let result = parser.redirection().now_or_never().unwrap();
180 let redir = result.unwrap().unwrap();
181 assert_eq!(redir.fd, None);
182 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
183 assert_eq!(operator, RedirOp::FileInOut);
184 assert_eq!(operand.to_string(), "/dev/null")
185 });
186 }
187
188 #[test]
189 fn parser_redirection_greater() {
190 let mut lexer = Lexer::with_code(">/dev/null\n");
191 let mut parser = Parser::new(&mut lexer);
192
193 let result = parser.redirection().now_or_never().unwrap();
194 let redir = result.unwrap().unwrap();
195 assert_eq!(redir.fd, None);
196 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
197 assert_eq!(operator, RedirOp::FileOut);
198 assert_eq!(operand.to_string(), "/dev/null")
199 });
200 }
201
202 #[test]
203 fn parser_redirection_greater_greater() {
204 let mut lexer = Lexer::with_code(" >> /dev/null\n");
205 let mut parser = Parser::new(&mut lexer);
206
207 let result = parser.redirection().now_or_never().unwrap();
208 let redir = result.unwrap().unwrap();
209 assert_eq!(redir.fd, None);
210 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
211 assert_eq!(operator, RedirOp::FileAppend);
212 assert_eq!(operand.to_string(), "/dev/null")
213 });
214 }
215
216 #[test]
217 fn parser_redirection_greater_bar() {
218 let mut lexer = Lexer::with_code(">| /dev/null\n");
219 let mut parser = Parser::new(&mut lexer);
220
221 let result = parser.redirection().now_or_never().unwrap();
222 let redir = result.unwrap().unwrap();
223 assert_eq!(redir.fd, None);
224 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
225 assert_eq!(operator, RedirOp::FileClobber);
226 assert_eq!(operand.to_string(), "/dev/null")
227 });
228 }
229
230 #[test]
231 fn parser_redirection_less_and() {
232 let mut lexer = Lexer::with_code("<& -\n");
233 let mut parser = Parser::new(&mut lexer);
234
235 let result = parser.redirection().now_or_never().unwrap();
236 let redir = result.unwrap().unwrap();
237 assert_eq!(redir.fd, None);
238 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
239 assert_eq!(operator, RedirOp::FdIn);
240 assert_eq!(operand.to_string(), "-")
241 });
242 }
243
244 #[test]
245 fn parser_redirection_greater_and() {
246 let mut lexer = Lexer::with_code(">& 3\n");
247 let mut parser = Parser::new(&mut lexer);
248
249 let result = parser.redirection().now_or_never().unwrap();
250 let redir = result.unwrap().unwrap();
251 assert_eq!(redir.fd, None);
252 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
253 assert_eq!(operator, RedirOp::FdOut);
254 assert_eq!(operand.to_string(), "3")
255 });
256 }
257
258 #[test]
259 fn parser_redirection_greater_greater_bar() {
260 let mut lexer = Lexer::with_code(">>| 3\n");
261 let mut parser = Parser::new(&mut lexer);
262
263 let result = parser.redirection().now_or_never().unwrap();
264 let redir = result.unwrap().unwrap();
265 assert_eq!(redir.fd, None);
266 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
267 assert_eq!(operator, RedirOp::Pipe);
268 assert_eq!(operand.to_string(), "3")
269 });
270 }
271
272 #[test]
273 fn parser_redirection_less_less_less() {
274 let mut lexer = Lexer::with_code("<<< foo\n");
275 let mut parser = Parser::new(&mut lexer);
276
277 let result = parser.redirection().now_or_never().unwrap();
278 let redir = result.unwrap().unwrap();
279 assert_eq!(redir.fd, None);
280 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
281 assert_eq!(operator, RedirOp::String);
282 assert_eq!(operand.to_string(), "foo")
283 });
284 }
285
286 #[test]
287 fn parser_redirection_less_less() {
288 let mut lexer = Lexer::with_code("<<end \nend\n");
289 let mut parser = Parser::new(&mut lexer);
290
291 let result = parser.redirection().now_or_never().unwrap();
292 let redir = result.unwrap().unwrap();
293 assert_eq!(redir.fd, None);
294 let here_doc = assert_matches!(redir.body, RedirBody::HereDoc(here_doc) => here_doc);
295
296 parser
297 .newline_and_here_doc_contents()
298 .now_or_never()
299 .unwrap()
300 .unwrap();
301 assert_eq!(here_doc.delimiter.to_string(), "end");
302 assert_eq!(here_doc.remove_tabs, false);
303 assert_eq!(here_doc.content.get().unwrap().to_string(), "");
304 }
305
306 #[test]
307 fn parser_redirection_less_less_dash() {
308 let mut lexer = Lexer::with_code("<<-end \nend\n");
309 let mut parser = Parser::new(&mut lexer);
310
311 let result = parser.redirection().now_or_never().unwrap();
312 let redir = result.unwrap().unwrap();
313 assert_eq!(redir.fd, None);
314 let here_doc = assert_matches!(redir.body, RedirBody::HereDoc(here_doc) => here_doc);
315
316 parser
317 .newline_and_here_doc_contents()
318 .now_or_never()
319 .unwrap()
320 .unwrap();
321 assert_eq!(here_doc.delimiter.to_string(), "end");
322 assert_eq!(here_doc.remove_tabs, true);
323 assert_eq!(here_doc.content.get().unwrap().to_string(), "");
324 }
325
326 #[test]
327 fn parser_redirection_with_io_number() {
328 let mut lexer = Lexer::with_code("12< /dev/null\n");
329 let mut parser = Parser::new(&mut lexer);
330
331 let result = parser.redirection().now_or_never().unwrap();
332 let redir = result.unwrap().unwrap();
333 assert_eq!(redir.fd, Some(Fd(12)));
334 assert_matches!(redir.body, RedirBody::Normal { operator, operand } => {
335 assert_eq!(operator, RedirOp::FileIn);
336 assert_eq!(operand.to_string(), "/dev/null")
337 });
338
339 let next = parser.peek_token().now_or_never().unwrap().unwrap();
340 assert_eq!(next.id, Operator(Newline));
341 }
342
343 #[test]
344 fn parser_redirection_fd_out_of_range() {
345 let mut lexer = Lexer::with_code("9999999999999999999999999999999999999999< x");
346 let mut parser = Parser::new(&mut lexer);
347
348 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
349 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::FdOutOfRange));
350 assert_eq!(
351 *e.location.code.value.borrow(),
352 "9999999999999999999999999999999999999999< x"
353 );
354 assert_eq!(e.location.code.start_line_number.get(), 1);
355 assert_eq!(*e.location.code.source, Source::Unknown);
356 assert_eq!(e.location.range, 0..40);
357 }
358
359 #[test]
360 fn parser_redirection_io_location() {
361 let mut lexer = Lexer::with_code("{n}< /dev/null\n");
362 let mut parser = Parser::new(&mut lexer);
363
364 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
365 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidIoLocation));
366 assert_eq!(*e.location.code.value.borrow(), "{n}< /dev/null\n");
367 assert_eq!(e.location.code.start_line_number.get(), 1);
368 assert_eq!(*e.location.code.source, Source::Unknown);
369 assert_eq!(e.location.range, 0..3);
370 }
371
372 #[test]
373 fn parser_redirection_not_operator() {
374 let mut lexer = Lexer::with_code("x");
375 let mut parser = Parser::new(&mut lexer);
376
377 let result = parser.redirection().now_or_never().unwrap();
378 assert_eq!(result, Ok(None));
379 }
380
381 #[test]
382 fn parser_redirection_non_word_operand() {
383 let mut lexer = Lexer::with_code(" < >");
384 let mut parser = Parser::new(&mut lexer);
385
386 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
387 assert_eq!(
388 e.cause,
389 ErrorCause::Syntax(SyntaxError::MissingRedirOperand)
390 );
391 assert_eq!(*e.location.code.value.borrow(), " < >");
392 assert_eq!(e.location.code.start_line_number.get(), 1);
393 assert_eq!(*e.location.code.source, Source::Unknown);
394 assert_eq!(e.location.range, 3..4);
395 }
396
397 #[test]
398 fn parser_redirection_eof_operand() {
399 let mut lexer = Lexer::with_code(" < ");
400 let mut parser = Parser::new(&mut lexer);
401
402 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
403 assert_eq!(
404 e.cause,
405 ErrorCause::Syntax(SyntaxError::MissingRedirOperand)
406 );
407 assert_eq!(*e.location.code.value.borrow(), " < ");
408 assert_eq!(e.location.code.start_line_number.get(), 1);
409 assert_eq!(*e.location.code.source, Source::Unknown);
410 assert_eq!(e.location.range, 4..4);
411 }
412
413 #[test]
414 fn parser_redirection_not_heredoc_delimiter() {
415 let mut lexer = Lexer::with_code("<< <<");
416 let mut parser = Parser::new(&mut lexer);
417
418 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
419 assert_eq!(
420 e.cause,
421 ErrorCause::Syntax(SyntaxError::MissingHereDocDelimiter)
422 );
423 assert_eq!(*e.location.code.value.borrow(), "<< <<");
424 assert_eq!(e.location.code.start_line_number.get(), 1);
425 assert_eq!(*e.location.code.source, Source::Unknown);
426 assert_eq!(e.location.range, 3..5);
427 }
428
429 #[test]
430 fn parser_redirection_eof_heredoc_delimiter() {
431 let mut lexer = Lexer::with_code("<<");
432 let mut parser = Parser::new(&mut lexer);
433
434 let e = parser.redirection().now_or_never().unwrap().unwrap_err();
435 assert_eq!(
436 e.cause,
437 ErrorCause::Syntax(SyntaxError::MissingHereDocDelimiter)
438 );
439 assert_eq!(*e.location.code.value.borrow(), "<<");
440 assert_eq!(e.location.code.start_line_number.get(), 1);
441 assert_eq!(*e.location.code.source, Source::Unknown);
442 assert_eq!(e.location.range, 2..2);
443 }
444}