From 50c2c38727760d44355a4c13928a02996670ddae Mon Sep 17 00:00:00 2001 From: WATANABE Yuki Date: Mon, 6 May 2024 23:21:14 +0900 Subject: [PATCH] Reorder simple command words in display --- yash-syntax/CHANGELOG.md | 3 +++ yash-syntax/src/syntax.rs | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/yash-syntax/CHANGELOG.md b/yash-syntax/CHANGELOG.md index 12792203..ae9023eb 100644 --- a/yash-syntax/CHANGELOG.md +++ b/yash-syntax/CHANGELOG.md @@ -18,6 +18,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 is not followed by any name characters. Previously, it returned `Err(SyntaxError::UnclosedParam{…})` if `{` was followed by a non-name character other than `}`. +- `impl std::fmt::Display for SimpleCommand` now prints the command words + after the redirections if there are no assignments and the first word looks + like a keyword. ### Fixed diff --git a/yash-syntax/src/syntax.rs b/yash-syntax/src/syntax.rs index 0e5f29ec..de2c5ae5 100644 --- a/yash-syntax/src/syntax.rs +++ b/yash-syntax/src/syntax.rs @@ -73,6 +73,7 @@ //! here-document contents omitted. To pretty-format an AST in multiple lines //! with here-document contents included, you can use ... TODO TBD. +use crate::parser::lex::Keyword; use crate::parser::lex::Operator; use crate::parser::lex::TryFromOperatorError; use crate::source::Location; @@ -964,6 +965,18 @@ impl SimpleCommand { pub fn is_one_word(&self) -> bool { self.assigns.is_empty() && self.words.len() == 1 && self.redirs.is_empty() } + + /// Tests whether the first word of the simple command is a keyword. + #[must_use] + fn first_word_is_keyword(&self) -> bool { + let Some(word) = self.words.first() else { + return false; + }; + let Some(literal) = word.to_string_if_literal() else { + return false; + }; + literal.parse::().is_ok() + } } impl fmt::Display for SimpleCommand { @@ -971,8 +984,16 @@ impl fmt::Display for SimpleCommand { let i1 = self.assigns.iter().map(|x| x as &dyn fmt::Display); let i2 = self.words.iter().map(|x| x as &dyn fmt::Display); let i3 = self.redirs.iter().map(|x| x as &dyn fmt::Display); - write!(f, "{}", i1.chain(i2).chain(i3).format(" ")) - // TODO Avoid printing a keyword as the first word + + if !self.assigns.is_empty() || !self.first_word_is_keyword() { + write!(f, "{}", i1.chain(i2).chain(i3).format(" ")) + } else { + // If the simple command starts with an assignment or redirection, + // the first word may be a keyword which is treated as a plain word. + // In this case, we need to avoid the word being interpreted as a + // keyword by printing the assignment or redirection first. + write!(f, "{}", i3.chain(i2).format(" ")) + } } } @@ -1925,6 +1946,16 @@ mod tests { assert_eq!(command.to_string(), "foo=bar <