onion_vm/types/
lazy_set.rs

1use std::{collections::VecDeque, fmt::Debug};
2
3use arc_gc::{
4    arc::{GCArc, GCArcWeak},
5    gc::GC,
6    traceable::GCTraceable,
7};
8
9use crate::{
10    lambda::runnable::{Runnable, RuntimeError, StepResult},
11    onion_tuple,
12    types::lambda::launcher::OnionLambdaRunnableLauncher,
13    unwrap_step_result,
14};
15
16use super::{
17    lambda::definition::{LambdaBody, OnionLambdaDefinition},
18    object::{OnionObject, OnionObjectCell, OnionStaticObject},
19    tuple::OnionTuple,
20};
21
22pub struct OnionLazySet {
23    container: OnionObject,
24    filter: OnionObject,
25}
26
27impl GCTraceable<OnionObjectCell> for OnionLazySet {
28    fn collect(&self, queue: &mut VecDeque<GCArcWeak<OnionObjectCell>>) {
29        self.container.collect(queue);
30        self.filter.collect(queue);
31    }
32}
33
34impl Debug for OnionLazySet {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        write!(f, "LazySet({:?}, {:?})", self.container, self.filter)
37    }
38}
39
40impl OnionLazySet {
41    pub fn new(container: OnionObject, filter: OnionObject) -> Self {
42        OnionLazySet {
43            container: container.into(),
44            filter: filter.into(),
45        }
46    }
47
48    pub fn new_static(
49        container: &OnionStaticObject,
50        filter: &OnionStaticObject,
51    ) -> OnionStaticObject {
52        OnionObject::LazySet(
53            OnionLazySet {
54                container: container.weak().clone(),
55                filter: filter.weak().clone(),
56            }
57            .into(),
58        )
59        .stabilize()
60    }
61
62    #[inline(always)]
63    pub fn get_container(&self) -> &OnionObject {
64        &self.container
65    }
66
67    #[inline(always)]
68    pub fn get_filter(&self) -> &OnionObject {
69        &self.filter
70    }
71
72    pub fn upgrade(&self, collected: &mut Vec<GCArc<OnionObjectCell>>) {
73        self.container.upgrade(collected);
74        self.filter.upgrade(collected)
75    }
76
77    pub fn with_attribute<F, R>(&self, key: &OnionObject, f: &F) -> Result<R, RuntimeError>
78    where
79        F: Fn(&OnionObject) -> Result<R, RuntimeError>,
80    {
81        match key {
82            OnionObject::String(s) if s.as_str() == "container" => f(&self.container),
83            OnionObject::String(s) if s.as_str() == "filter" => f(&self.filter),
84            OnionObject::String(s) if s.as_str() == "collect" => {
85                let collector = OnionLazySetCollector {
86                    container: self.container.stabilize(),
87                    filter: self.filter.stabilize(),
88                    collected: Vec::new(),
89                    current_index: 0,
90                };
91                let collector = OnionLambdaDefinition::new_static(
92                    &onion_tuple!(),
93                    LambdaBody::NativeFunction(Box::new(collector)),
94                    None,
95                    None,
96                    "collector".to_string(),
97                );
98                // Keep the collector alive until after we use its weak reference
99                let result = {
100                    let collector_weak = collector.weak();
101                    f(collector_weak)
102                };
103                result
104            }
105            _ => Err(RuntimeError::InvalidOperation(
106                format!("Attribute '{:?}' not found in lazy set", key).into(),
107            )),
108        }
109    }
110}
111
112#[derive(Clone)]
113pub struct OnionLazySetCollector {
114    pub(crate) container: OnionStaticObject,
115    pub(crate) filter: OnionStaticObject,
116    pub(crate) collected: Vec<OnionStaticObject>,
117    pub(crate) current_index: usize,
118}
119
120impl Runnable for OnionLazySetCollector {
121    fn copy(&self) -> Box<dyn Runnable> {
122        Box::new(OnionLazySetCollector {
123            container: self.container.clone(),
124            filter: self.filter.clone(),
125            collected: self.collected.clone(),
126            current_index: self.current_index,
127        })
128    }
129
130    fn receive(
131        &mut self,
132        step_result: &StepResult,
133        _gc: &mut GC<OnionObjectCell>,
134    ) -> Result<(), RuntimeError> {
135        match step_result {
136            StepResult::Return(result) => {
137                match result.weak() {
138                    OnionObject::Boolean(true) => {
139                        match self.container.weak() {
140                            OnionObject::Tuple(tuple) => {
141                                // 如果是布尔值 true,表示需要收集当前元素
142                                if let Some(item) = tuple.get_elements().get(self.current_index - 1)
143                                {
144                                    self.collected.push(item.stabilize());
145                                    Ok(())
146                                } else {
147                                    // 所有元素都处理完了
148                                    Ok(())
149                                }
150                            }
151                            _ => Err(RuntimeError::DetailedError(
152                                "Container must be a tuple".to_string().into(),
153                            )),
154                        }
155                    }
156                    _ => {
157                        // 如果返回的不是布尔值,直接忽略
158                        Ok(())
159                    }
160                }
161            }
162            StepResult::SetSelfObject(_) => {
163                // 如果是 SetSelfObject,表示需要设置当前对象
164                // 这里我们不需要做任何操作,因为我们已经在构造函数中设置了 self_object
165                Ok(())
166            }
167            _ => Err(RuntimeError::DetailedError(
168                "Unexpected step result in lazy set collector"
169                    .to_string()
170                    .into(),
171            )),
172        }
173    }
174
175    fn step(&mut self, _gc: &mut GC<OnionObjectCell>) -> StepResult {
176        unwrap_step_result!(self
177            .container
178            .weak()
179            .with_data(|container| match container {
180                OnionObject::Tuple(tuple) => {
181                    // 使用索引获取当前元素
182                    if let Some(item) = tuple.get_elements().get(self.current_index) {
183                        let item_clone = item.clone();
184                        self.current_index += 1; // 移动到下一个元素
185
186                        self.filter
187                            .weak()
188                            .with_data(|filter: &OnionObject| match filter {
189                                OnionObject::Lambda(_) => {
190                                    // let OnionObject::Tuple(params) = func.parameter.try_borrow()?
191                                    // else {
192                                    //     return Err(RuntimeError::InvalidType(format!(
193                                    //         "Filter's parameter must be a tuple, got {:?}",
194                                    //         func.parameter
195                                    //     )));
196                                    // };
197                                    // let argument =
198                                    //     params.clone_and_named_assignment(&OnionTuple::new(vec![
199                                    //         item_clone,
200                                    //     ]))?;
201                                    // let runnable = func.create_runnable(argument, &self.filter, gc)?;
202                                    let argument = OnionObject::Tuple(
203                                        OnionTuple::new(vec![item_clone]).into(),
204                                    )
205                                    .consume_and_stabilize();
206                                    let runnable =
207                                        Box::new(OnionLambdaRunnableLauncher::new_static(
208                                            &self.filter,
209                                            &argument,
210                                            &|r| Ok(r),
211                                        )?);
212                                    Ok(StepResult::NewRunnable(runnable))
213                                }
214                                OnionObject::Boolean(false) => Ok(StepResult::Continue),
215                                _ => {
216                                    self.collected.push(item_clone.consume_and_stabilize());
217                                    Ok(StepResult::Continue)
218                                }
219                            })
220                    } else {
221                        // 所有元素都处理完了
222                        Ok(StepResult::Return(
223                            OnionTuple::new_static_no_ref(&self.collected).into(),
224                        ))
225                    }
226                }
227                _ => Err(RuntimeError::InvalidType(
228                    "Container must be a tuple".to_string().into(),
229                )),
230            }))
231    }
232
233    fn format_context(&self) -> Result<serde_json::Value, RuntimeError> {
234        return Ok(serde_json::json!({
235            "type": "LazySetCollector",
236            "container": self.container.to_string(),
237            "filter": self.filter.to_string(),
238            "collected": self.collected.iter().map(|o| o.to_string()).collect::<Vec<_>>(),
239            "current_index": self.current_index,
240        }));
241    }
242}
243
244impl OnionLazySet {
245    pub fn reconstruct_container(&self) -> Result<OnionObject, RuntimeError> {
246        Ok(OnionObject::LazySet(
247            OnionLazySet {
248                container: self.container.clone(),
249                filter: self.filter.clone(),
250            }
251            .into(),
252        ))
253    }
254}