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, OpenBracketBracket};
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                    _ => Ok(Rec::Parsed(None)),
57                }
58            }
59
60            Rec::Parsed(Some(c)) => self
61                .short_function_definition(c)
62                .await
63                .map(|c| Rec::Parsed(Some(c))),
64        }
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::super::ErrorCause;
71    use super::super::lex::Lexer;
72    use super::super::lex::TokenId::EndOfInput;
73    use super::*;
74    use crate::source::Source;
75    use assert_matches::assert_matches;
76    use futures_util::FutureExt as _;
77
78    #[test]
79    fn parser_command_simple() {
80        let mut lexer = Lexer::with_code("foo < bar");
81        let mut parser = Parser::new(&mut lexer);
82
83        let result = parser.command().now_or_never().unwrap();
84        let command = result.unwrap().unwrap().unwrap();
85        assert_matches!(command, Command::Simple(c) => {
86            assert_eq!(c.to_string(), "foo <bar");
87        });
88
89        let next = parser.peek_token().now_or_never().unwrap().unwrap();
90        assert_eq!(next.id, EndOfInput);
91    }
92
93    #[test]
94    fn parser_command_compound() {
95        let mut lexer = Lexer::with_code("(foo) < bar");
96        let mut parser = Parser::new(&mut lexer);
97
98        let result = parser.command().now_or_never().unwrap();
99        let command = result.unwrap().unwrap().unwrap();
100        assert_matches!(command, Command::Compound(c) => {
101            assert_eq!(c.to_string(), "(foo) <bar");
102        });
103
104        let next = parser.peek_token().now_or_never().unwrap().unwrap();
105        assert_eq!(next.id, EndOfInput);
106    }
107
108    #[test]
109    fn parser_command_short_function() {
110        let mut lexer = Lexer::with_code("fun () ( echo )");
111        let mut parser = Parser::new(&mut lexer);
112
113        let result = parser.command().now_or_never().unwrap();
114        let command = result.unwrap().unwrap().unwrap();
115        assert_matches!(command, Command::Function(f) => {
116            assert_eq!(f.to_string(), "fun() (echo)");
117        });
118
119        let next = parser.peek_token().now_or_never().unwrap().unwrap();
120        assert_eq!(next.id, EndOfInput);
121    }
122
123    #[test]
124    fn parser_command_long_function() {
125        let mut lexer = Lexer::with_code("  function fun { echo; }");
126        let mut parser = Parser::new(&mut lexer);
127
128        let e = parser.command().now_or_never().unwrap().unwrap_err();
129        assert_eq!(
130            e.cause,
131            ErrorCause::Syntax(SyntaxError::UnsupportedFunctionDefinitionSyntax)
132        );
133        assert_eq!(*e.location.code.value.borrow(), "  function fun { echo; }");
134        assert_eq!(e.location.code.start_line_number.get(), 1);
135        assert_eq!(*e.location.code.source, Source::Unknown);
136        assert_eq!(e.location.range, 2..10);
137    }
138
139    #[test]
140    fn parser_command_double_bracket() {
141        let mut lexer = Lexer::with_code(" [[ foo ]]");
142        let mut parser = Parser::new(&mut lexer);
143
144        let e = parser.command().now_or_never().unwrap().unwrap_err();
145        assert_eq!(
146            e.cause,
147            ErrorCause::Syntax(SyntaxError::UnsupportedDoubleBracketCommand)
148        );
149        assert_eq!(*e.location.code.value.borrow(), " [[ foo ]]");
150        assert_eq!(e.location.code.start_line_number.get(), 1);
151        assert_eq!(*e.location.code.source, Source::Unknown);
152        assert_eq!(e.location.range, 1..3);
153    }
154
155    #[test]
156    fn parser_command_eof() {
157        let mut lexer = Lexer::with_code("");
158        let mut parser = Parser::new(&mut lexer);
159
160        let result = parser.command().now_or_never().unwrap().unwrap();
161        assert_eq!(result, Rec::Parsed(None));
162    }
163}