moore_common/
name.rs

1// Copyright (c) 2016-2021 Fabian Schuiki
2
3//! A name table that internalizes all names presented to it and allows for them
4//! to be referred to by a lightweight tag. This structure is heavily inspired
5//! by the interner used in the Rust compiler.
6
7use std::borrow::Borrow;
8use std::cell::RefCell;
9use std::cmp::Ordering;
10use std::collections::HashMap;
11use std::fmt;
12use std::hash::Hash;
13use std::ops::Deref;
14use std::rc::Rc;
15
16/// A name is a lightweight 32 bit tag that refers to a string in a name table.
17/// During parsing, encountered strings are inserted into the name table and
18/// only the corresponding tag is kept in the token. Names which have their most
19/// significant bit set represent case sensitive names, such as for extended
20/// identifiers.
21#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub struct Name(pub u32);
23
24impl Name {
25    /// Check if the name is case sensitive.
26    pub fn is_case_sensitive(&self) -> bool {
27        self.0 & 1 == 1
28    }
29
30    /// Return the string representation of this name.
31    pub fn as_str(self) -> RcStr {
32        get_name_table().get(self)
33    }
34}
35
36impl fmt::Debug for Name {
37    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38        write!(f, "{}({})", self, self.0)
39    }
40}
41
42impl fmt::Display for Name {
43    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44        fmt::Display::fmt(&self.as_str(), f)
45    }
46}
47
48// impl Encodable for Name {
49//     fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
50//         s.emit_bool(self.is_case_sensitive())?;
51//         s.emit_str(self.as_str().borrow())?;
52//         Ok(())
53//     }
54// }
55
56// impl Decodable for Name {
57//     fn decode<S: Decoder>(s: &mut S) -> Result<Name, S::Error> {
58//         let case = s.read_bool()?;
59//         let name = s.read_str()?;
60//         Ok(get_name_table().intern(&name, case))
61//     }
62// }
63
64impl Into<String> for Name {
65    fn into(self) -> String {
66        self.as_str().into()
67    }
68}
69
70impl<'a> From<&'a str> for Name {
71    fn from(s: &'a str) -> Name {
72        get_name_table().intern(s, true)
73    }
74}
75
76/// A reference-counted string that acts like a regular str slice, hiding the
77/// fact that it is wrapped in Rc<>.
78#[derive(Clone, PartialEq, Hash, PartialOrd)]
79pub struct RcStr(Rc<String>);
80
81impl RcStr {
82    /// Create a new ref-counted string which is a copy of `value`.
83    pub fn new(value: &str) -> RcStr {
84        RcStr(Rc::new(value.to_string()))
85    }
86
87    /// Create a new ref-counted string that contains `value`, without
88    /// allocating any new storage.
89    pub fn from(value: String) -> RcStr {
90        RcStr(Rc::new(value))
91    }
92}
93
94impl Eq for RcStr {}
95
96impl Ord for RcStr {
97    fn cmp(&self, other: &RcStr) -> Ordering {
98        self[..].cmp(&other[..])
99    }
100}
101
102impl fmt::Debug for RcStr {
103    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104        self[..].fmt(f)
105    }
106}
107
108impl fmt::Display for RcStr {
109    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110        self[..].fmt(f)
111    }
112}
113
114impl Borrow<str> for RcStr {
115    fn borrow(&self) -> &str {
116        &self.0[..]
117    }
118}
119
120impl Deref for RcStr {
121    type Target = str;
122    fn deref(&self) -> &str {
123        &self.0[..]
124    }
125}
126
127impl Into<String> for RcStr {
128    fn into(self) -> String {
129        (*self.0).clone()
130    }
131}
132
133impl AsRef<str> for RcStr {
134    fn as_ref(&self) -> &str {
135        self.0.as_ref()
136    }
137}
138
139/// A lookup table of names. Internalizes strings either in a case sensitive or
140/// case insensitive way. Allows for bidirectional lookup, i.e. by string or by
141/// assigned name.
142pub struct NameTable {
143    map: RefCell<HashMap<RcStr, Name>>,
144    vect: RefCell<Vec<RcStr>>,
145}
146
147impl NameTable {
148    /// Create a new empty name table.
149    pub fn new() -> NameTable {
150        NameTable {
151            map: RefCell::new(HashMap::new()),
152            vect: RefCell::new(Vec::new()),
153        }
154    }
155
156    /// Obtain a name for a string. This either inserts the string into the
157    /// table and returns the new name, or returns the existing name if the
158    /// string already exists in the table.
159    pub fn intern(&self, value: &str, case_sensitive: bool) -> Name {
160        let mut map = self.map.borrow_mut();
161        if let Some(&idx) = map.get(value) {
162            return idx;
163        }
164
165        // Since the name is not present in the table yet, we allocate a new idx
166        // for it. Also, if it is a case-insensitive name, we insert both its
167        // original form as well as its lowercase form into the lookup table.
168        let mut vect = self.vect.borrow_mut();
169        if case_sensitive {
170            let new_idx = Name((vect.len() as u32) << 1 | 1);
171            let v = RcStr::new(value);
172            map.insert(v.clone(), new_idx);
173            vect.push(v);
174            new_idx
175        } else {
176            let new_idx = Name((vect.len() as u32) << 1 | 0);
177            let lower = value.to_lowercase();
178            if let Some(&idx) = map.get(lower.as_str()) {
179                return idx;
180            }
181            let v = RcStr::new(value);
182            map.insert(RcStr::from(lower), new_idx);
183            map.insert(v.clone(), new_idx);
184            vect.push(v);
185            new_idx
186        }
187    }
188
189    /// Retrieve the string given a name tag.
190    pub fn get(&self, idx: Name) -> RcStr {
191        (*self.vect.borrow())[(idx.0 >> 1) as usize].clone()
192    }
193
194    /// Try to find a string.
195    pub fn find<Q: ?Sized>(&self, value: &Q) -> Option<Name>
196    where
197        RcStr: Borrow<Q>,
198        Q: Eq + Hash,
199    {
200        (*self.map.borrow()).get(value).map(|v| *v)
201    }
202}
203
204/// Get this thread's current name table.
205pub fn get_name_table() -> Rc<NameTable> {
206    thread_local!(static TBL: Rc<NameTable> = {
207        let nt = NameTable::new();
208        // token::prefill_name_table(&mut nt);
209        Rc::new(nt)
210    });
211    TBL.with(|x| x.clone())
212}