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