diff --git a/src/attribute.rs b/src/attribute.rs index 4ec813f076..a478942c43 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -13,8 +13,8 @@ pub(crate) enum Attribute<'src> { Doc(Option>), Extension(StringLiteral<'src>), Group(StringLiteral<'src>), - Linux, - Macos, + Linux { inverted: bool }, + Macos { inverted: bool }, NoCd, NoExitMessage, NoQuiet, @@ -22,8 +22,8 @@ pub(crate) enum Attribute<'src> { PositionalArguments, Private, Script(Option>), - Unix, - Windows, + Unix { inverted: bool }, + Windows { inverted: bool }, WorkingDirectory(StringLiteral<'src>), } @@ -51,6 +51,7 @@ impl<'src> Attribute<'src> { pub(crate) fn new( name: Name<'src>, arguments: Vec>, + inverted: bool, ) -> CompileResult<'src, Self> { let discriminant = name .lexeme() @@ -75,29 +76,34 @@ impl<'src> Attribute<'src> { ); } - Ok(match discriminant { - AttributeDiscriminant::Confirm => Self::Confirm(arguments.into_iter().next()), - AttributeDiscriminant::Doc => Self::Doc(arguments.into_iter().next()), - AttributeDiscriminant::Extension => Self::Extension(arguments.into_iter().next().unwrap()), - AttributeDiscriminant::Group => Self::Group(arguments.into_iter().next().unwrap()), - AttributeDiscriminant::Linux => Self::Linux, - AttributeDiscriminant::Macos => Self::Macos, - AttributeDiscriminant::NoCd => Self::NoCd, - AttributeDiscriminant::NoExitMessage => Self::NoExitMessage, - AttributeDiscriminant::NoQuiet => Self::NoQuiet, - AttributeDiscriminant::Openbsd => Self::Openbsd, - AttributeDiscriminant::PositionalArguments => Self::PositionalArguments, - AttributeDiscriminant::Private => Self::Private, - AttributeDiscriminant::Script => Self::Script({ + Ok(match (inverted, discriminant) { + (inverted, AttributeDiscriminant::Linux) => Self::Linux { inverted }, + (inverted, AttributeDiscriminant::Macos) => Self::Macos { inverted }, + (inverted, AttributeDiscriminant::Unix) => Self::Unix { inverted }, + (inverted, AttributeDiscriminant::Windows) => Self::Windows { inverted }, + + (true, _attr) => return Err(name.error(CompileErrorKind::InvalidInvertedAttribute { attr_name: name.lexeme() })), + + (false, AttributeDiscriminant::Confirm) => Self::Confirm(arguments.into_iter().next()), + (false, AttributeDiscriminant::Doc) => Self::Doc(arguments.into_iter().next()), + (false, AttributeDiscriminant::Extension) => { + Self::Extension(arguments.into_iter().next().unwrap()) + } + (false, AttributeDiscriminant::Group) => Self::Group(arguments.into_iter().next().unwrap()), + (false, AttributeDiscriminant::NoCd) => Self::NoCd, + (false, AttributeDiscriminant::NoExitMessage) => Self::NoExitMessage, + (false, AttributeDiscriminant::NoQuiet) => Self::NoQuiet, + (false, AttributeDiscriminant::Openbsd) => Self::Openbsd, + (false, AttributeDiscriminant::PositionalArguments) => Self::PositionalArguments, + (false, AttributeDiscriminant::Private) => Self::Private, + (false, AttributeDiscriminant::Script) => Self::Script({ let mut arguments = arguments.into_iter(); arguments.next().map(|command| Interpreter { command, arguments: arguments.collect(), }) }), - AttributeDiscriminant::Unix => Self::Unix, - AttributeDiscriminant::Windows => Self::Windows, - AttributeDiscriminant::WorkingDirectory => { + (false, AttributeDiscriminant::WorkingDirectory) => { Self::WorkingDirectory(arguments.into_iter().next().unwrap()) } }) @@ -118,28 +124,34 @@ impl<'src> Attribute<'src> { impl Display for Attribute<'_> { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.name())?; + let name = self.name(); match self { Self::Confirm(Some(argument)) | Self::Doc(Some(argument)) | Self::Extension(argument) | Self::Group(argument) - | Self::WorkingDirectory(argument) => write!(f, "({argument})")?, - Self::Script(Some(shell)) => write!(f, "({shell})")?, + | Self::WorkingDirectory(argument) => write!(f, "{name}({argument})")?, + Self::Script(Some(shell)) => write!(f, "{name}({shell})")?, + Self::Linux { inverted } + | Self::Macos { inverted } + | Self::Unix { inverted } + | Self::Windows { inverted } => { + if *inverted { + write!(f, "not({name})")?; + } else { + write!(f, "{name}")?; + } + } Self::Confirm(None) | Self::Doc(None) - | Self::Linux - | Self::Macos | Self::NoCd | Self::NoExitMessage | Self::NoQuiet | Self::Openbsd | Self::PositionalArguments | Self::Private - | Self::Script(None) - | Self::Unix - | Self::Windows => {} + | Self::Script(None) => write!(f, "{name}")?, } Ok(()) diff --git a/src/compile_error.rs b/src/compile_error.rs index ce53c12f70..e1c84ad6ef 100644 --- a/src/compile_error.rs +++ b/src/compile_error.rs @@ -186,6 +186,7 @@ impl Display for CompileError<'_> { _ => character.escape_default().collect(), } ), + InvalidInvertedAttribute { attr_name } => write!(f, "{attr_name} cannot be inverted with `not()`"), MismatchedClosingDelimiter { open, open_line, diff --git a/src/compile_error_kind.rs b/src/compile_error_kind.rs index 09e2eb337c..fd4b64f614 100644 --- a/src/compile_error_kind.rs +++ b/src/compile_error_kind.rs @@ -79,6 +79,9 @@ pub(crate) enum CompileErrorKind<'src> { InvalidEscapeSequence { character: char, }, + InvalidInvertedAttribute { + attr_name: &'src str, + }, MismatchedClosingDelimiter { close: Delimiter, open: Delimiter, diff --git a/src/parser.rs b/src/parser.rs index 5d7821a335..0e1192b05a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1135,7 +1135,18 @@ impl<'run, 'src> Parser<'run, 'src> { token.get_or_insert(bracket); loop { - let name = self.parse_name()?; + let (name, inverted) = { + let mut i = false; + let mut n = self.parse_name()?; + if n.lexeme() == "not" { + i = true; + self.expect(ParenL)?; + n = self.parse_name()?; + self.expect(ParenR)?; + } + + (n, i) + }; let mut arguments = Vec::new(); @@ -1152,7 +1163,7 @@ impl<'run, 'src> Parser<'run, 'src> { self.expect(ParenR)?; } - let attribute = Attribute::new(name, arguments)?; + let attribute = Attribute::new(name, arguments, inverted)?; let first = attributes.get(&attribute).or_else(|| { if attribute.repeatable() {