vyder_core/
environment.rs1use std::collections::HashMap;
2
3use crate::prelude::*;
4
5pub struct Environment {
6 scopes: Vec<HashMap<String, (ValueResult, bool)>>,
7}
8
9impl Environment {
10 pub fn new() -> Self {
11 Self {
12 scopes: vec![HashMap::new()],
13 }
14 }
15
16 pub fn define(&mut self, identifier: &str, value: ValueResult, is_const: bool) {
17 self.scopes
18 .last_mut()
19 .expect("should never be able to remove the root scope")
20 .insert(identifier.to_string(), (value, is_const));
21 }
22
23 pub fn assign(&mut self, identifier: &str, value: ValueResult, span: &Span) -> Result<()> {
24 let mut scope_index = self.scopes.len() - 1;
25
26 loop {
27 let scope = self
28 .scopes
29 .get_mut(scope_index)
30 .expect("scope_index should never be invalid");
31
32 match scope.get(identifier) {
33 Some((_, false)) => {
34 scope.insert(identifier.to_string(), (value, false));
35 return Ok(());
36 }
37 Some((_, true)) => {
38 return Err(Error::new(
39 ErrorType::CannotAssignToConstant(identifier.to_string()),
40 span.clone(),
41 ))
42 }
43 None => {}
44 };
45
46 if scope_index == 0 {
47 return Err(Error::new(
48 ErrorType::UndefinedVariable(identifier.to_string()),
49 span.clone(),
50 ));
51 }
52
53 scope_index -= 1;
54 }
55 }
56
57 pub fn get(&self, identifier: &str, span: &Span) -> Result<&ValueResult> {
58 let mut scope_index = self.scopes.len() - 1;
59
60 loop {
61 let scope = self.scopes.get(scope_index).unwrap();
62
63 if let Some((value, _)) = scope.get(identifier) {
64 return Ok(value);
65 }
66
67 if scope_index == 0 {
68 return Err(Error::new(
69 ErrorType::UndefinedVariable(identifier.to_string()),
70 span.clone(),
71 ));
72 }
73
74 scope_index -= 1;
75 }
76 }
77
78 pub fn enter_scope(&mut self) {
79 self.scopes.push(HashMap::new());
80 }
81
82 pub fn exit_scope(&mut self) {
83 assert!(self.scopes.len() > 1, "tried to remove the root scope");
84
85 self.scopes.pop();
86 }
87}
88
89impl Default for Environment {
90 fn default() -> Self {
91 Self::new()
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn environments_test() {
101 let mut environment = Environment::default();
102
103 environment.define(
104 "foo",
105 ValueResult::Ok(Value::new_value(
106 values::Bool { value: true }.into(),
107 Span::new((1, 1, 0), "test"),
108 )),
109 false,
110 );
111
112 assert!(environment
113 .get("bar", &Span::new((1, 1, 0), "test"))
114 .is_err());
115
116 environment.enter_scope();
117
118 environment.define(
119 "foo",
120 ValueResult::Ok(Value::new_value(
121 values::Bool { value: false }.into(),
122 Span::new((1, 1, 0), "test"),
123 )),
124 false,
125 );
126
127 assert!(
128 !environment
129 .get("foo", &Span::new((1, 1, 0), "test"))
130 .unwrap()
131 .ignore_error()
132 .expect::<values::Bool>()
133 .unwrap()
134 .value
135 );
136
137 environment.exit_scope();
138
139 assert!(
140 environment
141 .get("foo", &Span::new((1, 1, 0), "test"))
142 .unwrap()
143 .ignore_error()
144 .expect::<values::Bool>()
145 .unwrap()
146 .value
147 );
148 }
149}