1use std::{
9 collections::VecDeque,
10 fmt::{Debug, Display},
11 sync::Arc,
12};
13
14use arc_gc::{
15 arc::{GCArc, GCArcWeak},
16 gc::GC,
17 traceable::GCTraceable,
18};
19
20use crate::{
21 lambda::runnable::{Runnable, RuntimeError},
22 types::{
23 lambda::{
24 parameter::LambdaParameter, vm_instructions::instruction_set::VMInstructionPackage,
25 },
26 object::{OnionObject, OnionObjectCell, OnionStaticObject},
27 },
28 utils::fastmap::{OnionFastMap, OnionKeyPool},
29};
30
31use super::runnable::OnionLambdaRunnable;
32
33pub enum LambdaBody {
37 Instruction(Arc<VMInstructionPackage>),
39 NativeFunction(
41 (
42 Arc<
43 dyn Fn(
44 &OnionObject, &OnionFastMap<Box<str>, OnionStaticObject>, &OnionFastMap<Box<str>, OnionObject>, &mut GC<OnionObjectCell>, ) -> Box<dyn Runnable>
49 + Send
50 + Sync,
51 >,
52 OnionKeyPool<Box<str>>,
53 ),
54 ),
55}
56
57impl Clone for LambdaBody {
58 fn clone(&self) -> Self {
59 match self {
60 LambdaBody::Instruction(instruction) => LambdaBody::Instruction(instruction.clone()),
61 LambdaBody::NativeFunction(native_function) => {
62 LambdaBody::NativeFunction(native_function.clone())
63 }
64 }
65 }
66}
67
68impl LambdaBody {
69 fn create_string_pool(&self) -> OnionKeyPool<Box<str>> {
70 match self {
71 LambdaBody::Instruction(instruction) => instruction.create_key_pool(),
72 LambdaBody::NativeFunction((_, key_pool)) => key_pool.clone(),
73 }
74 }
75}
76
77impl Debug for LambdaBody {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 match self {
80 LambdaBody::Instruction(_) => write!(f, "Instruction(...)"),
81 LambdaBody::NativeFunction(_) => write!(f, "NativeFunction"),
82 }
83 }
84}
85
86impl Display for LambdaBody {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 match self {
89 LambdaBody::Instruction(_) => write!(f, "Instruction(...)"),
90 LambdaBody::NativeFunction(_) => write!(f, "NativeFunction"),
91 }
92 }
93}
94
95pub enum LambdaType {
97 Atomic,
99 AsyncLauncher,
101 SyncLauncher,
103}
104
105pub struct OnionLambdaDefinition {
109 parameter: LambdaParameter,
111 flatten_param_keys: Box<[Box<str>]>,
113 flatten_param_constraints: Box<[OnionObject]>,
115 body: LambdaBody,
117 capture: OnionFastMap<Box<str>, OnionObject>,
119 signature: Box<str>,
121 lambda_type: LambdaType,
123}
124
125impl OnionLambdaDefinition {
126 pub fn new_static(
127 parameter: LambdaParameter,
128 body: LambdaBody,
129 capture: OnionFastMap<Box<str>, OnionObject>,
130 signature: Box<str>,
131 lambda_type: LambdaType,
132 ) -> OnionStaticObject {
133 let flatten_param_keys = parameter.flatten_keys();
134 let flatten_param_constraints = parameter.flatten_constraints();
135 OnionObject::Lambda((
136 OnionLambdaDefinition {
137 parameter,
138 flatten_param_keys,
139 flatten_param_constraints,
140 body,
141 capture,
142 signature,
143 lambda_type,
144 }
145 .into(),
146 OnionObject::Undefined(None).into(),
147 ))
148 .consume_and_stabilize()
149 }
150
151 pub fn new_static_with_self(
152 parameter: LambdaParameter,
153 body: LambdaBody,
154 capture: OnionFastMap<Box<str>, OnionObject>,
155 self_object: &OnionObject,
156 signature: Box<str>,
157 lambda_type: LambdaType,
158 ) -> OnionStaticObject {
159 let flatten_param_keys = parameter.flatten_keys();
160 let flatten_param_constraints = parameter.flatten_constraints();
161 OnionObject::Lambda((
162 OnionLambdaDefinition {
163 parameter,
164 flatten_param_keys,
165 flatten_param_constraints,
166 body,
167 capture,
168 signature,
169 lambda_type,
170 }
171 .into(),
172 self_object.clone().into(),
173 ))
174 .consume_and_stabilize()
175 }
176
177 pub fn create_key_pool(&self) -> OnionKeyPool<Box<str>> {
179 self.body.create_string_pool()
180 }
181
182 pub fn with_lambda_type(&self, lambda_type: LambdaType) -> Self {
183 OnionLambdaDefinition {
184 parameter: self.parameter.clone(),
185 flatten_param_keys: self.flatten_param_keys.clone(),
186 flatten_param_constraints: self.flatten_param_constraints.clone(),
187 body: self.body.clone(),
188 capture: self.capture.clone(),
189 signature: self.signature.clone(),
190 lambda_type,
191 }
192 }
193
194 pub fn lambda_type(&self) -> &LambdaType {
195 &self.lambda_type
196 }
197
198 pub fn create_runnable(
200 &self,
201 argument: &OnionFastMap<Box<str>, OnionStaticObject>,
202 this_lambda: &OnionStaticObject,
203 self_object: &OnionObject,
204 gc: &mut GC<OnionObjectCell>,
205 ) -> Result<Box<dyn Runnable>, RuntimeError> {
206 match &self.body {
207 LambdaBody::Instruction(instruction) => {
208 let runnable = OnionLambdaRunnable::new(
209 argument,
210 &self.capture,
211 self_object,
212 this_lambda,
213 instruction.clone(),
214 match instruction.get_table().get(self.signature.as_ref()) {
215 Some(ip) => *ip as isize,
216 None => {
217 return Err(RuntimeError::InvalidOperation(
218 format!(
219 "Signature '{}' not found in instruction package",
220 self.signature
221 )
222 .into(),
223 ));
224 }
225 },
226 )?;
227 Ok(Box::new(runnable))
228 }
229 LambdaBody::NativeFunction((native_function, _)) => {
230 Ok(native_function(self_object, argument, &self.capture, gc))
231 }
232 }
233 }
234
235 pub fn get_signature(&self) -> &str {
236 &self.signature
237 }
238
239 pub fn get_parameter(&self) -> &LambdaParameter {
240 &self.parameter
241 }
242
243 pub fn get_flatten_param_keys(&self) -> &[Box<str>] {
244 &self.flatten_param_keys
245 }
246
247 pub fn get_flatten_param_constraints(&self) -> &[OnionObject] {
248 &self.flatten_param_constraints
249 }
250
251 pub fn get_capture(&self) -> &OnionFastMap<Box<str>, OnionObject> {
252 &self.capture
253 }
254
255 pub fn get_body(&self) -> &LambdaBody {
256 &self.body
257 }
258
259 pub fn upgrade(&self, collected: &mut Vec<GCArc<OnionObjectCell>>) {
260 self.parameter.upgrade(collected);
261 for (_, obj) in self.capture.pairs() {
262 obj.upgrade(collected);
263 }
264 }
265
266 pub fn with_attribute<F, R>(&self, key: &OnionObject, f: &F) -> Result<R, RuntimeError>
267 where
268 F: Fn(&OnionObject) -> Result<R, RuntimeError>,
269 {
270 match key {
271 OnionObject::String(s) if s.as_ref() == "$parameter" => {
272 let parameter = self.parameter.to_onion();
273 f(parameter.weak())
274 }
275 OnionObject::String(s) if s.as_ref() == "$signature" => {
276 f(&OnionObject::String(Arc::from(self.signature.clone())))
277 }
278 OnionObject::String(s) => {
279 if let Some(value) = self.capture.get(s.as_ref()) {
280 f(value)
281 } else {
282 Err(RuntimeError::InvalidOperation(
283 format!("Attribute '{:?}' not found in lambda definition", key).into(),
284 ))
285 }
286 }
287 _ => Err(RuntimeError::InvalidOperation(
288 format!("Attribute '{:?}' not found in lambda definition", key).into(),
289 )),
290 }
291 }
292}
293
294impl GCTraceable<OnionObjectCell> for OnionLambdaDefinition {
295 fn collect(&self, queue: &mut VecDeque<GCArcWeak<OnionObjectCell>>) {
296 self.parameter.collect(queue);
297 for (_, obj) in self.capture.pairs() {
298 obj.collect(queue);
299 }
300 }
301}
302
303impl Debug for OnionLambdaDefinition {
304 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
305 write!(
306 f,
307 "OnionLambdaDefinition {{ parameter: {:?}, body: {:?}, capture: {:?} }}",
308 self.parameter, self.body, self.capture
309 )
310 }
311}