1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use super::errors::ScopeError;
use crate::identifier::Identifier;
use crate::types::Type;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct Scope(Vec<HashMap<Identifier, Type>>);
impl Scope {
pub fn new() -> Scope {
Scope(vec![])
}
pub fn enter(&mut self) {
self.0.push(HashMap::new())
}
pub fn leave(&mut self) {
if !self.0.is_empty() {
self.0.pop();
} else {
panic!("scope leave: scope stack is empty");
}
}
pub fn declare(&mut self, id: Identifier, t: Type) -> Result<(), ScopeError> {
if self.lookup(&id) == None {
if let Some(last) = self.0.last_mut() {
last.insert(id, t);
Ok(())
} else {
panic!("scope declare: scope stack is empty");
}
} else {
Err(ScopeError)
}
}
pub fn lookup(&self, id: &Identifier) -> Option<Type> {
for table in self.0.clone().into_iter().rev() {
if let Some(t) = table.get(id) {
return Some(t.clone());
}
}
None
}
}
#[cfg(test)]
mod test {
use crate::identifier::Identifier;
use crate::types::Type;
use super::Scope;
#[test]
fn declare_then_lookup_succeeds() {
let id = Identifier::Local("test_id".to_string());
let t = Type::Integer;
let mut scope = Scope::new();
scope.enter();
scope
.declare(id.clone(), t.clone())
.expect("declare failed");
let t_ = scope.lookup(&id).expect("lookup failed");
assert_eq!(t, t_, "lookup returned wrong type");
}
#[test]
fn gone_after_leave() {
let id = Identifier::Local("test_id".to_string());
let t = Type::Integer;
let mut scope = Scope::new();
scope.enter();
scope.enter();
scope
.declare(id.clone(), t.clone())
.expect("declare failed");
scope.leave();
assert_eq!(None, scope.lookup(&id));
}
#[test]
fn still_there_after_enter_and_leave() {
let id = Identifier::Local("test_id".to_string());
let id2 = Identifier::Local("test_id2".to_string());
let t = Type::Integer;
let t2 = Type::String;
let mut scope = Scope::new();
scope.enter();
scope
.declare(id.clone(), t.clone())
.expect("declare id failed");
scope.enter();
scope.declare(id2, t2).expect("declare id2 failed");
scope.leave();
let t_ = scope.lookup(&id).expect("lookup failed");
assert_eq!(t, t_, "lookup returned wrong type");
}
}