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::source::Source;
79    use futures_util::FutureExt;
80
81    #[test]
82    fn parser_and_or_list_eof() {
83        let mut lexer = Lexer::with_code("");
84        let mut parser = Parser::new(&mut lexer);
85
86        let result = parser.and_or_list().now_or_never().unwrap();
87        assert_eq!(result, Ok(Rec::Parsed(None)));
88    }
89
90    #[test]
91    fn parser_and_or_list_one() {
92        let mut lexer = Lexer::with_code("foo");
93        let mut parser = Parser::new(&mut lexer);
94
95        let result = parser.and_or_list().now_or_never().unwrap();
96        let aol = result.unwrap().unwrap().unwrap();
97        assert_eq!(aol.first.to_string(), "foo");
98        assert_eq!(aol.rest, vec![]);
99    }
100
101    #[test]
102    fn parser_and_or_list_many() {
103        let mut lexer = Lexer::with_code("first && second || \n\n third;");
104        let mut parser = Parser::new(&mut lexer);
105
106        let result = parser.and_or_list().now_or_never().unwrap();
107        let aol = result.unwrap().unwrap().unwrap();
108        assert_eq!(aol.first.to_string(), "first");
109        assert_eq!(aol.rest.len(), 2);
110        assert_eq!(aol.rest[0].0, AndOr::AndThen);
111        assert_eq!(aol.rest[0].1.to_string(), "second");
112        assert_eq!(aol.rest[1].0, AndOr::OrElse);
113        assert_eq!(aol.rest[1].1.to_string(), "third");
114    }
115
116    #[test]
117    fn parser_and_or_list_missing_command_after_and_and() {
118        let mut lexer = Lexer::with_code("foo &&");
119        let mut parser = Parser::new(&mut lexer);
120
121        let e = parser.and_or_list().now_or_never().unwrap().unwrap_err();
122        assert_eq!(
123            e.cause,
124            ErrorCause::Syntax(SyntaxError::MissingPipeline(AndOr::AndThen))
125        );
126        assert_eq!(*e.location.code.value.borrow(), "foo &&");
127        assert_eq!(e.location.code.start_line_number.get(), 1);
128        assert_eq!(*e.location.code.source, Source::Unknown);
129        assert_eq!(e.location.range, 6..6);
130    }
131}