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