rucc_base/scope.rs
1//! A map from a name to a value, kept in a stack of scopes.
2//!
3//! Design: `spec/06-lexer-and-parser.md` section 6.4.
4//!
5//! Two passes need this and they need the same thing from it. The parser holds one per C
6//! namespace to answer whether an identifier in a specifier list is a type name, which is the
7//! one real ambiguity in C's grammar. Semantic analysis holds one per namespace to resolve a
8//! use of a name to the declaration it refers to. Neither of them knows anything about the
9//! other's values, so what is shared is the scoping and not what is scoped, and it lives here
10//! rather than being written twice and drifting.
11//!
12//! Nothing in here knows what C is. It is a name, a value, and the rule that an inner binding
13//! hides an outer one until its scope closes.
14
15use std::collections::HashMap;
16
17use crate::intern::Symbol;
18
19/// One binding, and the scope it was made in.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21struct Binding<V> {
22 depth: u32,
23 value: V,
24}
25
26/// A map from a name to a value, in a stack of scopes.
27///
28/// A lookup has to find the innermost binding of a name, and a scope closing has to expose
29/// whatever that name meant outside it. Walking a stack of scopes would make every lookup cost
30/// the depth, and a compiler looks up every identifier it reads, so the shape is inverted: one
31/// map from name to the stack of bindings for that name, innermost last, plus a log of the
32/// names bound in each open scope so that closing one knows what to undo.
33#[derive(Debug)]
34pub struct ScopeMap<V> {
35 /// The bindings of each name, innermost last. An empty stack means the name is not bound,
36 /// and the entry is kept rather than removed so that the allocation is reused by the next
37 /// declaration of that name, which in a header is usually the same names again.
38 bindings: HashMap<Symbol, Vec<Binding<V>>>,
39 /// Every name bound in an open scope, in the order it was bound.
40 log: Vec<Symbol>,
41 /// Where each open scope starts in `log`. The file scope is not in here, which is what
42 /// makes it impossible to close.
43 marks: Vec<usize>,
44}
45
46impl<V> Default for ScopeMap<V> {
47 fn default() -> Self {
48 ScopeMap { bindings: HashMap::new(), log: Vec::new(), marks: Vec::new() }
49 }
50}
51
52impl<V: Copy> ScopeMap<V> {
53 /// An empty namespace, with the file scope open.
54 #[must_use]
55 pub fn new() -> Self {
56 ScopeMap::default()
57 }
58
59 /// How many scopes are open. The file scope counts, so this is never zero.
60 #[inline]
61 #[must_use]
62 pub fn depth(&self) -> u32 {
63 // The count is bounded by the nesting the parser accepted, which is capped long before
64 // this could overflow.
65 self.marks.len() as u32 + 1
66 }
67
68 /// Whether the only open scope is the file scope.
69 #[inline]
70 #[must_use]
71 pub fn at_file_scope(&self) -> bool {
72 self.marks.is_empty()
73 }
74
75 /// Opens a scope.
76 pub fn push(&mut self) {
77 self.marks.push(self.log.len());
78 }
79
80 /// Closes the innermost scope, exposing whatever its names meant outside it.
81 ///
82 /// # Panics
83 ///
84 /// Panics on closing the file scope, which nothing in C does and which would leave the
85 /// namespace unable to hold a declaration.
86 pub fn pop(&mut self) {
87 let mark = self.marks.pop().expect("the file scope is never closed");
88 while self.log.len() > mark {
89 let name = self.log.pop().expect("the log is longer than the mark");
90 if let Some(stack) = self.bindings.get_mut(&name) {
91 stack.pop();
92 }
93 }
94 }
95
96 /// Binds `name` in the innermost scope, and gives back what it was already bound to *in
97 /// that same scope*.
98 ///
99 /// A returned value is a redeclaration, which is the caller's to judge: `int x; int x;` is
100 /// fine at file scope and `typedef int T; T T;` is not, and neither decision belongs here.
101 /// Shadowing an outer binding is not a redeclaration and gives back [`None`].
102 pub fn declare(&mut self, name: Symbol, value: V) -> Option<V> {
103 let depth = self.depth();
104 let stack = self.bindings.entry(name).or_default();
105 match stack.last_mut() {
106 Some(top) if top.depth == depth => {
107 let was = top.value;
108 top.value = value;
109 Some(was)
110 }
111 _ => {
112 stack.push(Binding { depth, value });
113 self.log.push(name);
114 None
115 }
116 }
117 }
118
119 /// What `name` is bound to in the innermost scope that binds it.
120 #[must_use]
121 pub fn get(&self, name: Symbol) -> Option<V> {
122 Some(self.bindings.get(&name)?.last()?.value)
123 }
124
125 /// What `name` is bound to in the innermost scope alone, ignoring the ones outside it.
126 #[must_use]
127 pub fn get_here(&self, name: Symbol) -> Option<V> {
128 let depth = self.depth();
129 let top = self.bindings.get(&name)?.last()?;
130 (top.depth == depth).then_some(top.value)
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 /// A symbol the interner would have handed out. Nothing here reads a spelling.
139 const X: Symbol = Symbol::from_raw(1);
140
141 #[test]
142 fn only_the_innermost_scope_counts_as_here() {
143 let mut names = ScopeMap::new();
144 names.declare(X, 1);
145 names.push();
146 assert_eq!(names.get(X), Some(1));
147 assert_eq!(names.get_here(X), None);
148 assert_eq!(names.depth(), 2);
149 names.pop();
150 assert_eq!(names.get_here(X), Some(1));
151 }
152
153 #[test]
154 fn an_inner_binding_hides_an_outer_one_and_gives_it_back() {
155 let mut names = ScopeMap::new();
156 names.declare(X, 1);
157 names.push();
158 assert_eq!(names.declare(X, 2), None);
159 assert_eq!(names.get(X), Some(2));
160 names.pop();
161 assert_eq!(names.get(X), Some(1));
162 }
163
164 #[test]
165 fn a_second_binding_in_one_scope_is_a_redeclaration_and_says_what_it_was() {
166 let mut names = ScopeMap::new();
167 assert_eq!(names.declare(X, 1), None);
168 assert_eq!(names.declare(X, 2), Some(1));
169 assert_eq!(names.get(X), Some(2));
170 }
171
172 #[test]
173 fn closing_a_scope_leaves_nothing_behind() {
174 let mut names = ScopeMap::new();
175 assert!(names.at_file_scope());
176 for depth in 0..64 {
177 names.push();
178 names.declare(X, depth);
179 }
180 assert_eq!(names.depth(), 65);
181 for _ in 0..64 {
182 names.pop();
183 }
184 assert!(names.at_file_scope());
185 assert_eq!(names.get(X), None);
186 }
187
188 #[test]
189 #[should_panic(expected = "the file scope is never closed")]
190 fn the_outermost_scope_cannot_be_closed() {
191 ScopeMap::<u32>::new().pop();
192 }
193}