Skip to main content

perl_keywords/
lib.rs

1//! Canonical Perl keyword inventories and allocation-free lookup helpers.
2//!
3//! Exports a single [`KEYWORDS`] constant containing the full set of Perl
4//! reserved words, pragmas, and special tokens. The list is used by the lexer,
5//! completion provider, and semantic-token highlighter to distinguish keywords
6//! from user-defined identifiers.
7
8/// Canonical union of keyword inventories used by the workspace.
9pub const KEYWORDS: &[&str] = &[
10    "AUTOLOAD",
11    "BEGIN",
12    "CHECK",
13    "DESTROY",
14    "END",
15    "INIT",
16    "UNITCHECK",
17    "__FILE__",
18    "__LINE__",
19    "__PACKAGE__",
20    "abs",
21    "and",
22    "bless",
23    "blessed",
24    "break",
25    "catch",
26    "chomp",
27    "chop",
28    "chr",
29    "class",
30    "close",
31    "cmp",
32    "continue",
33    "default",
34    "defined",
35    "delete",
36    "die",
37    "do",
38    "each",
39    "else",
40    "elsif",
41    "eq",
42    "eval",
43    "exists",
44    "exit",
45    "finally",
46    "for",
47    "foreach",
48    "format",
49    "ge",
50    "given",
51    "goto",
52    "grep",
53    "gt",
54    "hex",
55    "if",
56    "index",
57    "int",
58    "join",
59    "keys",
60    "last",
61    "lc",
62    "lcfirst",
63    "le",
64    "length",
65    "local",
66    "lt",
67    "m",
68    "map",
69    "method",
70    "my",
71    "ne",
72    "next",
73    "no",
74    "not",
75    "oct",
76    "open",
77    "or",
78    "ord",
79    "our",
80    "pack",
81    "package",
82    "pop",
83    "print",
84    "printf",
85    "push",
86    "q",
87    "qq",
88    "qr",
89    "qw",
90    "qx",
91    "read",
92    "redo",
93    "ref",
94    "require",
95    "return",
96    "reverse",
97    "rindex",
98    "s",
99    "say",
100    "scalar",
101    "shift",
102    "sort",
103    "splice",
104    "split",
105    "sprintf",
106    "sqrt",
107    "state",
108    "sub",
109    "substr",
110    "tie",
111    "tr",
112    "try",
113    "uc",
114    "ucfirst",
115    "undef",
116    "unless",
117    "unpack",
118    "unshift",
119    "untie",
120    "until",
121    "use",
122    "values",
123    "wantarray",
124    "warn",
125    "when",
126    "while",
127    "write",
128    "xor",
129    "y",
130];
131
132/// Keywords used by `perl-lsp-completion` keyword completion.
133pub const LSP_COMPLETION_KEYWORDS: &[&str] = &[
134    "AUTOLOAD",
135    "BEGIN",
136    "CHECK",
137    "DESTROY",
138    "END",
139    "INIT",
140    "UNITCHECK",
141    "__FILE__",
142    "__LINE__",
143    "__PACKAGE__",
144    "and",
145    "blessed",
146    "cmp",
147    "defined",
148    "die",
149    "do",
150    "else",
151    "elsif",
152    "eq",
153    "eval",
154    "exit",
155    "for",
156    "foreach",
157    "ge",
158    "goto",
159    "gt",
160    "if",
161    "last",
162    "le",
163    "local",
164    "lt",
165    "my",
166    "ne",
167    "next",
168    "not",
169    "or",
170    "our",
171    "package",
172    "redo",
173    "ref",
174    "require",
175    "return",
176    "scalar",
177    "state",
178    "sub",
179    "undef",
180    "unless",
181    "until",
182    "use",
183    "wantarray",
184    "warn",
185    "while",
186    "xor",
187];
188
189/// Keywords used by DAP debug-console completions.
190pub const DAP_COMPLETION_KEYWORDS: &[&str] = &[
191    "abs", "bless", "chomp", "chop", "chr", "close", "defined", "delete", "die", "do", "each",
192    "else", "elsif", "eval", "exists", "for", "foreach", "grep", "hex", "if", "index", "int",
193    "join", "keys", "last", "lc", "lcfirst", "length", "local", "map", "my", "next", "oct", "open",
194    "ord", "our", "pack", "package", "pop", "print", "printf", "push", "qw", "redo", "ref",
195    "require", "return", "reverse", "rindex", "say", "scalar", "shift", "sort", "splice", "split",
196    "sprintf", "sqrt", "sub", "substr", "tie", "uc", "ucfirst", "unless", "unpack", "unshift",
197    "untie", "until", "use", "values", "warn", "while",
198];
199
200/// Keywords used by runtime fallback completion in `perl-lsp`.
201pub const LSP_RUNTIME_COMPLETION_KEYWORDS: &[&str] = &[
202    "close", "default", "die", "else", "elsif", "for", "foreach", "given", "goto", "grep", "if",
203    "last", "local", "map", "my", "next", "open", "our", "package", "pop", "print", "push", "read",
204    "redo", "require", "return", "say", "shift", "sort", "splice", "state", "sub", "unless",
205    "unshift", "until", "use", "warn", "when", "while", "write",
206];
207
208/// Keywords reserved for LSP rename validation.
209pub const RENAME_KEYWORDS: &[&str] = &[
210    "and", "else", "elsif", "eq", "for", "foreach", "if", "last", "local", "my", "ne", "next",
211    "not", "or", "our", "package", "redo", "require", "return", "state", "sub", "unless", "until",
212    "use", "while",
213];
214
215/// Keywords used by parser LSP-compat completion/rename paths.
216pub const PARSER_LSP_KEYWORDS: &[&str] = &[
217    "break", "continue", "default", "die", "do", "else", "elsif", "eval", "for", "foreach",
218    "given", "goto", "if", "last", "local", "my", "next", "no", "our", "package", "redo",
219    "require", "return", "sub", "unless", "until", "use", "warn", "when", "while",
220];
221
222/// Keywords recognized by `perl-lexer` for token classification.
223pub const LEXER_KEYWORDS: &[&str] = &[
224    "BEGIN",
225    "CHECK",
226    "END",
227    "INIT",
228    "UNITCHECK",
229    "and",
230    "break",
231    "catch",
232    "class",
233    "cmp",
234    "continue",
235    "default",
236    "die",
237    "do",
238    "else",
239    "elsif",
240    "eval",
241    "finally",
242    "for",
243    "foreach",
244    "format",
245    "given",
246    "goto",
247    "grep",
248    "if",
249    "last",
250    "local",
251    "m",
252    "map",
253    "method",
254    "my",
255    "next",
256    "not",
257    "or",
258    "our",
259    "package",
260    "print",
261    "q",
262    "qq",
263    "qr",
264    "qw",
265    "qx",
266    "redo",
267    "require",
268    "return",
269    "s",
270    "say",
271    "sort",
272    "split",
273    "state",
274    "sub",
275    "tr",
276    "try",
277    "undef",
278    "unless",
279    "until",
280    "use",
281    "warn",
282    "when",
283    "while",
284    "xor",
285    "y",
286];
287
288/// Return `true` when `token` exists in the canonical keyword inventory.
289#[must_use]
290pub fn is_keyword(token: &str) -> bool {
291    KEYWORDS.binary_search(&token).is_ok()
292}
293
294/// Return `true` when `token` is recognized as a lexer keyword.
295#[must_use]
296pub fn is_lexer_keyword(token: &str) -> bool {
297    LEXER_KEYWORDS.binary_search(&token).is_ok()
298}
299
300/// Return `true` when `token` exists in the LSP completion keyword bucket.
301#[must_use]
302pub fn is_lsp_completion_keyword(token: &str) -> bool {
303    LSP_COMPLETION_KEYWORDS.binary_search(&token).is_ok()
304}
305
306/// Return `true` when `token` exists in the DAP completion keyword bucket.
307#[must_use]
308pub fn is_dap_completion_keyword(token: &str) -> bool {
309    DAP_COMPLETION_KEYWORDS.binary_search(&token).is_ok()
310}
311
312/// Return `true` when `token` exists in the runtime completion keyword bucket.
313#[must_use]
314pub fn is_lsp_runtime_completion_keyword(token: &str) -> bool {
315    LSP_RUNTIME_COMPLETION_KEYWORDS.binary_search(&token).is_ok()
316}
317
318/// Return `true` when `token` is reserved in rename validation paths.
319#[must_use]
320pub fn is_rename_keyword(token: &str) -> bool {
321    RENAME_KEYWORDS.binary_search(&token).is_ok()
322}
323
324/// Return `true` when `token` exists in parser LSP-compat keyword bucket.
325#[must_use]
326pub fn is_parser_lsp_keyword(token: &str) -> bool {
327    PARSER_LSP_KEYWORDS.binary_search(&token).is_ok()
328}
329
330#[cfg(test)]
331mod tests {
332    use super::{
333        DAP_COMPLETION_KEYWORDS, KEYWORDS, LEXER_KEYWORDS, LSP_COMPLETION_KEYWORDS,
334        LSP_RUNTIME_COMPLETION_KEYWORDS, PARSER_LSP_KEYWORDS, RENAME_KEYWORDS,
335        is_dap_completion_keyword, is_keyword, is_lexer_keyword, is_lsp_completion_keyword,
336        is_lsp_runtime_completion_keyword, is_parser_lsp_keyword, is_rename_keyword,
337    };
338
339    fn assert_sorted_unique(name: &str, items: &[&str]) {
340        let mut last = "";
341        for &item in items {
342            assert!(item > last, "{name} must be sorted + unique: {item} after {last}");
343            last = item;
344        }
345    }
346
347    #[test]
348    fn keyword_lists_are_sorted_and_unique() {
349        assert_sorted_unique("KEYWORDS", KEYWORDS);
350        assert_sorted_unique("LSP_COMPLETION_KEYWORDS", LSP_COMPLETION_KEYWORDS);
351        assert_sorted_unique("DAP_COMPLETION_KEYWORDS", DAP_COMPLETION_KEYWORDS);
352        assert_sorted_unique("LSP_RUNTIME_COMPLETION_KEYWORDS", LSP_RUNTIME_COMPLETION_KEYWORDS);
353        assert_sorted_unique("RENAME_KEYWORDS", RENAME_KEYWORDS);
354        assert_sorted_unique("PARSER_LSP_KEYWORDS", PARSER_LSP_KEYWORDS);
355        assert_sorted_unique("LEXER_KEYWORDS", LEXER_KEYWORDS);
356    }
357
358    #[test]
359    fn known_keywords_are_present() {
360        assert!(is_keyword("my"));
361        assert!(is_keyword("foreach"));
362        assert!(is_keyword("print"));
363        assert!(is_keyword("__PACKAGE__"));
364        assert!(!is_keyword("definitely_not_a_perl_keyword"));
365    }
366
367    #[test]
368    fn lookup_helpers_match_bucket_membership() {
369        for &item in LSP_COMPLETION_KEYWORDS {
370            assert!(is_lsp_completion_keyword(item));
371            assert!(is_keyword(item));
372        }
373        for &item in DAP_COMPLETION_KEYWORDS {
374            assert!(is_dap_completion_keyword(item));
375            assert!(is_keyword(item));
376        }
377        for &item in LSP_RUNTIME_COMPLETION_KEYWORDS {
378            assert!(is_lsp_runtime_completion_keyword(item));
379            assert!(is_keyword(item));
380        }
381        for &item in RENAME_KEYWORDS {
382            assert!(is_rename_keyword(item));
383            assert!(is_keyword(item));
384        }
385        for &item in PARSER_LSP_KEYWORDS {
386            assert!(is_parser_lsp_keyword(item));
387            assert!(is_keyword(item));
388        }
389        for &item in LEXER_KEYWORDS {
390            assert!(is_lexer_keyword(item));
391            assert!(is_keyword(item));
392        }
393    }
394
395    #[test]
396    fn lookup_helpers_reject_unknown_tokens() {
397        assert!(!is_lsp_completion_keyword("print"));
398        assert!(!is_dap_completion_keyword("AUTOLOAD"));
399        assert!(!is_lsp_runtime_completion_keyword("AUTOLOAD"));
400        assert!(!is_rename_keyword("print"));
401        assert!(!is_parser_lsp_keyword("AUTOLOAD"));
402        assert!(!is_lexer_keyword("__PACKAGE__"));
403    }
404}