exp_rs/eval/
context_stack.rs1use crate::Real;
7use crate::context::EvalContext;
8use crate::error::ExprError;
9use crate::types::HString;
10use alloc::rc::Rc;
11use alloc::vec::Vec;
12use heapless::FnvIndexMap;
13
14const MAX_CONTEXTS: usize = 128;
16
17pub struct ContextStack {
19 contexts: Vec<Option<ContextWrapper>>,
21 next_id: usize,
23 parent_map: FnvIndexMap<usize, Option<usize>, MAX_CONTEXTS>,
25}
26
27struct ContextWrapper {
29 context: Rc<EvalContext>,
31 #[allow(dead_code)]
33 is_owned: bool,
34}
35
36impl Default for ContextStack {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl ContextStack {
43 pub fn new() -> Self {
45 Self {
46 contexts: Vec::with_capacity(8),
47 next_id: 0,
48 parent_map: FnvIndexMap::new(),
49 }
50 }
51
52 pub fn clear(&mut self) {
54 self.contexts.clear();
55 self.next_id = 0;
56 self.parent_map.clear();
57 }
58
59 pub fn push_context(&mut self, ctx: Option<Rc<EvalContext>>) -> Result<usize, ExprError> {
61 let id = self.next_id;
62
63 if id >= MAX_CONTEXTS {
65 return Err(ExprError::CapacityExceeded("context stack"));
66 }
67
68 self.next_id += 1;
69
70 if self.contexts.len() <= id {
72 self.contexts.resize_with(id + 1, || None);
73 }
74
75 if let Some(ctx) = ctx {
77 self.contexts[id] = Some(ContextWrapper {
78 context: ctx,
79 is_owned: false,
80 });
81 } else {
82 self.contexts[id] = Some(ContextWrapper {
84 context: Rc::new(EvalContext::default()),
85 is_owned: true,
86 });
87 }
88
89 self.parent_map
91 .insert(id, None)
92 .map_err(|_| ExprError::CapacityExceeded("parent map"))?;
93
94 Ok(id)
95 }
96
97 pub fn push_context_with_parent(
99 &mut self,
100 ctx: EvalContext,
101 parent_id: usize,
102 ) -> Result<usize, ExprError> {
103 let id = self.next_id;
104
105 if id >= MAX_CONTEXTS {
107 return Err(ExprError::CapacityExceeded("context stack"));
108 }
109
110 self.next_id += 1;
111
112 if self.contexts.len() <= id {
114 self.contexts.resize_with(id + 1, || None);
115 }
116
117 self.contexts[id] = Some(ContextWrapper {
119 context: Rc::new(ctx),
120 is_owned: true,
121 });
122
123 self.parent_map
125 .insert(id, Some(parent_id))
126 .map_err(|_| ExprError::CapacityExceeded("parent map"))?;
127
128 Ok(id)
129 }
130
131 pub fn get_context(&self, id: usize) -> Option<&Rc<EvalContext>> {
133 self.contexts
134 .get(id)
135 .and_then(|opt| opt.as_ref())
136 .map(|wrapper| &wrapper.context)
137 }
138
139 pub fn lookup_variable(&self, ctx_id: usize, name: &HString) -> Option<Real> {
141 let mut current_id = Some(ctx_id);
142 let mut visited_contexts = Vec::new();
143
144 while let Some(id) = current_id {
145 if let Some(ctx) = self.get_context(id) {
146 if let Some(&value) = ctx.variables.get(name) {
151 return Some(value);
152 }
153
154 if let Some(&value) = ctx.constants.get(name) {
156 return Some(value);
157 }
158
159 visited_contexts.push(id);
161 if let Some(ref parent_ctx) = ctx.parent {
162 return self.lookup_in_context_chain(
164 parent_ctx.as_ref(),
165 name,
166 &visited_contexts,
167 );
168 }
169 }
170
171 current_id = self.parent_map.get(&id).and_then(|&parent| parent);
173 }
174
175 None
176 }
177
178 fn lookup_in_context_chain(
180 &self,
181 ctx: &EvalContext,
182 name: &HString,
183 visited: &[usize],
184 ) -> Option<Real> {
185 if let Some(&value) = ctx.variables.get(name) {
187 return Some(value);
188 }
189
190 if let Some(&value) = ctx.constants.get(name) {
192 return Some(value);
193 }
194
195 if let Some(ref parent) = ctx.parent {
197 return self.lookup_in_context_chain(parent.as_ref(), name, visited);
198 }
199
200 None
201 }
202
203 pub fn get_parent_id(&self, ctx_id: usize) -> Option<usize> {
205 self.parent_map.get(&ctx_id).and_then(|&parent| parent)
206 }
207}