1use rustc_hash::FxHashMap as HashMap;
2use serde_json::{Map, Value};
3
4use crate::{lambda::runnable::RuntimeError, types::object::OnionStaticObject};
5
6#[derive(Clone, Debug)]
7pub struct Frame {
8 pub variables: HashMap<usize, OnionStaticObject>,
9 pub stack: Vec<OnionStaticObject>,
10}
11
12impl Frame {
13 #[inline(always)]
14 pub fn get_stack(&self) -> &Vec<OnionStaticObject> {
15 &self.stack
16 }
17 #[inline(always)]
18 pub fn get_stack_mut(&mut self) -> &mut Vec<OnionStaticObject> {
19 &mut self.stack
20 }
21
22 pub fn format_context(&self) -> Value {
23 let mut frame_obj = Map::new();
24
25 let mut variables = Map::new();
27 for (var_name, var_value) in &self.variables {
28 let value_str = var_value
29 .weak()
30 .to_string(&vec![])
31 .unwrap_or("Unknown value".into());
32 variables.insert(var_name.to_string(), Value::String(value_str));
33 }
34 frame_obj.insert("variables".to_string(), Value::Object(variables));
35
36 let stack_values: Vec<Value> = self
38 .stack
39 .iter()
40 .map(|obj| {
41 let obj_str = obj
42 .weak()
43 .to_string(&vec![])
44 .unwrap_or("Unknown object".into());
45 Value::String(obj_str)
46 })
47 .collect();
48 frame_obj.insert("stack".to_string(), Value::Array(stack_values));
49
50 Value::Object(frame_obj)
51 }
52}
53
54#[derive(Clone)]
55pub struct Context {
56 pub(crate) frames: Vec<Frame>,
57}
58
59impl Context {
60 pub fn new() -> Self {
61 Context { frames: Vec::new() }
62 }
63
64 pub fn push_frame(&mut self, frame: Frame) {
65 self.frames.push(frame);
66 }
67
68 pub fn pop_frame(&mut self) -> Result<Frame, RuntimeError> {
69 match self.frames.pop() {
70 Some(frame) => Ok(frame),
71 None => Err(RuntimeError::DetailedError(
72 "Cannot pop frame from empty context".to_string().into(),
73 )),
74 }
75 }
76 pub fn concat_last_frame(&mut self) -> Result<(), RuntimeError> {
77 if self.frames.len() < 2 {
78 return Ok(());
79 }
80
81 let last_frame = self.frames.pop().unwrap();
82 let second_last_frame = self.frames.last_mut().unwrap();
83 second_last_frame.stack.extend(last_frame.stack);
84 Ok(())
85 }
86 pub fn clear_stack(&mut self) {
87 if self.frames.len() > 0 {
88 self.frames.last_mut().unwrap().stack.clear();
89 }
90 }
91
92 pub fn push_object(&mut self, object: OnionStaticObject) -> Result<(), RuntimeError> {
93 if self.frames.len() == 0 {
94 return Err(RuntimeError::DetailedError(
95 "Cannot push object to empty context".to_string().into(),
96 ));
97 }
98 self.frames.last_mut().unwrap().stack.push(object);
99 Ok(())
100 }
101
102 pub fn pop(&mut self) -> Result<OnionStaticObject, RuntimeError> {
103 if self.frames.len() == 0 {
104 return Err(RuntimeError::DetailedError(
105 "Cannot pop object from empty context".to_string().into(),
106 ));
107 }
108 let last_frame = self.frames.last_mut().unwrap();
109 if last_frame.get_stack().len() == 0 {
110 return Err(RuntimeError::DetailedError(
111 "Cannot pop object from empty stack".to_string().into(),
112 ));
113 }
114 let stack = last_frame.get_stack_mut();
115 Ok(stack.pop().unwrap())
116 }
117
118 pub fn discard_objects(&mut self, count: usize) -> Result<(), RuntimeError> {
119 if self.frames.len() == 0 {
120 return Err(RuntimeError::DetailedError(
121 "Cannot discard objects from empty context"
122 .to_string()
123 .into(),
124 ));
125 }
126 let last_frame = self.frames.last_mut().unwrap();
127 let stack = last_frame.get_stack_mut();
128 if stack.len() < count {
129 return Err(RuntimeError::DetailedError(
130 "Cannot discard more objects than available in stack"
131 .to_string()
132 .into(),
133 ));
134 }
135 stack.truncate(stack.len() - count);
139 Ok(())
140 }
141
142 pub fn discard_objects_offset(
143 &mut self,
144 offset: usize,
145 count: usize,
146 ) -> Result<(), RuntimeError> {
147 if self.frames.len() == 0 {
148 return Err(RuntimeError::DetailedError(
149 "Cannot discard objects from empty context"
150 .to_string()
151 .into(),
152 ));
153 }
154 let last_frame = self.frames.last_mut().unwrap();
155 let stack = last_frame.get_stack_mut();
156 if stack.len() < offset + count {
157 return Err(RuntimeError::DetailedError(
158 "Cannot discard more objects than available in stack"
159 .to_string()
160 .into(),
161 ));
162 }
163
164 let remove_start = stack.len() - offset - count;
166 let remove_end = stack.len() - offset;
167 stack.drain(remove_start..remove_end);
168 Ok(())
169 }
170
171 pub fn get_object_rev(&self, idx: usize) -> Result<&OnionStaticObject, RuntimeError> {
172 if self.frames.len() == 0 {
173 return Err(RuntimeError::DetailedError(
174 "Cannot get object from empty context".to_string().into(),
175 ));
176 }
177 let last_frame = self.frames.last().unwrap();
178 if last_frame.get_stack().len() <= idx {
179 return Err(RuntimeError::DetailedError(
180 "Cannot get object from empty stack".to_string().into(),
181 ));
182 }
183 let stack = last_frame.get_stack();
184 match stack.get(stack.len() - 1 - idx) {
185 None => Err(RuntimeError::DetailedError(
186 "Index out of bounds".to_string().into(),
187 )),
188 Some(o) => Ok(o),
189 }
190 }
191
192 pub fn get_object_rev_mut(
193 &mut self,
194 idx: usize,
195 ) -> Result<&mut OnionStaticObject, RuntimeError> {
196 if self.frames.len() == 0 {
197 return Err(RuntimeError::DetailedError(
198 "Cannot get object from empty context".to_string().into(),
199 ));
200 }
201 let last_frame = self.frames.last_mut().unwrap();
202 if last_frame.get_stack().len() <= idx {
203 return Err(RuntimeError::DetailedError(
204 "Cannot get object from empty stack".to_string().into(),
205 ));
206 }
207 let stack = last_frame.get_stack_mut();
208 let idx = stack.len() - 1 - idx;
209 match stack.get_mut(idx) {
210 None => Err(RuntimeError::DetailedError(
211 "Index out of bounds".to_string().into(),
212 )),
213 Some(o) => Ok(o),
214 }
215 }
216
217 #[inline(always)]
238 pub fn let_variable(
239 &mut self,
240 name: usize,
241 value: OnionStaticObject,
242 ) -> Result<(), RuntimeError> {
243 if self.frames.len() == 0 {
244 return Err(RuntimeError::InvalidOperation(
245 "Cannot let variable in empty context".to_string().into(),
246 ));
247 }
248
249 let last_frame = self.frames.last_mut().unwrap();
250 last_frame.variables.insert(name, value);
251 Ok(())
252 }
253
254 #[inline(always)]
279 pub fn get_variable(&self, name: usize) -> Option<&OnionStaticObject> {
280 if self.frames.len() == 0 {
281 return None;
282 } for frame in self.frames.iter().rev() {
284 if let Some(value) = frame.variables.get(&name) {
285 return Some(value);
286 }
287 }
288 None
289 }
290
291 fn _debug_print(&self) {
292 println!("Context Debug Print:");
293 for (i, frame) in self.frames.iter().enumerate() {
294 println!("Frame {}: {:?}", i, frame);
295 }
296 }
297
298 pub fn get_variable_mut(&mut self, name: usize) -> Option<&mut OnionStaticObject> {
326 if self.frames.len() == 0 {
327 return None;
328 } for frame in self.frames.iter_mut().rev() {
330 if let Some(value) = frame.variables.get_mut(&name) {
331 return Some(value);
332 }
333 }
334 None
335 }
336
337 pub fn swap(&mut self, idx1: usize, idx2: usize) -> Result<(), RuntimeError> {
338 if self.frames.len() == 0 {
339 return Err(RuntimeError::DetailedError(
340 "Cannot swap objects in empty context".to_string().into(),
341 ));
342 }
343 let last_frame = self.frames.last_mut().unwrap();
344 let stack = last_frame.get_stack_mut();
345 if stack.len() <= idx1 || stack.len() <= idx2 {
346 return Err(RuntimeError::DetailedError(
347 "Cannot swap objects in empty stack".to_string().into(),
348 ));
349 }
350 let len = stack.len();
351 stack.swap(len - 1 - idx1, len - 1 - idx2);
352 Ok(())
353 }
354
355 pub fn get_current_stack_mut(&mut self) -> Result<&mut Vec<OnionStaticObject>, RuntimeError> {
356 if self.frames.len() == 0 {
357 return Err(RuntimeError::DetailedError(
358 "Cannot get stack from empty context".to_string().into(),
359 ));
360 }
361 let last_frame = self.frames.last_mut().unwrap();
362 Ok(last_frame.get_stack_mut())
363 }
364
365 #[inline(always)]
366 pub fn push_to_stack(stack: &mut Vec<OnionStaticObject>, object: OnionStaticObject) {
367 stack.push(object);
368 }
369
370 pub fn pop_from_stack(
371 stack: &mut Vec<OnionStaticObject>,
372 ) -> Result<OnionStaticObject, RuntimeError> {
373 if stack.is_empty() {
374 return Err(RuntimeError::DetailedError(
375 "Cannot pop from empty stack".to_string().into(),
376 ));
377 }
378 Ok(stack.pop().unwrap())
379 }
380
381 #[inline(always)]
382 pub fn discard_from_stack(
383 stack: &mut Vec<OnionStaticObject>,
384 count: usize,
385 ) -> Result<(), RuntimeError> {
386 if stack.len() < count {
387 return Err(RuntimeError::DetailedError(
388 "Cannot discard more objects than available in stack"
389 .to_string()
390 .into(),
391 ));
392 }
393 stack.truncate(stack.len() - count);
394 Ok(())
395 }
396
397 #[inline(always)]
398 pub fn get_object_from_stack(
399 stack: &Vec<OnionStaticObject>,
400 idx: usize,
401 ) -> Result<&OnionStaticObject, RuntimeError> {
402 if stack.len() <= idx {
403 return Err(RuntimeError::DetailedError(
404 "Index out of bounds".to_string().into(),
405 ));
406 }
407 Ok(&stack[stack.len() - 1 - idx])
408 }
409
410 pub fn replace_last_object(stack: &mut Vec<OnionStaticObject>, object: OnionStaticObject) {
411 let last_index = stack.len() - 1;
412 stack[last_index] = object;
413 }
414}
415
416impl Context {
417 pub fn format_to_json(&self) -> Value {
418 let mut frames = Map::new();
419
420 for (i, frame) in self.frames.iter().enumerate() {
421 let mut frame_obj = Map::new();
422
423 let mut variables = Map::new();
425 for (var_name, var_value) in &frame.variables {
426 let value_str = var_value
427 .weak()
428 .to_string(&vec![])
429 .unwrap_or("Unknown value".into());
430
431 variables.insert(var_name.to_string(), Value::String(value_str));
432 }
433 frame_obj.insert("variables".to_string(), Value::Object(variables));
434
435 let stack_values: Vec<Value> = frame
437 .stack
438 .iter()
439 .map(|obj| {
440 let obj_str = obj
441 .weak()
442 .to_string(&vec![])
443 .unwrap_or("Unknown value".into());
444 Value::String(obj_str)
445 })
446 .collect();
447 frame_obj.insert("stack".to_string(), Value::Array(stack_values));
448
449 frames.insert(format!("frame_{}", i), Value::Object(frame_obj));
450 }
451
452 Value::Object(frames)
453 }
454}