1use rustc_hash::FxHashMap as HashMap;
8
9use crate::{
10 lambda::runnable::RuntimeError,
11 types::{
12 lambda::vm_instructions::instruction_set::VMInstructionPackage, object::OnionStaticObject,
13 },
14 utils::format_object_summary,
15};
16
17#[derive(Clone, Debug)]
21pub struct Frame {
22 variables: HashMap<usize, OnionStaticObject>,
24 stack: Vec<OnionStaticObject>,
26}
27
28impl Frame {
29 pub fn new() -> Self {
30 Frame {
31 variables: HashMap::default(),
32 stack: Vec::new(),
33 }
34 }
35 #[inline(always)]
36 pub fn get_stack(&self) -> &Vec<OnionStaticObject> {
37 &self.stack
38 }
39 #[inline(always)]
40 pub fn get_stack_mut(&mut self) -> &mut Vec<OnionStaticObject> {
41 &mut self.stack
42 }
43
44 pub fn format_context(&self, package: &VMInstructionPackage) -> String {
45 let mut parts = Vec::new();
46 let string_pool = package.get_string_pool();
47
48 if self.variables.is_empty() {
50 parts.push(" - Variables: (none)".to_string());
51 } else {
52 parts.push(" - Variables:".to_string());
53 for (id, value) in &self.variables {
54 let var_name = string_pool
56 .get(*id)
57 .map(|s| s.as_ref())
58 .unwrap_or("<Unknown Var>");
59
60 let value_summary = format_object_summary(value.weak());
61 parts.push(format!(" - {}: {}", var_name, value_summary));
62 }
63 }
64
65 if self.stack.is_empty() {
67 parts.push(" - Operand Stack: (empty)".to_string());
68 } else {
69 parts.push(format!(" - Operand Stack ({} items):", self.stack.len()));
70 for (i, value) in self.stack.iter().rev().enumerate() {
71 let value_summary = format_object_summary(value.weak());
73 parts.push(format!(" - [Top - {}]: {}", i, value_summary));
74 }
75 }
76
77 parts.join("\n")
78 }
79}
80
81#[derive(Clone)]
85pub struct Context {
86 frames: Vec<Frame>,
88}
89
90impl Context {
91 pub fn new() -> Self {
92 Context { frames: Vec::new() }
93 }
94
95 pub fn push_frame(&mut self, frame: Frame) {
96 self.frames.push(frame);
97 }
98
99 pub fn pop_frame(&mut self) -> Result<Frame, RuntimeError> {
100 match self.frames.pop() {
101 Some(frame) => Ok(frame),
102 None => Err(RuntimeError::DetailedError(
103 "Cannot pop frame from empty context".into(),
104 )),
105 }
106 }
107 pub fn concat_last_frame(&mut self) -> Result<(), RuntimeError> {
108 if self.frames.len() < 2 {
109 return Ok(());
110 }
111
112 let last_frame = self.frames.pop().unwrap();
113 let second_last_frame = self.frames.last_mut().unwrap();
114 second_last_frame.stack.extend(last_frame.stack);
115 Ok(())
116 }
117 pub fn clear_stack(&mut self) {
118 if self.frames.len() > 0 {
119 self.frames.last_mut().unwrap().stack.clear();
120 }
121 }
122
123 pub fn push_object(&mut self, object: OnionStaticObject) -> Result<(), RuntimeError> {
124 if self.frames.len() == 0 {
125 return Err(RuntimeError::DetailedError(
126 "Cannot push object to empty context".into(),
127 ));
128 }
129 self.frames.last_mut().unwrap().stack.push(object);
130 Ok(())
131 }
132
133 pub fn pop(&mut self) -> Result<OnionStaticObject, RuntimeError> {
134 if self.frames.len() == 0 {
135 return Err(RuntimeError::DetailedError(
136 "Cannot pop object from empty context".into(),
137 ));
138 }
139 let last_frame = self.frames.last_mut().unwrap();
140 if last_frame.get_stack().len() == 0 {
141 return Err(RuntimeError::DetailedError(
142 "Cannot pop object from empty stack".into(),
143 ));
144 }
145 let stack = last_frame.get_stack_mut();
146 Ok(stack.pop().unwrap())
147 }
148
149 pub fn discard_objects(&mut self, count: usize) -> Result<(), RuntimeError> {
150 if self.frames.len() == 0 {
151 return Err(RuntimeError::DetailedError(
152 "Cannot discard objects from empty context"
153 .to_string()
154 .into(),
155 ));
156 }
157 let last_frame = self.frames.last_mut().unwrap();
158 let stack = last_frame.get_stack_mut();
159 if stack.len() < count {
160 return Err(RuntimeError::DetailedError(
161 "Cannot discard more objects than available in stack"
162 .to_string()
163 .into(),
164 ));
165 }
166 stack.truncate(stack.len() - count);
167 Ok(())
168 }
169
170 pub fn discard_objects_offset(
171 &mut self,
172 offset: usize,
173 count: usize,
174 ) -> Result<(), RuntimeError> {
175 if self.frames.len() == 0 {
176 return Err(RuntimeError::DetailedError(
177 "Cannot discard objects from empty context"
178 .to_string()
179 .into(),
180 ));
181 }
182 let last_frame = self.frames.last_mut().unwrap();
183 let stack = last_frame.get_stack_mut();
184 if stack.len() < offset + count {
185 return Err(RuntimeError::DetailedError(
186 "Cannot discard more objects than available in stack"
187 .to_string()
188 .into(),
189 ));
190 }
191
192 let remove_start = stack.len() - offset - count;
194 let remove_end = stack.len() - offset;
195 stack.drain(remove_start..remove_end);
196 Ok(())
197 }
198
199 pub fn get_object_rev(&self, idx: usize) -> Result<&OnionStaticObject, RuntimeError> {
200 if self.frames.len() == 0 {
201 return Err(RuntimeError::DetailedError(
202 "Cannot get object from empty context".into(),
203 ));
204 }
205 let last_frame = self.frames.last().unwrap();
206 if last_frame.get_stack().len() <= idx {
207 return Err(RuntimeError::DetailedError(
208 "Cannot get object from empty stack".into(),
209 ));
210 }
211 let stack = last_frame.get_stack();
212 match stack.get(stack.len() - 1 - idx) {
213 None => Err(RuntimeError::DetailedError("Index out of bounds".into())),
214 Some(o) => Ok(o),
215 }
216 }
217
218 pub fn get_object_rev_mut(
219 &mut self,
220 idx: usize,
221 ) -> Result<&mut OnionStaticObject, RuntimeError> {
222 if self.frames.len() == 0 {
223 return Err(RuntimeError::DetailedError(
224 "Cannot get object from empty context".into(),
225 ));
226 }
227 let last_frame = self.frames.last_mut().unwrap();
228 if last_frame.get_stack().len() <= idx {
229 return Err(RuntimeError::DetailedError(
230 "Cannot get object from empty stack".into(),
231 ));
232 }
233 let stack = last_frame.get_stack_mut();
234 let idx = stack.len() - 1 - idx;
235 match stack.get_mut(idx) {
236 None => Err(RuntimeError::DetailedError("Index out of bounds".into())),
237 Some(o) => Ok(o),
238 }
239 }
240
241 #[inline(always)]
242 pub fn let_variable(
243 &mut self,
244 name: usize,
245 value: OnionStaticObject,
246 ) -> Result<(), RuntimeError> {
247 if self.frames.len() == 0 {
248 return Err(RuntimeError::InvalidOperation(
249 "Cannot let variable in empty context".into(),
250 ));
251 }
252
253 let last_frame = self.frames.last_mut().unwrap();
254 last_frame.variables.insert(name, value);
255 Ok(())
256 }
257
258 #[inline(always)]
259 pub fn get_variable(&self, name: usize) -> Option<&OnionStaticObject> {
260 if self.frames.len() == 0 {
261 return None;
262 } for frame in self.frames.iter().rev() {
264 if let Some(value) = frame.variables.get(&name) {
265 return Some(value);
266 }
267 }
268 None
269 }
270
271 fn _debug_print(&self) {
272 println!("Context Debug Print:");
273 for (i, frame) in self.frames.iter().enumerate() {
274 println!("Frame {}: {:?}", i, frame);
275 }
276 }
277
278 pub fn get_variable_mut(&mut self, name: usize) -> Option<&mut OnionStaticObject> {
279 if self.frames.len() == 0 {
280 return None;
281 } for frame in self.frames.iter_mut().rev() {
283 if let Some(value) = frame.variables.get_mut(&name) {
284 return Some(value);
285 }
286 }
287 None
288 }
289
290 pub fn swap(&mut self, idx1: usize, idx2: usize) -> Result<(), RuntimeError> {
291 if self.frames.len() == 0 {
292 return Err(RuntimeError::DetailedError(
293 "Cannot swap objects in empty context".into(),
294 ));
295 }
296 let last_frame = self.frames.last_mut().unwrap();
297 let stack = last_frame.get_stack_mut();
298 if stack.len() <= idx1 || stack.len() <= idx2 {
299 return Err(RuntimeError::DetailedError(
300 "Cannot swap objects in empty stack".into(),
301 ));
302 }
303 let len = stack.len();
304 stack.swap(len - 1 - idx1, len - 1 - idx2);
305 Ok(())
306 }
307
308 pub fn get_current_stack_mut(&mut self) -> Result<&mut Vec<OnionStaticObject>, RuntimeError> {
309 if self.frames.len() == 0 {
310 return Err(RuntimeError::DetailedError(
311 "Cannot get stack from empty context".into(),
312 ));
313 }
314 let last_frame = self.frames.last_mut().unwrap();
315 Ok(last_frame.get_stack_mut())
316 }
317
318 #[inline(always)]
319 pub fn push_to_stack(stack: &mut Vec<OnionStaticObject>, object: OnionStaticObject) {
320 stack.push(object);
321 }
322
323 pub fn pop_from_stack(
324 stack: &mut Vec<OnionStaticObject>,
325 ) -> Result<OnionStaticObject, RuntimeError> {
326 if stack.is_empty() {
327 return Err(RuntimeError::DetailedError(
328 "Cannot pop from empty stack".into(),
329 ));
330 }
331 Ok(stack.pop().unwrap())
332 }
333
334 #[inline(always)]
335 pub fn discard_from_stack(
336 stack: &mut Vec<OnionStaticObject>,
337 count: usize,
338 ) -> Result<(), RuntimeError> {
339 if stack.len() < count {
340 return Err(RuntimeError::DetailedError(
341 "Cannot discard more objects than available in stack"
342 .to_string()
343 .into(),
344 ));
345 }
346 stack.truncate(stack.len() - count);
347 Ok(())
348 }
349
350 #[inline(always)]
351 pub fn get_object_from_stack(
352 stack: &Vec<OnionStaticObject>,
353 idx: usize,
354 ) -> Result<&OnionStaticObject, RuntimeError> {
355 if stack.len() <= idx {
356 return Err(RuntimeError::DetailedError("Index out of bounds".into()));
357 }
358 Ok(&stack[stack.len() - 1 - idx])
359 }
360
361 #[inline(always)]
362 pub fn get_objects_slice(
363 stack: &Vec<OnionStaticObject>,
364 idx: usize,
365 len: usize,
366 ) -> Result<&[OnionStaticObject], RuntimeError> {
367 if len == 0 {
369 return Ok(&[]);
370 }
371
372 if stack.len() <= idx {
373 return Err(RuntimeError::DetailedError("Index out of bounds".into()));
374 }
375
376 if idx + len > stack.len() {
377 return Err(RuntimeError::DetailedError(
378 "Slice length exceeds available elements".into(),
379 ));
380 }
381
382 let start = stack.len() - idx - len;
383 let end = stack.len() - idx;
384 Ok(&stack[start..end])
385 }
386 pub fn replace_last_object(stack: &mut Vec<OnionStaticObject>, object: OnionStaticObject) {
387 let last_index = stack.len() - 1;
388 stack[last_index] = object;
389 }
390
391 pub fn format_context(&self, package: &VMInstructionPackage) -> String {
392 if self.frames.is_empty() {
393 return "Context: (No active frames)".to_string();
394 }
395
396 let mut parts = Vec::new();
397 parts.push(format!("Call Stack ({} frames):", self.frames.len()));
398
399 for (i, frame) in self.frames.iter().rev().enumerate() {
401 parts.push(format!("--- Frame #{} (most recent) ---", i));
404
405 let frame_context = frame.format_context(package);
407 parts.push(frame_context);
408 }
409
410 parts.join("\n")
411 }
412}