1#[derive(Debug, Clone, Copy)]
5pub enum Category {
6 Strict,
7 Reserved,
8 Weak,
9}
10
11#[derive(Debug, Clone, Copy)]
13pub struct Keyword {
14 token: &'static str,
15 category: Category,
16 escaped: &'static str,
17}
18
19impl std::ops::Deref for Keyword {
20 type Target = &'static str;
21
22 fn deref(&self) -> &Self::Target {
23 &self.token
24 }
25}
26
27
28impl Keyword {
29 pub fn category(&self) -> Category {
31 self.category
32 }
33
34 pub fn token(&self) -> &str {
36 self.token
37 }
38}
39
40macro_rules! keywords {
41 ($($cat:path => { $($name:ident : $value:tt)* })*) => {
42 pub const KEYWORDS : &[Keyword] = &[$($(Keyword{
43 token: stringify!($value),
45 category: $cat,
46 escaped: concat!("r#", stringify!($value))
47 }),*),*];
48 };
49}
50
51use std::{collections::HashMap};
52
53keywords! {
55 Category::Strict => {
56 KW_AS : as
57 KW_BREAK : break
58 KW_CONST : const
59 KW_CONTINUE : continue
60 KW_CRATE : crate
61 KW_ELSE : else
62 KW_ENUM : enum
63 KW_EXTERN : extern
64 KW_FALSE : false
65 KW_FN : fn
66 KW_FOR : for
67 KW_IF : if
68 KW_IMPL : impl
69 KW_IN : in
70 KW_LET : let
71 KW_LOOP : loop
72 KW_MATCH : match
73 KW_MOD : mod
74 KW_MOVE : move
75 KW_MUT : mut
76 KW_PUB : pub
77 KW_REF : ref
78 KW_RETURN : return
79 KW_SELFVALUE : self
80 KW_SELFTYPE : Self
81 KW_STATIC : static
82 KW_STRUCT : struct
83 KW_SUPER : super
84 KW_TRAIT : trait
85 KW_TRUE : true
86 KW_TYPE : type
87 KW_UNSAFE : unsafe
88 KW_USE : use
89 KW_WHERE : where
90 KW_WHILE : while
91 KW_ASYNC : async
92 KW_AWAIT : await
93 KW_DYN : dyn
94 }
95 Category::Reserved => {
96 KW_ABSTRACT : abstract
97 KW_ALIGNOF : alignof
98 KW_BECOME : become
99 KW_DO : do
100 KW_FINAL : final
101 KW_MACRO : macro
102 KW_OFFSETOF : offsetof
103 KW_OVERRIDE : override
104 KW_PRIV : priv
105 KW_PROC : proc
106 KW_PURE : pure
107 KW_SIZEOF : sizeof
108 KW_TYPEOF : typeof
109 KW_UNSIZED : unsized
110 KW_VIRTUAL : virtual
111 KW_YIELD : yield
112 }
113 Category::Weak => {
114 KW_UNION : union
115 KW_STATICLIFETIME : 'static
116 }
117}
118
119pub fn is_keyword(token: &str) -> bool {
120 KEYWORDS_BY_TOKEN.contains_key(token)
121}
122use lazy_static::lazy_static;
123
124lazy_static! {
125 static ref KEYWORDS_BY_TOKEN: HashMap<&'static str, &'static Keyword> = KEYWORDS
126 .iter()
127 .map(|keyword| (keyword.token, keyword))
128 .collect();
129}
130
131pub fn get_keyword(keyword: &str) -> Option<&'static Keyword> {
132 KEYWORDS_BY_TOKEN.get(keyword).cloned()
133}
134
135fn is_valid_ident_char(c: char) -> bool {
136 c.is_ascii_alphanumeric() || c == '_'
137}
138
139fn is_ident_shaped(ident: &str) -> bool {
140 ident.chars().all(is_valid_ident_char)
141 && match ident.chars().next() {
142 Some('_') => ident.len() >= 2,
143 Some(c) => c.is_ascii_alphabetic(),
144 None => false,
145 }
146}
147
148
149pub fn is_ident(ident: &str) -> bool {
151 is_ident_shaped(ident) && !is_keyword(ident)
153 || (ident.starts_with("r#") && is_ident_shaped(ident.split_at(2).1))
154}
155
156
157pub fn escape_ident(ident: &str) -> &str {
159 if let Some(keyword) = get_keyword(ident) {
160 keyword.escaped
161 } else {
162 ident
163 }
164}
165
166#[cfg(test)]
167mod test;
168