1pub mod ast_bindings;
21pub mod native;
22pub mod solver;
23use std::sync::Arc;
24
25use onion_vm::{
26 GCTraceable,
27 lambda::runnable::RuntimeError,
28 types::{
29 object::{OnionObject, OnionObjectCell, OnionObjectExt, OnionStaticObject},
30 tuple::OnionTuple,
31 },
32};
33
34use crate::parser::ast::{ASTNode, ASTNodeType};
35use base64::engine::Engine;
36
37#[derive(Debug, Clone)]
42pub struct OnionASTObject {
43 ast: ASTNode,
44}
45
46impl GCTraceable<OnionObjectCell> for OnionASTObject {
47 fn collect(&self, _: &mut std::collections::VecDeque<onion_vm::GCArcWeak<OnionObjectCell>>) {}
48}
49
50impl OnionObjectExt for OnionASTObject {
51 fn as_any(&self) -> &dyn std::any::Any {
52 self
53 }
54
55 fn upgrade(&self, _: &mut Vec<onion_vm::GCArc<OnionObjectCell>>) {}
56
57 fn equals(&self, other: &OnionObject) -> Result<bool, RuntimeError> {
58 other.with_data(|data| match data {
59 OnionObject::Custom(data) => {
60 if let Some(ast_object) = data.as_any().downcast_ref::<OnionASTObject>() {
61 Ok(self.ast == ast_object.ast)
62 } else {
63 Ok(false)
64 }
65 }
66 _ => Ok(false),
67 })
68 }
69
70 fn key_of(&self) -> Result<OnionStaticObject, RuntimeError> {
71 let key_ast = ASTNode {
73 node_type: self.ast.node_type.clone(),
74 children: vec![],
75 source_location: self.ast.source_location.clone(),
76 };
77 Ok(OnionObject::Custom(Arc::new(OnionASTObject { ast: key_ast })).stabilize())
78 }
79
80 fn value_of(&self) -> Result<OnionStaticObject, RuntimeError> {
81 let children = self
83 .ast
84 .children
85 .iter()
86 .map(|child| OnionObject::Custom(Arc::new(OnionASTObject { ast: child.clone() })))
87 .collect::<Vec<_>>();
88 Ok(OnionObject::Tuple(OnionTuple::new(children).into()).stabilize())
89 }
90
91 fn len(&self) -> Result<OnionStaticObject, RuntimeError> {
92 Ok(OnionObject::Integer(self.ast.children.len() as i64).stabilize())
93 }
94
95 fn apply(&self, value: &OnionObject) -> Result<OnionStaticObject, RuntimeError> {
96 value.with_data(|data| match data {
97 OnionObject::Integer(i) => {
98 let index = if *i < 0 {
100 return Err(RuntimeError::InvalidOperation(
101 "Negative index is not allowed".into(),
102 ));
103 } else {
104 *i as usize
105 };
106
107 if index >= self.ast.children.len() {
108 return Err(RuntimeError::InvalidOperation(
109 format!(
110 "Index {} out of bounds for AST children of length {}",
111 index,
112 self.ast.children.len()
113 )
114 .into(),
115 ));
116 }
117
118 let child_ast = self.ast.children[index].clone();
119 Ok(OnionObject::Custom(Arc::new(OnionASTObject { ast: child_ast })).stabilize())
120 }
121 OnionObject::Pair(pair) => {
122 let index = pair.get_key().with_data(|key_data| match key_data {
124 OnionObject::Integer(i) => {
125 if *i < 0 {
126 Err(RuntimeError::InvalidOperation(
127 "Negative index is not allowed".into(),
128 ))
129 } else {
130 Ok(*i as usize)
131 }
132 }
133 _ => Err(RuntimeError::InvalidType(
134 "Pair key must be an integer index".into()
135 )),
136 })?;
137
138 if index >= self.ast.children.len() {
139 return Err(RuntimeError::InvalidOperation(
140 format!(
141 "Index {} out of bounds for AST children of length {}",
142 index,
143 self.ast.children.len()
144 )
145 .into(),
146 ));
147 }
148
149 let new_child_ast = OnionASTObject::from_onion(pair.get_value())?;
151
152 let mut new_children = self.ast.children.clone();
154 new_children[index] = new_child_ast;
155
156 let new_ast = ASTNode {
158 node_type: self.ast.node_type.clone(),
159 source_location: self.ast.source_location.clone(),
160 children: new_children,
161 };
162
163 Ok(OnionObject::Custom(Arc::new(OnionASTObject { ast: new_ast })).stabilize())
164 }
165 _ => Err(RuntimeError::InvalidType(
166 "Apply argument must be an integer (for access) or a pair (for replacement)".into()
167 )),
168 })
169 }
170
171 fn with_attribute(
172 &self,
173 key: &OnionObject,
174 f: &mut dyn FnMut(&OnionObject) -> Result<(), RuntimeError>,
175 ) -> Result<(), RuntimeError> {
176 key.with_data(|key_data| match key_data {
177 OnionObject::String(attr_name) => {
178 match attr_name.as_ref() {
179 "node_type" => {
180 let type_name = match &self.ast.node_type {
182 ASTNodeType::Null => "Null",
183 ASTNodeType::Undefined => "Undefined",
184 ASTNodeType::String(_) => "String",
185 ASTNodeType::Boolean(_) => "Boolean",
186 ASTNodeType::Number(_) => "Number",
187 ASTNodeType::Base64(_) => "Base64",
188 ASTNodeType::Variable(_) => "Variable",
189 ASTNodeType::Required(_) => "Required",
190 ASTNodeType::Let(_) => "Let",
191 ASTNodeType::Frame => "Frame",
192 ASTNodeType::Assign => "Assign",
193 ASTNodeType::LambdaDef(_, _) => "LambdaDef",
194 ASTNodeType::Expressions => "Expressions",
195 ASTNodeType::Apply => "Apply",
196 ASTNodeType::Operation(_) => "Operation",
197 ASTNodeType::Tuple => "Tuple",
198 ASTNodeType::AssumeTuple => "AssumeTuple",
199 ASTNodeType::Pair => "Pair",
200 ASTNodeType::GetAttr => "GetAttr",
201 ASTNodeType::Return => "Return",
202 ASTNodeType::If => "If",
203 ASTNodeType::While => "While",
204 ASTNodeType::Modifier(_) => "Modifier",
205 ASTNodeType::Break => "Break",
206 ASTNodeType::Continue => "Continue",
207 ASTNodeType::Range => "Range",
208 ASTNodeType::In => "In",
209 ASTNodeType::Namespace(_) => "Namespace",
210 ASTNodeType::LazySet => "Set",
211 ASTNodeType::Map => "Map",
212 ASTNodeType::Is => "Is",
213 ASTNodeType::Raise => "Raise",
214 ASTNodeType::Dynamic => "Dynamic",
215 ASTNodeType::Static => "Static",
216 ASTNodeType::Comptime => "Comptime",
217 };
218 let type_obj = OnionObject::String(type_name.into());
219 f(&type_obj)
220 },
221 "has_data" => {
222 let has_data = matches!(
224 &self.ast.node_type,
225 ASTNodeType::String(_) | ASTNodeType::Boolean(_) | ASTNodeType::Number(_) |
226 ASTNodeType::Base64(_) | ASTNodeType::Variable(_) | ASTNodeType::Required(_) |
227 ASTNodeType::Let(_) | ASTNodeType::LambdaDef(_, _) | ASTNodeType::Operation(_) |
228 ASTNodeType::Modifier(_) | ASTNodeType::Namespace(_)
229 );
230 let has_data_obj = OnionObject::Boolean(has_data);
231 f(&has_data_obj)
232 },
233 "data" => {
234 match &self.ast.node_type {
236 ASTNodeType::String(s) => {
237 let data_obj = OnionObject::String(s.clone().into());
238 f(&data_obj)
239 },
240 ASTNodeType::Boolean(b) => {
241 let data_obj = OnionObject::Boolean(*b);
242 f(&data_obj)
243 },
244 ASTNodeType::Number(n) => {
245 let data_obj = OnionObject::String(n.clone().into());
246 f(&data_obj)
247 },
248 ASTNodeType::Base64(b64) => {
249 let data_obj = OnionObject::String(b64.clone().into());
250 f(&data_obj)
251 },
252 ASTNodeType::Variable(name) | ASTNodeType::Required(name) |
253 ASTNodeType::Let(name) | ASTNodeType::Namespace(name) => {
254 let data_obj = OnionObject::String(name.clone().into());
255 f(&data_obj)
256 },
257 ASTNodeType::LambdaDef(is_dyn, captures) => {
258 let captures_vec: Vec<OnionObject> = captures.iter()
260 .map(|s| OnionObject::String(s.clone().into()))
261 .collect();
262 let captures_tuple = OnionObject::Tuple(OnionTuple::new(captures_vec).into());
263 let data_tuple = OnionObject::Tuple(OnionTuple::new(vec![
264 OnionObject::Boolean(*is_dyn),
265 captures_tuple
266 ]).into());
267 f(&data_tuple)
268 },
269 ASTNodeType::Operation(op) => {
270 let op_str = match op {
271 crate::parser::ast::ASTNodeOperation::Add => "+",
272 crate::parser::ast::ASTNodeOperation::Abs => "abs",
273 crate::parser::ast::ASTNodeOperation::Subtract => "-",
274 crate::parser::ast::ASTNodeOperation::Minus => "minus",
275 crate::parser::ast::ASTNodeOperation::Multiply => "*",
276 crate::parser::ast::ASTNodeOperation::Divide => "/",
277 crate::parser::ast::ASTNodeOperation::Modulus => "%",
278 crate::parser::ast::ASTNodeOperation::Power => "**",
279 crate::parser::ast::ASTNodeOperation::And => "and",
280 crate::parser::ast::ASTNodeOperation::Xor => "xor",
281 crate::parser::ast::ASTNodeOperation::Or => "or",
282 crate::parser::ast::ASTNodeOperation::Not => "not",
283 crate::parser::ast::ASTNodeOperation::Equal => "==",
284 crate::parser::ast::ASTNodeOperation::NotEqual => "!=",
285 crate::parser::ast::ASTNodeOperation::Greater => ">",
286 crate::parser::ast::ASTNodeOperation::Less => "<",
287 crate::parser::ast::ASTNodeOperation::GreaterEqual => ">=",
288 crate::parser::ast::ASTNodeOperation::LessEqual => "<=",
289 crate::parser::ast::ASTNodeOperation::LeftShift => "<<",
290 crate::parser::ast::ASTNodeOperation::RightShift => ">>",
291 };
292 let data_obj = OnionObject::String(op_str.into());
293 f(&data_obj)
294 },
295 ASTNodeType::Modifier(mod_type) => {
296 let mod_str = match mod_type {
297 crate::parser::ast::ASTNodeModifier::Mut => "mut",
298 crate::parser::ast::ASTNodeModifier::Const => "const",
299 crate::parser::ast::ASTNodeModifier::KeyOf => "keyof",
300 crate::parser::ast::ASTNodeModifier::ValueOf => "valueof",
301 crate::parser::ast::ASTNodeModifier::Assert => "assert",
302 crate::parser::ast::ASTNodeModifier::Import => "import",
303 crate::parser::ast::ASTNodeModifier::TypeOf => "typeof",
304 crate::parser::ast::ASTNodeModifier::LengthOf => "lengthof",
305 crate::parser::ast::ASTNodeModifier::Launch => "launch",
306 crate::parser::ast::ASTNodeModifier::Spawn => "spawn",
307 crate::parser::ast::ASTNodeModifier::Async => "async",
308 crate::parser::ast::ASTNodeModifier::Sync => "sync",
309 crate::parser::ast::ASTNodeModifier::Atomic => "atomic",
310 };
311 let data_obj = OnionObject::String(mod_str.into());
312 f(&data_obj)
313 },
314 _ => {
315 let null_obj = OnionObject::Null;
317 f(&null_obj)
318 }
319 }
320 },
321 "value" => {
323 match &self.ast.node_type {
324 ASTNodeType::String(s) | ASTNodeType::Number(s) | ASTNodeType::Base64(s) => {
325 let value_obj = OnionObject::String(s.clone().into());
326 f(&value_obj)
327 },
328 ASTNodeType::Boolean(b) => {
329 let value_obj = OnionObject::Boolean(*b);
330 f(&value_obj)
331 },
332 _ => Err(RuntimeError::InvalidOperation(
333 "Attribute 'value' is only supported for String, Number, Base64, and Boolean node types".into()
334 ))
335 }
336 },
337 "name" => {
338 match &self.ast.node_type {
339 ASTNodeType::Variable(name) | ASTNodeType::Required(name) |
340 ASTNodeType::Let(name) | ASTNodeType::Namespace(name) => {
341 let name_obj = OnionObject::String(name.clone().into());
342 f(&name_obj)
343 },
344 _ => Err(RuntimeError::InvalidOperation(
345 "Attribute 'name' is only supported for Variable, Required, Let, and Namespace node types".into()
346 ))
347 }
348 },
349 "op" => {
350 match &self.ast.node_type {
351 ASTNodeType::Operation(op) => {
352 let op_str = match op {
353 crate::parser::ast::ASTNodeOperation::Add => "+",
354 crate::parser::ast::ASTNodeOperation::Abs => "abs",
355 crate::parser::ast::ASTNodeOperation::Subtract => "-",
356 crate::parser::ast::ASTNodeOperation::Minus => "minus",
357 crate::parser::ast::ASTNodeOperation::Multiply => "*",
358 crate::parser::ast::ASTNodeOperation::Divide => "/",
359 crate::parser::ast::ASTNodeOperation::Modulus => "%",
360 crate::parser::ast::ASTNodeOperation::Power => "**",
361 crate::parser::ast::ASTNodeOperation::And => "and",
362 crate::parser::ast::ASTNodeOperation::Xor => "xor",
363 crate::parser::ast::ASTNodeOperation::Or => "or",
364 crate::parser::ast::ASTNodeOperation::Not => "not",
365 crate::parser::ast::ASTNodeOperation::Equal => "==",
366 crate::parser::ast::ASTNodeOperation::NotEqual => "!=",
367 crate::parser::ast::ASTNodeOperation::Greater => ">",
368 crate::parser::ast::ASTNodeOperation::Less => "<",
369 crate::parser::ast::ASTNodeOperation::GreaterEqual => ">=",
370 crate::parser::ast::ASTNodeOperation::LessEqual => "<=",
371 crate::parser::ast::ASTNodeOperation::LeftShift => "<<",
372 crate::parser::ast::ASTNodeOperation::RightShift => ">>",
373 };
374 let op_obj = OnionObject::String(op_str.into());
375 f(&op_obj)
376 },
377 _ => Err(RuntimeError::InvalidOperation(
378 "Attribute 'op' is only supported for Operation node type".into()
379 ))
380 }
381 },
382 "modifier" => {
383 match &self.ast.node_type {
384 ASTNodeType::Modifier(mod_type) => {
385 let mod_str = match mod_type {
386 crate::parser::ast::ASTNodeModifier::Mut => "mut",
387 crate::parser::ast::ASTNodeModifier::Const => "const",
388 crate::parser::ast::ASTNodeModifier::KeyOf => "keyof",
389 crate::parser::ast::ASTNodeModifier::ValueOf => "valueof",
390 crate::parser::ast::ASTNodeModifier::Assert => "assert",
391 crate::parser::ast::ASTNodeModifier::Import => "import",
392 crate::parser::ast::ASTNodeModifier::TypeOf => "typeof",
393 crate::parser::ast::ASTNodeModifier::LengthOf => "lengthof",
394 crate::parser::ast::ASTNodeModifier::Launch => "launch",
395 crate::parser::ast::ASTNodeModifier::Spawn => "spawn",
396 crate::parser::ast::ASTNodeModifier::Async => "async",
397 crate::parser::ast::ASTNodeModifier::Sync => "sync",
398 crate::parser::ast::ASTNodeModifier::Atomic => "atomic",
399 };
400 let mod_obj = OnionObject::String(mod_str.into());
401 f(&mod_obj)
402 },
403 _ => Err(RuntimeError::InvalidOperation(
404 "Attribute 'modifier' is only supported for Modifier node type".into()
405 ))
406 }
407 },
408 "is_dyn" | "dyn" => {
409 match &self.ast.node_type {
410 ASTNodeType::LambdaDef(is_dyn, _) => {
411 let dyn_obj = OnionObject::Boolean(*is_dyn);
412 f(&dyn_obj)
413 },
414 _ => Err(RuntimeError::InvalidOperation(
415 "Attribute 'is_dyn'/'dyn' is only supported for LambdaDef node type".into()
416 ))
417 }
418 },
419 "captures" => {
420 match &self.ast.node_type {
421 ASTNodeType::LambdaDef(_, captures) => {
422 let captures_vec: Vec<OnionObject> = captures.iter()
423 .map(|s| OnionObject::String(s.clone().into()))
424 .collect();
425 let captures_obj = OnionObject::Tuple(OnionTuple::new(captures_vec).into());
426 f(&captures_obj)
427 },
428 _ => Err(RuntimeError::InvalidOperation(
429 "Attribute 'captures' is only supported for LambdaDef node type".into()
430 ))
431 }
432 },
433 _ => Err(RuntimeError::InvalidOperation(
434 format!("Unknown attribute '{}'. Available attributes: node_type, has_data, data, value (for String/Number/Base64/Boolean), name (for Variable/Required/Let/Namespace), op (for Operation), modifier (for Modifier), is_dyn/dyn (for LambdaDef), captures (for LambdaDef)", attr_name).into()
435 ))
436 }
437 },
438 _ => Err(RuntimeError::InvalidType(
439 "Attribute key must be a string".into()
440 ))
441 })
442 }
443
444 fn type_of(&self) -> Result<String, RuntimeError> {
445 Ok("OnionASTObject".to_string())
446 }
447
448 fn to_string(&self, _ptrs: &Vec<*const OnionObject>) -> Result<String, RuntimeError> {
449 Ok(format!("OnionASTObject: {:?}", self.ast))
450 }
451 fn repr(&self, _ptrs: &Vec<*const OnionObject>) -> Result<String, RuntimeError> {
452 Ok(format!("OnionASTObject: {:?}", self.ast))
453 }
454
455 fn binary_shl(&self, other: &OnionObject) -> Result<OnionStaticObject, RuntimeError> {
456 other.with_data(|data| match data {
458 OnionObject::Tuple(tuple) => {
459 let mut children = vec![];
460 for elem in tuple.get_elements() {
461 children.push(OnionASTObject::from_onion(elem)?);
462 }
463 let new_ast = ASTNode {
464 node_type: self.ast.node_type.clone(),
465 source_location: self.ast.source_location.clone(),
466 children,
467 };
468 Ok(OnionObject::Custom(Arc::new(OnionASTObject { ast: new_ast })).stabilize())
469 }
470 _ => Err(RuntimeError::InvalidType(
471 "Expected a tuple for binary shift left".into(),
472 )),
473 })
474 }
475}
476
477impl OnionASTObject {
478 pub fn new(ast: ASTNode) -> Self {
480 Self { ast }
481 }
482
483 pub fn from_onion(object: &OnionObject) -> Result<ASTNode, RuntimeError> {
498 match object {
499 OnionObject::Custom(ast_object) => {
500 if let Some(ast_object) = ast_object.as_any().downcast_ref::<OnionASTObject>() {
501 Ok(ast_object.ast.clone())
502 } else {
503 Err(RuntimeError::InvalidType(
504 format!(
505 "Unsupported OnionObject type for AST conversion: {:?}",
506 object
507 )
508 .into(),
509 ))
510 }
511 }
512 OnionObject::Mut(_) => Err(RuntimeError::InvalidOperation(
513 ("Mutable objects may introduce cyclic references, ".to_owned()
514 + "which cannot be safely or deterministically converted to AST objects")
515 .into(),
516 )),
517 OnionObject::Boolean(v) => Ok(ASTNode {
518 node_type: ASTNodeType::Boolean(*v),
519 source_location: None,
520 children: vec![],
521 }),
522 OnionObject::String(s) => Ok(ASTNode {
523 node_type: ASTNodeType::String(s.as_ref().into()),
524 source_location: None,
525 children: vec![],
526 }),
527 OnionObject::Bytes(b) => {
528 let b64 = base64::engine::general_purpose::STANDARD.encode(b);
529 Ok(ASTNode {
530 node_type: ASTNodeType::Base64(b64),
531 source_location: None,
532 children: vec![],
533 })
534 }
535 OnionObject::Float(f) => Ok(ASTNode {
536 node_type: ASTNodeType::Number(f.to_string()),
537 source_location: None,
538 children: vec![],
539 }),
540 OnionObject::Integer(i) => Ok(ASTNode {
541 node_type: ASTNodeType::Number(i.to_string()),
542 source_location: None,
543 children: vec![],
544 }),
545 OnionObject::Null => Ok(ASTNode {
546 node_type: ASTNodeType::Null,
547 source_location: None,
548 children: vec![],
549 }),
550 OnionObject::Undefined(_) => Ok(ASTNode {
551 node_type: ASTNodeType::Undefined,
552 source_location: None,
553 children: vec![],
554 }),
555 OnionObject::Tuple(tuple) => {
556 let children = tuple
557 .get_elements()
558 .iter()
559 .map(|elem| OnionASTObject::from_onion(elem))
560 .collect::<Result<Vec<_>, _>>()?;
561 Ok(ASTNode {
562 node_type: ASTNodeType::Tuple,
563 source_location: None,
564 children,
565 })
566 }
567 OnionObject::Range(start, end) => Ok(ASTNode {
568 node_type: ASTNodeType::Range,
569 source_location: None,
570 children: vec![
571 ASTNode {
572 node_type: ASTNodeType::Number(start.to_string()),
573 source_location: None,
574 children: vec![],
575 },
576 ASTNode {
577 node_type: ASTNodeType::Number(end.to_string()),
578 source_location: None,
579 children: vec![],
580 },
581 ],
582 }),
583 OnionObject::Pair(pair) => {
584 let left = OnionASTObject::from_onion(pair.get_key())?;
585 let right = OnionASTObject::from_onion(pair.get_value())?;
586 Ok(ASTNode {
587 node_type: ASTNodeType::Pair,
588 source_location: None,
589 children: vec![left, right],
590 })
591 }
592 v => Err(RuntimeError::InvalidType(
593 format!("Unsupported OnionObject type for AST conversion: {:?}", v).into(),
594 )),
595 }
596 }
597}