Skip to main content

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 super::lex::Keyword::{Function, Namespace, OpenBracketBracket, Select};
26use super::lex::TokenId::Token;
27use super::{Error, SyntaxError};
28use crate::syntax::Command;
29
30impl Parser<'_, '_> {
31    /// Parses a command.
32    ///
33    /// If there is no valid command at the current position, this function
34    /// returns `Ok(Rec::Parsed(None))`.
35    pub async fn command(&mut self) -> Result<Rec<Option<Command>>> {
36        match self.simple_command().await? {
37            Rec::AliasSubstituted => Ok(Rec::AliasSubstituted),
38
39            Rec::Parsed(None) => {
40                if let Some(compound) = self.full_compound_command().await? {
41                    return Ok(Rec::Parsed(Some(Command::Compound(compound))));
42                }
43
44                let next = self.peek_token().await?;
45                match next.id {
46                    Token(Some(Function)) => {
47                        let cause = SyntaxError::UnsupportedFunctionDefinitionSyntax.into();
48                        let location = next.word.location.clone();
49                        Err(Error { cause, location })
50                    }
51                    Token(Some(OpenBracketBracket)) => {
52                        let cause = SyntaxError::UnsupportedDoubleBracketCommand.into();
53                        let location = next.word.location.clone();
54                        Err(Error { cause, location })
55                    }
56                    Token(Some(Namespace)) => {
57                        let cause = SyntaxError::UnsupportedNamespaceCommand.into();
58                        let location = next.word.location.clone();
59                        Err(Error { cause, location })
60                    }
61                    Token(Some(Select)) => {
62                        let cause = SyntaxError::UnsupportedSelectCommand.into();
63                        let location = next.word.location.clone();
64                        Err(Error { cause, location })
65                    }
66                    _ => Ok(Rec::Parsed(None)),
67                }
68            }
69
70            Rec::Parsed(Some(c)) => self
71                .short_function_definition(c)
72                .await
73                .map(|c| Rec::Parsed(Some(c))),
74        }
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::super::ErrorCause;
81    use super::super::lex::Lexer;
82    use super::super::lex::TokenId::EndOfInput;
83    use super::*;
84    use crate::source::Source;
85    use assert_matches::assert_matches;
86    use futures_util::FutureExt as _;
87
88    #[test]
89    fn parser_command_simple() {
90        let mut lexer = Lexer::with_code("foo < bar");
91        let mut parser = Parser::new(&mut lexer);
92
93        let result = parser.command().now_or_never().unwrap();
94        let command = result.unwrap().unwrap().unwrap();
95        assert_matches!(command, Command::Simple(c) => {
96            assert_eq!(c.to_string(), "foo <bar");
97        });
98
99        let next = parser.peek_token().now_or_never().unwrap().unwrap();
100        assert_eq!(next.id, EndOfInput);
101    }
102
103    #[test]
104    fn parser_command_compound() {
105        let mut lexer = Lexer::with_code("(foo) < bar");
106        let mut parser = Parser::new(&mut lexer);
107
108        let result = parser.command().now_or_never().unwrap();
109        let command = result.unwrap().unwrap().unwrap();
110        assert_matches!(command, Command::Compound(c) => {
111            assert_eq!(c.to_string(), "(foo) <bar");
112        });
113
114        let next = parser.peek_token().now_or_never().unwrap().unwrap();
115        assert_eq!(next.id, EndOfInput);
116    }
117
118    #[test]
119    fn parser_command_short_function() {
120        let mut lexer = Lexer::with_code("fun () ( echo )");
121        let mut parser = Parser::new(&mut lexer);
122
123        let result = parser.command().now_or_never().unwrap();
124        let command = result.unwrap().unwrap().unwrap();
125        assert_matches!(command, Command::Function(f) => {
126            assert_eq!(f.to_string(), "fun() (echo)");
127        });
128
129        let next = parser.peek_token().now_or_never().unwrap().unwrap();
130        assert_eq!(next.id, EndOfInput);
131    }
132
133    #[test]
134    fn parser_command_long_function() {
135        let mut lexer = Lexer::with_code("  function fun { echo; }");
136        let mut parser = Parser::new(&mut lexer);
137
138        let e = parser.command().now_or_never().unwrap().unwrap_err();
139        assert_eq!(
140            e.cause,
141            ErrorCause::Syntax(SyntaxError::UnsupportedFunctionDefinitionSyntax)
142        );
143        assert_eq!(*e.location.code.value.borrow(), "  function fun { echo; }");
144        assert_eq!(e.location.code.start_line_number.get(), 1);
145        assert_eq!(*e.location.code.source, Source::Unknown);
146        assert_eq!(e.location.range, 2..10);
147    }
148
149    #[test]
150    fn parser_command_double_bracket() {
151        let mut lexer = Lexer::with_code(" [[ foo ]]");
152        let mut parser = Parser::new(&mut lexer);
153
154        let e = parser.command().now_or_never().unwrap().unwrap_err();
155        assert_eq!(
156            e.cause,
157            ErrorCause::Syntax(SyntaxError::UnsupportedDoubleBracketCommand)
158        );
159        assert_eq!(*e.location.code.value.borrow(), " [[ foo ]]");
160        assert_eq!(e.location.code.start_line_number.get(), 1);
161        assert_eq!(*e.location.code.source, Source::Unknown);
162        assert_eq!(e.location.range, 1..3);
163    }
164
165    #[test]
166    fn parser_command_namespace() {
167        let mut lexer = Lexer::with_code(" namespace foo { echo; }");
168        let mut parser = Parser::new(&mut lexer);
169
170        let e = parser.command().now_or_never().unwrap().unwrap_err();
171        assert_eq!(
172            e.cause,
173            ErrorCause::Syntax(SyntaxError::UnsupportedNamespaceCommand)
174        );
175        assert_eq!(*e.location.code.value.borrow(), " namespace foo { echo; }");
176        assert_eq!(e.location.code.start_line_number.get(), 1);
177        assert_eq!(*e.location.code.source, Source::Unknown);
178        assert_eq!(e.location.range, 1..10);
179    }
180
181    #[test]
182    fn parser_command_select() {
183        let mut lexer = Lexer::with_code(" select i in a b; do echo; done");
184        let mut parser = Parser::new(&mut lexer);
185
186        let e = parser.command().now_or_never().unwrap().unwrap_err();
187        assert_eq!(
188            e.cause,
189            ErrorCause::Syntax(SyntaxError::UnsupportedSelectCommand)
190        );
191        assert_eq!(
192            *e.location.code.value.borrow(),
193            " select i in a b; do echo; done"
194        );
195        assert_eq!(e.location.code.start_line_number.get(), 1);
196        assert_eq!(*e.location.code.source, Source::Unknown);
197        assert_eq!(e.location.range, 1..7);
198    }
199
200    #[test]
201    fn parser_command_eof() {
202        let mut lexer = Lexer::with_code("");
203        let mut parser = Parser::new(&mut lexer);
204
205        let result = parser.command().now_or_never().unwrap().unwrap();
206        assert_eq!(result, Rec::Parsed(None));
207    }
208}