tree_sitter_graph/
variables.rs1use std::collections::hash_map::Entry::Occupied;
9use std::collections::hash_map::Entry::Vacant;
10use std::collections::HashMap;
11use thiserror::Error;
12
13use crate::graph::Value;
14use crate::Identifier;
15
16#[derive(Debug, Error)]
17pub enum VariableError {
18 #[error("Cannot assign immutable variable")]
19 CannotAssignImmutableVariable(String),
20 #[error("Variable already defined")]
21 VariableAlreadyDefined(String),
22 #[error("Undefined variable")]
23 UndefinedVariable(String),
24}
25
26pub(crate) trait Variables<V> {
28 fn get(&self, name: &Identifier) -> Option<&V>;
30}
31
32pub(crate) trait MutVariables<V>: Variables<V> {
33 fn add(&mut self, name: Identifier, value: V, mutable: bool) -> Result<(), VariableError>;
36
37 fn set(&mut self, name: Identifier, value: V) -> Result<(), VariableError>;
39}
40
41pub(crate) struct VariableMap<'a, V> {
43 context: Option<&'a mut dyn MutVariables<V>>,
44 values: HashMap<Identifier, Variable<V>>,
45}
46
47struct Variable<V> {
48 value: V,
49 mutable: bool,
50}
51
52impl<'a, V> VariableMap<'a, V> {
53 pub(crate) fn new() -> Self {
55 Self {
56 context: None,
57 values: HashMap::new(),
58 }
59 }
60
61 pub(crate) fn nested(context: &'a mut dyn MutVariables<V>) -> Self {
64 Self {
65 context: Some(context),
66 values: HashMap::new(),
67 }
68 }
69
70 pub(crate) fn clear(&mut self) {
72 self.values.clear();
73 }
74}
75
76impl<V> Variables<V> for VariableMap<'_, V> {
77 fn get(&self, name: &Identifier) -> Option<&V> {
78 self.values
79 .get(name)
80 .map(|v| &v.value)
81 .or_else(|| self.context.as_ref().map(|p| p.get(name)).flatten())
82 }
83}
84
85impl<V> MutVariables<V> for VariableMap<'_, V> {
86 fn add(&mut self, name: Identifier, value: V, mutable: bool) -> Result<(), VariableError> {
87 match self.values.entry(name) {
88 Vacant(v) => {
89 let variable = Variable { value, mutable };
90 v.insert(variable);
91 Ok(())
92 }
93 Occupied(o) => Err(VariableError::VariableAlreadyDefined(o.key().to_string())),
94 }
95 }
96
97 fn set(&mut self, name: Identifier, value: V) -> Result<(), VariableError> {
98 match self.values.entry(name) {
99 Vacant(v) => self
100 .context
101 .as_mut()
102 .map(|context| context.set(v.key().clone(), value))
103 .unwrap_or(Err(VariableError::UndefinedVariable(
104 v.into_key().to_string(),
105 ))),
106 Occupied(mut o) => {
107 let variable = o.get_mut();
108 if variable.mutable {
109 variable.value = value;
110 Ok(())
111 } else {
112 Err(VariableError::CannotAssignImmutableVariable(
113 o.key().to_string(),
114 ))
115 }
116 }
117 }
118 }
119}
120
121pub struct Globals<'a> {
123 context: Option<&'a dyn Variables<Value>>,
124 values: HashMap<Identifier, Value>,
125}
126
127impl<'a> Globals<'a> {
128 pub fn new() -> Self {
130 Self {
131 context: None,
132 values: HashMap::new(),
133 }
134 }
135
136 pub fn nested(context: &'a Globals<'a>) -> Self {
139 Self {
140 context: Some(context),
141 values: HashMap::new(),
142 }
143 }
144
145 pub fn add(&mut self, name: Identifier, value: Value) -> Result<(), VariableError> {
148 match self.values.entry(name) {
149 Vacant(v) => {
150 v.insert(value);
151 Ok(())
152 }
153 Occupied(o) => Err(VariableError::VariableAlreadyDefined(o.key().to_string())),
154 }
155 }
156
157 pub fn get(&self, name: &Identifier) -> Option<&Value> {
159 self.values
160 .get(name)
161 .or_else(|| self.context.as_ref().map(|p| p.get(name)).flatten())
162 }
163
164 pub fn remove(&mut self, name: &Identifier) {
166 self.values.remove(name);
167 }
168
169 pub fn is_empty(&self) -> bool {
170 self.values.is_empty()
171 }
172
173 pub fn iter<'b>(&'b self) -> Iter<'b> {
174 Iter(self.values.iter())
175 }
176
177 pub fn clear(&mut self) {
179 self.values.clear();
180 }
181}
182
183pub struct Iter<'a>(std::collections::hash_map::Iter<'a, Identifier, Value>);
184
185impl<'a> std::iter::Iterator for Iter<'a> {
186 type Item = (&'a Identifier, &'a Value);
187
188 fn next(&mut self) -> Option<Self::Item> {
189 self.0.next()
190 }
191}
192
193impl Variables<Value> for Globals<'_> {
194 fn get(&self, name: &Identifier) -> Option<&Value> {
195 self.get(name)
196 }
197}