Line data Source code
1 : //! The keywords of the grammar, generated from `grammar/lists/*.list` (shared with the bison grammar).
2 :
3 : use crate::{symbol_kind, SymbolKind};
4 :
5 : #[rustfmt::skip]
6 : mod table;
7 :
8 : pub use table::{KEYWORDS, MAX_KEYWORD_LENGTH};
9 : use table::KEYWORD_TABLE;
10 :
11 : /// The category of a keyword
12 : #[derive(Clone, Copy, Debug, PartialEq, Eq)]
13 : pub enum KeywordCategory {
14 : SqlColumnName,
15 : SqlReserved,
16 : SqlTypeFunc,
17 : SqlUnreserved,
18 : }
19 :
20 : /// A keyword
21 : #[derive(Clone, Copy, Debug, PartialEq, Eq)]
22 : pub struct Keyword {
23 : /// The lower-case keyword text
24 : pub name: &'static str,
25 : /// The terminal symbol of the keyword
26 : pub symbol: SymbolKind,
27 : /// The category
28 : pub category: KeywordCategory,
29 : }
30 :
31 : impl Keyword {
32 : /// Find a keyword by its lower-case text
33 191568 : pub fn find(text: &[u8]) -> Option<&'static Keyword> {
34 191568 : if text.len() > MAX_KEYWORD_LENGTH {
35 5077 : return None;
36 186491 : }
37 186491 : let mask = KEYWORD_TABLE.len() - 1;
38 186491 : let mut slot = fnv1a(text) as usize & mask;
39 : loop {
40 220999 : match KEYWORD_TABLE[slot] {
41 94466 : 0 => return None,
42 126533 : i => {
43 126533 : let keyword = &KEYWORDS[i as usize - 1];
44 126533 : if keyword.name.as_bytes() == text {
45 92025 : return Some(keyword);
46 34508 : }
47 : }
48 : }
49 34508 : slot = (slot + 1) & mask;
50 : }
51 191568 : }
52 :
53 : /// The text of a keyword or operator symbol
54 1138303 : pub fn symbol_text(symbol: SymbolKind) -> Option<&'static str> {
55 1138303 : if let Some(name) = Self::name_of(symbol) {
56 1092529 : return Some(name);
57 45774 : }
58 45774 : match symbol {
59 0 : symbol_kind::S_EQUALS_GREATER => Some("=>"),
60 574 : symbol_kind::S_LESS_EQUALS => Some("<="),
61 574 : symbol_kind::S_GREATER_EQUALS => Some(">="),
62 574 : symbol_kind::S_NOT_EQUALS => Some("!="),
63 0 : symbol_kind::S_COLON_EQUALS => Some(":="),
64 44052 : _ => None,
65 : }
66 1138303 : }
67 :
68 : /// The keyword text of a terminal symbol, if the symbol is a keyword
69 1231745 : pub fn name_of(symbol: SymbolKind) -> Option<&'static str> {
70 1231745 : keyword_names().get(symbol as usize).copied().flatten()
71 1231745 : }
72 : }
73 :
74 : /// FNV-1a, the hash of the keyword lookup table (see //grammar/rust:gen_keywords.py)
75 186491 : fn fnv1a(text: &[u8]) -> u32 {
76 186491 : let mut h: u32 = 0x811C_9DC5;
77 1111854 : for &b in text {
78 1111854 : h ^= b as u32;
79 1111854 : h = h.wrapping_mul(0x0100_0193);
80 1111854 : }
81 186491 : h
82 186491 : }
83 :
84 : /// Keyword texts indexed by symbol kind
85 1231745 : fn keyword_names() -> &'static [Option<&'static str>] {
86 : static NAMES: std::sync::OnceLock<Vec<Option<&'static str>>> = std::sync::OnceLock::new();
87 1231745 : NAMES.get_or_init(|| {
88 12 : let mut names = vec![None; crate::tables::YYNTOKENS];
89 5508 : for k in KEYWORDS {
90 5508 : names[k.symbol as usize] = Some(k.name);
91 5508 : }
92 12 : names
93 12 : })
94 1231745 : }
95 :
96 : #[cfg(test)]
97 : mod tests {
98 : use super::*;
99 :
100 : #[test]
101 1 : fn finds_keywords() {
102 1 : assert_eq!(KEYWORDS.len(), 459);
103 1 : let select = Keyword::find(b"select").unwrap();
104 1 : assert_eq!(select.symbol, symbol_kind::S_SELECT);
105 1 : assert_eq!(select.category, KeywordCategory::SqlReserved);
106 1 : assert_eq!(Keyword::find(b"SELECT"), None);
107 1 : assert_eq!(Keyword::find(b"selec"), None);
108 1 : assert_eq!(Keyword::find(b"a_name_that_is_too_long_to_be_a_keyword"), None);
109 458 : assert!(KEYWORDS.windows(2).all(|w| w[0].name < w[1].name));
110 459 : assert!(KEYWORDS.iter().all(|k| Keyword::find(k.name.as_bytes()) == Some(k)));
111 1 : }
112 :
113 : #[test]
114 1 : fn names_symbols() {
115 1 : assert_eq!(Keyword::name_of(symbol_kind::S_SELECT), Some("select"));
116 1 : assert_eq!(Keyword::name_of(symbol_kind::S_IDENT), None);
117 1 : assert_eq!(Keyword::name_of(symbol_kind::S_NOT_LA), None);
118 1 : }
119 : }
|