yash_syntax/parser/
command.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 command
18//!
19//! Note that the detail parser for each type of commands is in another
20//! dedicated module.
21
22use super::core::Parser;
23use super::core::Rec;
24use super::core::Result;
25use crate::syntax::Command;
26
27impl Parser<'_, '_> {
28    /// Parses a command.
29    ///
30    /// If there is no valid command at the current position, this function
31    /// returns `Ok(Rec::Parsed(None))`.
32    pub async fn command(&mut self) -> Result<Rec<Option<Command>>> {
33        match self.simple_command().await? {
34            Rec::AliasSubstituted => Ok(Rec::AliasSubstituted),
35            Rec::Parsed(None) => self
36                .full_compound_command()
37                .await
38                .map(|c| Rec::Parsed(c.map(Command::Compound))),
39            Rec::Parsed(Some(c)) => self
40                .short_function_definition(c)
41                .await
42                .map(|c| Rec::Parsed(Some(c))),
43        }
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::super::lex::Lexer;
50    use super::super::lex::TokenId::EndOfInput;
51    use super::*;
52    use assert_matches::assert_matches;
53    use futures_util::FutureExt;
54
55    #[test]
56    fn parser_command_simple() {
57        let mut lexer = Lexer::with_code("foo < bar");
58        let mut parser = Parser::new(&mut lexer);
59
60        let result = parser.command().now_or_never().unwrap();
61        let command = result.unwrap().unwrap().unwrap();
62        assert_matches!(command, Command::Simple(c) => {
63            assert_eq!(c.to_string(), "foo <bar");
64        });
65
66        let next = parser.peek_token().now_or_never().unwrap().unwrap();
67        assert_eq!(next.id, EndOfInput);
68    }
69
70    #[test]
71    fn parser_command_compound() {
72        let mut lexer = Lexer::with_code("(foo) < bar");
73        let mut parser = Parser::new(&mut lexer);
74
75        let result = parser.command().now_or_never().unwrap();
76        let command = result.unwrap().unwrap().unwrap();
77        assert_matches!(command, Command::Compound(c) => {
78            assert_eq!(c.to_string(), "(foo) <bar");
79        });
80
81        let next = parser.peek_token().now_or_never().unwrap().unwrap();
82        assert_eq!(next.id, EndOfInput);
83    }
84
85    #[test]
86    fn parser_command_function() {
87        let mut lexer = Lexer::with_code("fun () ( echo )");
88        let mut parser = Parser::new(&mut lexer);
89
90        let result = parser.command().now_or_never().unwrap();
91        let command = result.unwrap().unwrap().unwrap();
92        assert_matches!(command, Command::Function(f) => {
93            assert_eq!(f.to_string(), "fun() (echo)");
94        });
95
96        let next = parser.peek_token().now_or_never().unwrap().unwrap();
97        assert_eq!(next.id, EndOfInput);
98    }
99
100    #[test]
101    fn parser_command_eof() {
102        let mut lexer = Lexer::with_code("");
103        let mut parser = Parser::new(&mut lexer);
104
105        let result = parser.command().now_or_never().unwrap().unwrap();
106        assert_eq!(result, Rec::Parsed(None));
107    }
108}