identifiers/
lib.rs

1// mod strict;
2// mod reserved;
3
4#[derive(Debug, Clone, Copy)]
5pub enum Category {
6    Strict,
7    Reserved,
8    Weak,
9}
10
11/// a keyword
12#[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    /// Get the keyword's category.
30    pub fn category(&self) -> Category {
31        self.category
32    }
33
34    /// Get the keyword's token.
35    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            // id: stringify!($name),
44            token: stringify!($value),
45            category: $cat,
46            escaped: concat!("r#", stringify!($value))
47        }),*),*];
48    };
49}
50
51use std::{collections::HashMap};
52
53// https://doc.rust-lang.org/reference/keywords.html
54keywords! {
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
149/// is this an identifier?
150pub fn is_ident(ident: &str) -> bool {
151    // http://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/reference/identifiers.html
152    is_ident_shaped(ident) && !is_keyword(ident)
153        || (ident.starts_with("r#") && is_ident_shaped(ident.split_at(2).1))
154}
155
156
157// escape keyword in an identifier
158pub 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