1 // Functions having to do with parser keywords, like testing if a function is a block command.
2 #include "config.h"  // IWYU pragma: keep
3 
4 #include "parser_keywords.h"
5 
6 #include <string>
7 #include <unordered_set>
8 
9 #include "common.h"
10 #include "fallback.h"  // IWYU pragma: keep
11 
12 using string_set_t = std::unordered_set<wcstring>;
13 
14 static const wcstring skip_keywords[]{
15     L"else",
16     L"begin",
17 };
18 
19 static const wcstring subcommand_keywords[]{L"command", L"builtin", L"while", L"exec", L"if",
20                                             L"and",     L"or",      L"not",   L"time", L"begin"};
21 
22 static const string_set_t block_keywords = {L"for",      L"while",  L"if",
23                                             L"function", L"switch", L"begin"};
24 
25 static const wcstring reserved_keywords[] = {
26     L"end",  L"case",   L"else", L"return", L"continue", L"break", L"argparse",
27     L"read", L"string", L"set",  L"status", L"test",     L"["};
28 
29 // The lists above are purposely implemented separately from the logic below, so that future
30 // maintainers may assume the contents of the list based off their names, and not off what the
31 // functions below require them to contain.
32 
list_max_length(const string_set_t & list)33 static size_t list_max_length(const string_set_t &list) {
34     size_t result = 0;
35     for (const auto &w : list) {
36         if (w.length() > result) {
37             result = w.length();
38         }
39     }
40     return result;
41 }
42 
parser_keywords_skip_arguments(const wcstring & cmd)43 bool parser_keywords_skip_arguments(const wcstring &cmd) {
44     return cmd == skip_keywords[0] || cmd == skip_keywords[1];
45 }
46 
parser_keywords_is_subcommand(const wcstring & cmd)47 bool parser_keywords_is_subcommand(const wcstring &cmd) {
48     const static string_set_t search_list = ([] {
49         string_set_t results;
50         results.insert(std::begin(subcommand_keywords), std::end(subcommand_keywords));
51         results.insert(std::begin(skip_keywords), std::end(skip_keywords));
52         return results;
53     })();
54 
55     const static auto max_len = list_max_length(search_list);
56     const static auto not_found = search_list.end();
57 
58     // Everything above is executed only at startup, this is the actual optimized search routine:
59     return cmd.length() <= max_len && search_list.find(cmd) != not_found;
60 }
61 
parser_keywords_is_block(const wcstring & word)62 bool parser_keywords_is_block(const wcstring &word) {
63     const static auto max_len = list_max_length(block_keywords);
64     const static auto not_found = block_keywords.end();
65 
66     // Everything above is executed only at startup, this is the actual optimized search routine:
67     return word.length() <= max_len && block_keywords.find(word) != not_found;
68 }
69 
parser_keywords_is_reserved(const wcstring & word)70 bool parser_keywords_is_reserved(const wcstring &word) {
71     const static string_set_t search_list = ([] {
72         string_set_t results;
73         results.insert(std::begin(subcommand_keywords), std::end(subcommand_keywords));
74         results.insert(std::begin(skip_keywords), std::end(skip_keywords));
75         results.insert(std::begin(block_keywords), std::end(block_keywords));
76         results.insert(std::begin(reserved_keywords), std::end(reserved_keywords));
77         return results;
78     })();
79     const static size_t max_len = list_max_length(search_list);
80     return word.length() <= max_len && search_list.count(word) > 0;
81 }
82