onion_vm/types/lambda/
parameter.rs

1use std::fmt::Display;
2
3use arc_gc::{arc::GCArc, traceable::GCTraceable};
4
5use crate::{
6    lambda::runnable::RuntimeError,
7    types::{
8        object::{OnionObject, OnionObjectCell, OnionStaticObject},
9        pair::OnionPair,
10        tuple::OnionTuple,
11    },
12};
13
14#[derive(Debug, Clone)]
15pub enum LambdaParameter {
16    Single((Box<str>, OnionObject)),
17    Multiple(Box<[LambdaParameter]>),
18}
19
20impl GCTraceable<OnionObjectCell> for LambdaParameter {
21    fn collect(
22        &self,
23        queue: &mut std::collections::VecDeque<arc_gc::arc::GCArcWeak<OnionObjectCell>>,
24    ) {
25        match self {
26            LambdaParameter::Single((_, obj)) => {
27                obj.collect(queue);
28            }
29            LambdaParameter::Multiple(params) => {
30                for param in params {
31                    param.collect(queue);
32                }
33            }
34        }
35    }
36}
37
38impl Display for LambdaParameter {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        match self {
41            LambdaParameter::Single((key, obj)) => {
42                write!(f, "{} : {:?}", key, obj)
43            }
44            LambdaParameter::Multiple(params) => {
45                write!(
46                    f,
47                    "({})",
48                    params
49                        .iter()
50                        .map(|p| p.to_string())
51                        .collect::<Vec<_>>()
52                        .join(", ")
53                )
54            }
55        }
56    }
57}
58
59impl LambdaParameter {
60    pub fn top(key: &str) -> Self {
61        Self::Single((key.into(), OnionObject::Boolean(true)))
62    }
63
64    pub fn bottom(key: &str) -> Self {
65        Self::Single((key.into(), OnionObject::Boolean(false)))
66    }
67}
68
69impl LambdaParameter {
70    pub fn len(&self) -> usize {
71        match self {
72            LambdaParameter::Single(_) => 1,
73            LambdaParameter::Multiple(params) => params.iter().map(LambdaParameter::len).sum(),
74        }
75    }
76
77    /// 按照扁平化的方式依照顺序获取参数的约束定义
78    pub fn constraint_at(&self, index: usize) -> Option<&OnionObject> {
79        match self {
80            LambdaParameter::Single((_, obj)) => {
81                if index == 0 {
82                    Some(obj)
83                } else {
84                    None
85                }
86            }
87            LambdaParameter::Multiple(params) => {
88                let mut curr = index;
89                for param in params {
90                    let len = param.len();
91                    if curr < len {
92                        return param.constraint_at(curr);
93                    } else {
94                        curr -= len;
95                    }
96                }
97                None
98            }
99        }
100    }
101
102    #[allow(dead_code)]
103    pub fn key_at(&self, index: usize) -> Option<&str> {
104        match self {
105            LambdaParameter::Single((key, _)) => {
106                if index == 0 {
107                    Some(key)
108                } else {
109                    None
110                }
111            }
112            LambdaParameter::Multiple(params) => {
113                let mut curr = index;
114                for param in params {
115                    let len = param.len();
116                    if curr < len {
117                        return param.key_at(curr);
118                    } else {
119                        curr -= len;
120                    }
121                }
122                None
123            }
124        }
125    }
126
127    #[allow(dead_code)]
128    pub fn at(&self, index: usize) -> Option<(&str, &OnionObject)> {
129        match self {
130            LambdaParameter::Single((key, obj)) => {
131                if index == 0 {
132                    Some((key, obj))
133                } else {
134                    None
135                }
136            }
137            LambdaParameter::Multiple(params) => {
138                let mut curr = index;
139                for param in params {
140                    let len = param.len();
141                    if curr < len {
142                        return param.at(curr);
143                    } else {
144                        curr -= len;
145                    }
146                }
147                None
148            }
149        }
150    }
151
152    /// 打包成一个对象
153    pub fn to_onion(&self) -> OnionStaticObject {
154        fn inner(param: &LambdaParameter) -> OnionObject {
155            match param {
156                LambdaParameter::Single((key, obj)) => OnionObject::Pair(
157                    OnionPair::new(OnionObject::String(key.clone().into()), obj.clone()).into(),
158                ),
159                LambdaParameter::Multiple(params) => {
160                    let mut pairs = vec![];
161                    for param in params {
162                        pairs.push(inner(param));
163                    }
164                    OnionObject::Tuple(OnionTuple::new(pairs).into())
165                }
166            }
167        }
168        inner(self).consume_and_stabilize()
169    }
170
171    pub fn from_onion(obj: &OnionObject) -> Result<LambdaParameter, RuntimeError> {
172        fn inner(obj: &OnionObject) -> Result<LambdaParameter, RuntimeError> {
173            match obj {
174                OnionObject::Pair(pair) => {
175                    let key = match pair.get_key() {
176                        OnionObject::String(s) => s.as_ref(),
177                        _ => {
178                            return Err(RuntimeError::InvalidType(
179                                format!("Expected string key, found: {:?}", obj).into(),
180                            ));
181                        }
182                    };
183                    let value = pair.get_value();
184                    Ok(LambdaParameter::Single((key.into(), value.clone())))
185                }
186                OnionObject::Tuple(tuple) => {
187                    let mut params = Vec::with_capacity(tuple.get_elements().len());
188                    for item in tuple.get_elements().iter() {
189                        params.push(inner(item)?);
190                    }
191                    Ok(LambdaParameter::Multiple(params.into_boxed_slice()))
192                }
193                OnionObject::String(s) => {
194                    // 处理单个字符串参数
195                    Ok(LambdaParameter::Single((
196                        Box::from(s.as_ref()),
197                        OnionObject::Boolean(true),
198                    )))
199                }
200                _ => Err(RuntimeError::InvalidType(
201                    format!("Expected one of pair, tuple, or string, found: {:?}", obj).into(),
202                )),
203            }
204        }
205        inner(obj)
206    }
207}
208
209impl LambdaParameter {
210    pub fn upgrade(&self, collected: &mut Vec<GCArc<OnionObjectCell>>) {
211        match self {
212            Self::Single((_, v)) => v.upgrade(collected),
213            Self::Multiple(v) => v.iter().for_each(|e| e.upgrade(collected)),
214        }
215    }
216}
217
218impl LambdaParameter {
219    pub fn unpack_arguments(
220        &self,
221        argument: &OnionObject,
222    ) -> Result<Vec<OnionObject>, RuntimeError> {
223        let mut collected = Vec::with_capacity(self.len());
224
225        fn inner(
226            object: &OnionObject,
227            layer: &LambdaParameter,
228            collected: &mut Vec<OnionObject>,
229        ) -> Result<(), RuntimeError> {
230            match layer {
231                LambdaParameter::Single(_) => {
232                    // If expecting one param, the argument can be a single object.
233                    collected.push(object.clone());
234                    Ok(())
235                }
236                LambdaParameter::Multiple(v) => {
237                    // If expecting multiple, the argument must be a tuple.
238                    object.with_data(|data| match data {
239                        OnionObject::Tuple(tuple) => {
240                            if tuple.get_elements().len() != v.len() {
241                                return Err(RuntimeError::InvalidOperation(
242                                    "Arity Mismatch".into(),
243                                ));
244                            }
245                            for (i, object) in tuple.get_elements().iter().enumerate() {
246                                inner(object, &v[i], collected)?;
247                            }
248                            Ok(())
249                        }
250                        _ => Err(RuntimeError::InvalidType(
251                            format!("Expected tuple, found: {:?}", data).into(),
252                        )),
253                    })
254                }
255            }
256        }
257
258        inner(argument, &self, &mut collected)?;
259        Ok(collected)
260    }
261}
262
263impl LambdaParameter {
264    /// Recursively flattens the parameter structure to produce a flat list of parameter names.
265    ///
266    /// For a parameter structure like `(a, (b, c))`, this will return `["a", "b", "c"]`.
267    pub fn flatten_keys(&self) -> Box<[Box<str>]> {
268        let mut keys = Vec::with_capacity(self.len());
269        fn inner(param: &LambdaParameter, collected_keys: &mut Vec<Box<str>>) {
270            match param {
271                LambdaParameter::Single((name, _)) => {
272                    collected_keys.push(name.clone());
273                }
274                LambdaParameter::Multiple(params) => {
275                    for sub_param in params {
276                        inner(sub_param, collected_keys);
277                    }
278                }
279            }
280        }
281        inner(self, &mut keys);
282        keys.into_boxed_slice()
283    }
284
285    /// Recursively flattens the parameter structure to produce a flat list of constraint objects.
286    ///
287    /// For a parameter structure like `(a: Int, (b: String, c: Bool))`,
288    /// this will return a Vec containing the OnionObject for `Int`, `String`, and `Bool`.
289    pub fn flatten_constraints(&self) -> Box<[OnionObject]> {
290        let mut constraints = Vec::with_capacity(self.len());
291
292        fn inner(param: &LambdaParameter, collected_constraints: &mut Vec<OnionObject>) {
293            match param {
294                LambdaParameter::Single((_, constraint_obj)) => {
295                    collected_constraints.push(constraint_obj.clone());
296                }
297                LambdaParameter::Multiple(params) => {
298                    for sub_param in params {
299                        inner(sub_param, collected_constraints);
300                    }
301                }
302            }
303        }
304        inner(self, &mut constraints);
305        constraints.into_boxed_slice()
306    }
307}