From f5b2928cbbf2aa1ad4949ba2cb3a82a07d224437 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Sun, 12 Jan 2025 08:20:07 +0100 Subject: [PATCH] Refactor subcommand keyword detection This file has bitrotted; for example commit bc66921ac9 (Optimize keyword detection, 2019-04-03) removed use of SKIP_KEYWORDS but confusingly it's still around (even after 0118eafee1 (Remove unused functions, members (and a variable), 2022-04-09). Also some keywords appear in multiple lists; the separate lists are not really used today; there is a comment stating they may be useful in future. It would be great to add an optimization back but either way we should present the set of reserved words in source code as a contiguous list, to make it easy for humans to see all relevant information. --- src/parser_keywords.rs | 111 +++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 49 deletions(-) diff --git a/src/parser_keywords.rs b/src/parser_keywords.rs index df6bfee36..390acb5f4 100644 --- a/src/parser_keywords.rs +++ b/src/parser_keywords.rs @@ -2,64 +2,77 @@ use crate::wchar::prelude::*; -const SKIP_KEYWORDS: &[&wstr] = &[L!("else"), L!("begin")]; -const SUBCOMMAND_KEYWORDS: &[&wstr] = &[ - L!("and"), - L!("begin"), - L!("builtin"), - L!("command"), - L!("exec"), - L!("if"), - L!("not"), - L!("or"), - L!("time"), - L!("while"), -]; -const BLOCK_KEYWORDS: &[&wstr] = &[ - L!("begin"), - L!("for"), - L!("function"), - L!("if"), - L!("switch"), - L!("while"), -]; +struct ReservedWord { + text: &'static wstr, + is_super_command: bool, +} + +macro_rules! rw { + ( ( $text:literal ) ) => { + ReservedWord { + text: L!($text), + is_super_command: false, + } + }; + ( ( $text:literal, [subcommand] ) ) => { + ReservedWord { + text: L!($text), + is_super_command: true, + } + }; +} +macro_rules! reserved_words { + ( $( $reserved_word:tt , ) * ) => { + &[ $( rw!($reserved_word), )* ] + } +} // Don't forget to add any new reserved keywords to the documentation -const RESERVED_KEYWORDS: &[&wstr] = &[ - L!("["), - L!("_"), - L!("argparse"), - L!("break"), - L!("case"), - L!("continue"), - L!("else"), - L!("end"), - L!("eval"), - L!("read"), - L!("return"), - L!("set"), - L!("status"), - L!("string"), - L!("test"), -]; +const RESERVED_WORDS: &[ReservedWord] = reserved_words!( + ("["), + ("_"), + ("and", [subcommand]), + ("argparse"), + ("begin", [subcommand]), + ("break"), + ("builtin", [subcommand]), + ("case"), + ("command", [subcommand]), + ("continue"), + ("else"), + ("end"), + ("eval"), + ("exec", [subcommand]), + ("for"), + ("function"), + ("if", [subcommand]), + ("not", [subcommand]), + ("or", [subcommand]), + ("read"), + ("return"), + ("set"), + ("status"), + ("string"), + ("switch"), + ("test"), + ("time", [subcommand]), + ("while", [subcommand]), +); -// The lists above are purposely implemented separately from the logic below, so that future -// maintainers may assume the contents of the list based off their names, and not off what the -// functions below require them to contain. +fn reserved_word(cmd: &wstr) -> Option<&'static ReservedWord> { + RESERVED_WORDS + .iter() + .find(|reserved_word| reserved_word.text == cmd) +} -/// Tests if the specified commands parameters should be interpreted as another command, which will -/// be true if the command is either 'command', 'exec', 'if', 'while', or 'builtin'. This does not -/// handle "else if" which is more complicated. +/// Tests if the specified command's parameters should be interpreted as another command. pub fn parser_keywords_is_subcommand(cmd: &wstr) -> bool { - SUBCOMMAND_KEYWORDS.contains(&cmd) || SKIP_KEYWORDS.contains(&cmd) + reserved_word(cmd).is_some_and(|reserved_word| reserved_word.is_super_command) } /// Tests if the specified command is a reserved word, i.e. if it is the name of one of the builtin /// functions that change the block or command scope, like 'for', 'end' or 'command' or 'exec'. /// These functions may not be overloaded, so their names are reserved. pub fn parser_keywords_is_reserved(word: &wstr) -> bool { - SUBCOMMAND_KEYWORDS.contains(&word) - || SKIP_KEYWORDS.contains(&word) - || BLOCK_KEYWORDS.contains(&word) - || RESERVED_KEYWORDS.contains(&word) + reserved_word(word).is_some() }