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