yash_syntax/parser/
and_or.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2020 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! Syntax parser for and-or list
18
19use super::core::Parser;
20use super::core::Rec;
21use super::core::Result;
22use super::error::Error;
23use super::error::SyntaxError;
24use super::lex::Operator::{AndAnd, BarBar};
25use super::lex::TokenId::Operator;
26use crate::syntax::AndOr;
27use crate::syntax::AndOrList;
28
29impl Parser<'_, '_> {
30    /// Parses an and-or list.
31    ///
32    /// If there is no valid and-or list at the current position, this function
33    /// returns `Ok(Rec::Parsed(None))`.
34    pub async fn and_or_list(&mut self) -> Result<Rec<Option<AndOrList>>> {
35        let first = match self.pipeline().await? {
36            Rec::AliasSubstituted => return Ok(Rec::AliasSubstituted),
37            Rec::Parsed(None) => return Ok(Rec::Parsed(None)),
38            Rec::Parsed(Some(p)) => p,
39        };
40
41        let mut rest = vec![];
42        loop {
43            let condition = match self.peek_token().await?.id {
44                Operator(AndAnd) => AndOr::AndThen,
45                Operator(BarBar) => AndOr::OrElse,
46                _ => break,
47            };
48            self.take_token_raw().await?;
49
50            while self.newline_and_here_doc_contents().await? {}
51
52            let maybe_pipeline = loop {
53                if let Rec::Parsed(maybe_pipeline) = self.pipeline().await? {
54                    break maybe_pipeline;
55                }
56            };
57            let pipeline = match maybe_pipeline {
58                None => {
59                    let cause = SyntaxError::MissingPipeline(condition).into();
60                    let location = self.peek_token().await?.word.location.clone();
61                    return Err(Error { cause, location });
62                }
63                Some(pipeline) => pipeline,
64            };
65
66            rest.push((condition, pipeline));
67        }
68
69        Ok(Rec::Parsed(Some(AndOrList { first, rest })))
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::super::error::ErrorCause;
76    use super::super::lex::Lexer;
77    use super::*;
78    use crate::alias::EmptyGlossary;
79    use crate::source::Source;
80    use futures_util::FutureExt;
81
82    #[test]
83    fn parser_and_or_list_eof() {
84        let mut lexer = Lexer::from_memory("", Source::Unknown);
85        let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
86
87        let result = parser.and_or_list().now_or_never().unwrap();
88        assert_eq!(result, Ok(Rec::Parsed(None)));
89    }
90
91    #[test]
92    fn parser_and_or_list_one() {
93        let mut lexer = Lexer::from_memory("foo", Source::Unknown);
94        let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
95
96        let result = parser.and_or_list().now_or_never().unwrap();
97        let aol = result.unwrap().unwrap().unwrap();
98        assert_eq!(aol.first.to_string(), "foo");
99        assert_eq!(aol.rest, vec![]);
100    }
101
102    #[test]
103    fn parser_and_or_list_many() {
104        let mut lexer = Lexer::from_memory("first && second || \n\n third;", Source::Unknown);
105        let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
106
107        let result = parser.and_or_list().now_or_never().unwrap();
108        let aol = result.unwrap().unwrap().unwrap();
109        assert_eq!(aol.first.to_string(), "first");
110        assert_eq!(aol.rest.len(), 2);
111        assert_eq!(aol.rest[0].0, AndOr::AndThen);
112        assert_eq!(aol.rest[0].1.to_string(), "second");
113        assert_eq!(aol.rest[1].0, AndOr::OrElse);
114        assert_eq!(aol.rest[1].1.to_string(), "third");
115    }
116
117    #[test]
118    fn parser_and_or_list_missing_command_after_and_and() {
119        let mut lexer = Lexer::from_memory("foo &&", Source::Unknown);
120        let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
121
122        let e = parser.and_or_list().now_or_never().unwrap().unwrap_err();
123        assert_eq!(
124            e.cause,
125            ErrorCause::Syntax(SyntaxError::MissingPipeline(AndOr::AndThen))
126        );
127        assert_eq!(*e.location.code.value.borrow(), "foo &&");
128        assert_eq!(e.location.code.start_line_number.get(), 1);
129        assert_eq!(*e.location.code.source, Source::Unknown);
130        assert_eq!(e.location.range, 6..6);
131    }
132}