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 crate::alias::EmptyGlossary;
53    use crate::source::Source;
54    use assert_matches::assert_matches;
55    use futures_util::FutureExt;
56
57    #[test]
58    fn parser_command_simple() {
59        let mut lexer = Lexer::from_memory("foo < bar", Source::Unknown);
60        let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
61
62        let result = parser.command().now_or_never().unwrap();
63        let command = result.unwrap().unwrap().unwrap();
64        assert_matches!(command, Command::Simple(c) => {
65            assert_eq!(c.to_string(), "foo <bar");
66        });
67
68        let next = parser.peek_token().now_or_never().unwrap().unwrap();
69        assert_eq!(next.id, EndOfInput);
70    }
71
72    #[test]
73    fn parser_command_compound() {
74        let mut lexer = Lexer::from_memory("(foo) < bar", Source::Unknown);
75        let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
76
77        let result = parser.command().now_or_never().unwrap();
78        let command = result.unwrap().unwrap().unwrap();
79        assert_matches!(command, Command::Compound(c) => {
80            assert_eq!(c.to_string(), "(foo) <bar");
81        });
82
83        let next = parser.peek_token().now_or_never().unwrap().unwrap();
84        assert_eq!(next.id, EndOfInput);
85    }
86
87    #[test]
88    fn parser_command_function() {
89        let mut lexer = Lexer::from_memory("fun () ( echo )", Source::Unknown);
90        let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
91
92        let result = parser.command().now_or_never().unwrap();
93        let command = result.unwrap().unwrap().unwrap();
94        assert_matches!(command, Command::Function(f) => {
95            assert_eq!(f.to_string(), "fun() (echo)");
96        });
97
98        let next = parser.peek_token().now_or_never().unwrap().unwrap();
99        assert_eq!(next.id, EndOfInput);
100    }
101
102    #[test]
103    fn parser_command_eof() {
104        let mut lexer = Lexer::from_memory("", Source::Unknown);
105        let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
106
107        let result = parser.command().now_or_never().unwrap().unwrap();
108        assert_eq!(result, Rec::Parsed(None));
109    }
110}