1use std::collections::BTreeMap;
2use std::future::Future;
3use std::pin::Pin;
4use std::rc::Rc;
5use std::time::Instant;
6
7use crate::chunk::{Chunk, CompiledFunction, Constant, Op};
8use crate::value::{
9 compare_values, values_equal, VmAsyncBuiltinFn, VmBuiltinFn, VmClosure, VmEnv, VmError,
10 VmTaskHandle, VmValue,
11};
12
13struct CallFrame {
15 chunk: Chunk,
16 ip: usize,
17 stack_base: usize,
18 saved_env: VmEnv,
19 fn_name: String,
21}
22
23struct ExceptionHandler {
25 catch_ip: usize,
26 stack_depth: usize,
27 frame_depth: usize,
28 error_type: String,
30}
31
32#[derive(Debug, Clone, PartialEq)]
34pub enum DebugAction {
35 Continue,
37 Stop,
39}
40
41#[derive(Debug, Clone)]
43pub struct DebugState {
44 pub line: usize,
45 pub variables: BTreeMap<String, VmValue>,
46 pub frame_name: String,
47 pub frame_depth: usize,
48}
49
50pub struct Vm {
52 stack: Vec<VmValue>,
53 env: VmEnv,
54 output: String,
55 builtins: BTreeMap<String, VmBuiltinFn>,
56 async_builtins: BTreeMap<String, VmAsyncBuiltinFn>,
57 iterators: Vec<(Vec<VmValue>, usize)>,
59 frames: Vec<CallFrame>,
61 exception_handlers: Vec<ExceptionHandler>,
63 spawned_tasks: BTreeMap<String, VmTaskHandle>,
65 task_counter: u64,
67 deadlines: Vec<(Instant, usize)>,
69 breakpoints: Vec<usize>,
71 step_mode: bool,
73 step_frame_depth: usize,
75 stopped: bool,
77 last_line: usize,
79 source_dir: Option<std::path::PathBuf>,
81 imported_paths: Vec<std::path::PathBuf>,
83 source_file: Option<String>,
85 source_text: Option<String>,
87}
88
89impl Vm {
90 pub fn new() -> Self {
91 Self {
92 stack: Vec::with_capacity(256),
93 env: VmEnv::new(),
94 output: String::new(),
95 builtins: BTreeMap::new(),
96 async_builtins: BTreeMap::new(),
97 iterators: Vec::new(),
98 frames: Vec::new(),
99 exception_handlers: Vec::new(),
100 spawned_tasks: BTreeMap::new(),
101 task_counter: 0,
102 deadlines: Vec::new(),
103 breakpoints: Vec::new(),
104 step_mode: false,
105 step_frame_depth: 0,
106 stopped: false,
107 last_line: 0,
108 source_dir: None,
109 imported_paths: Vec::new(),
110 source_file: None,
111 source_text: None,
112 }
113 }
114
115 pub fn set_source_info(&mut self, file: &str, text: &str) {
117 self.source_file = Some(file.to_string());
118 self.source_text = Some(text.to_string());
119 }
120
121 pub fn set_breakpoints(&mut self, lines: Vec<usize>) {
123 self.breakpoints = lines;
124 }
125
126 pub fn set_step_mode(&mut self, step: bool) {
128 self.step_mode = step;
129 self.step_frame_depth = self.frames.len();
130 }
131
132 pub fn set_step_over(&mut self) {
134 self.step_mode = true;
135 self.step_frame_depth = self.frames.len();
136 }
137
138 pub fn set_step_out(&mut self) {
140 self.step_mode = true;
141 self.step_frame_depth = self.frames.len().saturating_sub(1);
142 }
143
144 pub fn is_stopped(&self) -> bool {
146 self.stopped
147 }
148
149 pub fn debug_state(&self) -> DebugState {
151 let line = self.current_line();
152 let variables = self.env.all_variables();
153 let frame_name = if self.frames.len() > 1 {
154 format!("frame_{}", self.frames.len() - 1)
155 } else {
156 "pipeline".to_string()
157 };
158 DebugState {
159 line,
160 variables,
161 frame_name,
162 frame_depth: self.frames.len(),
163 }
164 }
165
166 pub fn debug_stack_frames(&self) -> Vec<(String, usize)> {
168 let mut frames = Vec::new();
169 for (i, frame) in self.frames.iter().enumerate() {
170 let line = if frame.ip > 0 && frame.ip - 1 < frame.chunk.lines.len() {
171 frame.chunk.lines[frame.ip - 1] as usize
172 } else {
173 0
174 };
175 let name = if frame.fn_name.is_empty() {
176 if i == 0 {
177 "pipeline".to_string()
178 } else {
179 format!("fn_{}", i)
180 }
181 } else {
182 frame.fn_name.clone()
183 };
184 frames.push((name, line));
185 }
186 frames
187 }
188
189 fn current_line(&self) -> usize {
191 if let Some(frame) = self.frames.last() {
192 let ip = if frame.ip > 0 { frame.ip - 1 } else { 0 };
193 if ip < frame.chunk.lines.len() {
194 return frame.chunk.lines[ip] as usize;
195 }
196 }
197 0
198 }
199
200 pub async fn step_execute(&mut self) -> Result<Option<(VmValue, bool)>, VmError> {
203 let current_line = self.current_line();
205 let line_changed = current_line != self.last_line && current_line > 0;
206
207 if line_changed {
208 self.last_line = current_line;
209
210 if self.breakpoints.contains(¤t_line) {
212 self.stopped = true;
213 return Ok(Some((VmValue::Nil, true))); }
215
216 if self.step_mode && self.frames.len() <= self.step_frame_depth + 1 {
218 self.step_mode = false;
219 self.stopped = true;
220 return Ok(Some((VmValue::Nil, true))); }
222 }
223
224 self.stopped = false;
226 self.execute_one_cycle().await
227 }
228
229 async fn execute_one_cycle(&mut self) -> Result<Option<(VmValue, bool)>, VmError> {
231 if let Some(&(deadline, _)) = self.deadlines.last() {
233 if Instant::now() > deadline {
234 self.deadlines.pop();
235 let err = VmError::Thrown(VmValue::String(Rc::from("Deadline exceeded")));
236 match self.handle_error(err) {
237 Ok(None) => return Ok(None),
238 Ok(Some(val)) => return Ok(Some((val, false))),
239 Err(e) => return Err(e),
240 }
241 }
242 }
243
244 let frame = match self.frames.last_mut() {
246 Some(f) => f,
247 None => {
248 let val = self.stack.pop().unwrap_or(VmValue::Nil);
249 return Ok(Some((val, false)));
250 }
251 };
252
253 if frame.ip >= frame.chunk.code.len() {
255 let val = self.stack.pop().unwrap_or(VmValue::Nil);
256 let popped_frame = self.frames.pop().unwrap();
257 if self.frames.is_empty() {
258 return Ok(Some((val, false)));
259 } else {
260 self.env = popped_frame.saved_env;
261 self.stack.truncate(popped_frame.stack_base);
262 self.stack.push(val);
263 return Ok(None);
264 }
265 }
266
267 let op = frame.chunk.code[frame.ip];
268 frame.ip += 1;
269
270 match self.execute_op(op).await {
271 Ok(Some(val)) => Ok(Some((val, false))),
272 Ok(None) => Ok(None),
273 Err(VmError::Return(val)) => {
274 if let Some(popped_frame) = self.frames.pop() {
275 let current_depth = self.frames.len();
276 self.exception_handlers
277 .retain(|h| h.frame_depth <= current_depth);
278 if self.frames.is_empty() {
279 return Ok(Some((val, false)));
280 }
281 self.env = popped_frame.saved_env;
282 self.stack.truncate(popped_frame.stack_base);
283 self.stack.push(val);
284 Ok(None)
285 } else {
286 Ok(Some((val, false)))
287 }
288 }
289 Err(e) => match self.handle_error(e) {
290 Ok(None) => Ok(None),
291 Ok(Some(val)) => Ok(Some((val, false))),
292 Err(e) => Err(e),
293 },
294 }
295 }
296
297 pub fn start(&mut self, chunk: &Chunk) {
299 self.frames.push(CallFrame {
300 chunk: chunk.clone(),
301 ip: 0,
302 stack_base: self.stack.len(),
303 saved_env: self.env.clone(),
304 fn_name: String::new(),
305 });
306 }
307
308 pub fn register_builtin<F>(&mut self, name: &str, f: F)
310 where
311 F: Fn(&[VmValue], &mut String) -> Result<VmValue, VmError> + 'static,
312 {
313 self.builtins.insert(name.to_string(), Rc::new(f));
314 }
315
316 pub fn unregister_builtin(&mut self, name: &str) {
318 self.builtins.remove(name);
319 }
320
321 pub fn register_async_builtin<F, Fut>(&mut self, name: &str, f: F)
323 where
324 F: Fn(Vec<VmValue>) -> Fut + 'static,
325 Fut: Future<Output = Result<VmValue, VmError>> + 'static,
326 {
327 self.async_builtins
328 .insert(name.to_string(), Rc::new(move |args| Box::pin(f(args))));
329 }
330
331 fn child_vm(&self) -> Vm {
334 Vm {
335 stack: Vec::with_capacity(64),
336 env: self.env.clone(),
337 output: String::new(),
338 builtins: self.builtins.clone(),
339 async_builtins: self.async_builtins.clone(),
340 iterators: Vec::new(),
341 frames: Vec::new(),
342 exception_handlers: Vec::new(),
343 spawned_tasks: BTreeMap::new(),
344 task_counter: 0,
345 deadlines: Vec::new(),
346 breakpoints: Vec::new(),
347 step_mode: false,
348 step_frame_depth: 0,
349 stopped: false,
350 last_line: 0,
351 source_dir: None,
352 imported_paths: Vec::new(),
353 source_file: self.source_file.clone(),
354 source_text: self.source_text.clone(),
355 }
356 }
357
358 pub fn set_source_dir(&mut self, dir: &std::path::Path) {
360 self.source_dir = Some(dir.to_path_buf());
361 }
362
363 pub fn set_global(&mut self, name: &str, value: VmValue) {
365 self.env.define(name, value, false);
366 }
367
368 fn execute_import<'a>(
370 &'a mut self,
371 path: &'a str,
372 selected_names: Option<&'a [String]>,
373 ) -> Pin<Box<dyn Future<Output = Result<(), VmError>> + 'a>> {
374 Box::pin(async move {
375 use std::path::PathBuf;
376
377 let base = self
379 .source_dir
380 .clone()
381 .unwrap_or_else(|| PathBuf::from("."));
382 let mut file_path = base.join(path);
383
384 if !file_path.exists() && file_path.extension().is_none() {
386 file_path.set_extension("harn");
387 }
388
389 if !file_path.exists() {
391 for pkg_dir in [".harn/packages", ".burin/packages"] {
392 let pkg_path = base.join(pkg_dir).join(path);
393 if pkg_path.exists() {
394 file_path = if pkg_path.is_dir() {
395 let lib = pkg_path.join("lib.harn");
396 if lib.exists() {
397 lib
398 } else {
399 pkg_path
400 }
401 } else {
402 pkg_path
403 };
404 break;
405 }
406 let mut pkg_harn = pkg_path.clone();
407 pkg_harn.set_extension("harn");
408 if pkg_harn.exists() {
409 file_path = pkg_harn;
410 break;
411 }
412 }
413 }
414
415 let canonical = file_path
417 .canonicalize()
418 .unwrap_or_else(|_| file_path.clone());
419 if self.imported_paths.contains(&canonical) {
420 return Ok(()); }
422 self.imported_paths.push(canonical);
423
424 let source = std::fs::read_to_string(&file_path).map_err(|e| {
426 VmError::Runtime(format!(
427 "Import error: cannot read '{}': {e}",
428 file_path.display()
429 ))
430 })?;
431
432 let mut lexer = harn_lexer::Lexer::new(&source);
433 let tokens = lexer
434 .tokenize()
435 .map_err(|e| VmError::Runtime(format!("Import lex error: {e}")))?;
436 let mut parser = harn_parser::Parser::new(tokens);
437 let program = parser
438 .parse()
439 .map_err(|e| VmError::Runtime(format!("Import parse error: {e}")))?;
440
441 let has_pub = program
443 .iter()
444 .any(|n| matches!(&n.node, harn_parser::Node::FnDecl { is_pub: true, .. }));
445
446 for node in &program {
448 match &node.node {
449 harn_parser::Node::FnDecl {
450 name,
451 params,
452 body,
453 is_pub,
454 ..
455 } => {
456 if selected_names.is_none() && has_pub && !is_pub {
460 continue;
461 }
462 if let Some(names) = selected_names {
463 if !names.contains(name) {
464 continue;
465 }
466 }
467 let mut compiler = crate::Compiler::new();
469 let func_chunk = compiler
470 .compile_fn_body(params, body)
471 .map_err(|e| VmError::Runtime(format!("Import compile error: {e}")))?;
472 let closure = VmClosure {
473 func: func_chunk,
474 env: self.env.clone(),
475 };
476 self.env
477 .define(name, VmValue::Closure(Rc::new(closure)), false);
478 }
479 harn_parser::Node::ImportDecl { path: sub_path } => {
480 let old_dir = self.source_dir.clone();
482 if let Some(parent) = file_path.parent() {
483 self.source_dir = Some(parent.to_path_buf());
484 }
485 self.execute_import(sub_path, None).await?;
486 self.source_dir = old_dir;
487 }
488 harn_parser::Node::SelectiveImport {
489 names,
490 path: sub_path,
491 } => {
492 let old_dir = self.source_dir.clone();
493 if let Some(parent) = file_path.parent() {
494 self.source_dir = Some(parent.to_path_buf());
495 }
496 self.execute_import(sub_path, Some(names)).await?;
497 self.source_dir = old_dir;
498 }
499 _ => {} }
501 }
502
503 Ok(())
504 })
505 }
506
507 pub fn output(&self) -> &str {
509 &self.output
510 }
511
512 pub async fn execute(&mut self, chunk: &Chunk) -> Result<VmValue, VmError> {
514 self.run_chunk(chunk).await
515 }
516
517 fn handle_error(&mut self, error: VmError) -> Result<Option<VmValue>, VmError> {
519 let thrown_value = match &error {
521 VmError::Thrown(v) => v.clone(),
522 other => VmValue::String(Rc::from(other.to_string())),
523 };
524
525 if let Some(handler) = self.exception_handlers.pop() {
526 if !handler.error_type.is_empty() {
528 let matches = match &thrown_value {
529 VmValue::EnumVariant { enum_name, .. } => *enum_name == handler.error_type,
530 _ => false,
531 };
532 if !matches {
533 return self.handle_error(error);
535 }
536 }
537
538 while self.frames.len() > handler.frame_depth {
540 if let Some(frame) = self.frames.pop() {
541 self.env = frame.saved_env;
542 }
543 }
544
545 while self
547 .deadlines
548 .last()
549 .is_some_and(|d| d.1 > handler.frame_depth)
550 {
551 self.deadlines.pop();
552 }
553
554 self.stack.truncate(handler.stack_depth);
556
557 self.stack.push(thrown_value);
559
560 if let Some(frame) = self.frames.last_mut() {
562 frame.ip = handler.catch_ip;
563 }
564
565 Ok(None) } else {
567 Err(error) }
569 }
570
571 async fn run_chunk(&mut self, chunk: &Chunk) -> Result<VmValue, VmError> {
572 self.frames.push(CallFrame {
574 chunk: chunk.clone(),
575 ip: 0,
576 stack_base: self.stack.len(),
577 saved_env: self.env.clone(),
578 fn_name: String::new(),
579 });
580
581 loop {
582 if let Some(&(deadline, _)) = self.deadlines.last() {
584 if Instant::now() > deadline {
585 self.deadlines.pop();
586 let err = VmError::Thrown(VmValue::String(Rc::from("Deadline exceeded")));
587 match self.handle_error(err) {
588 Ok(None) => continue,
589 Ok(Some(val)) => return Ok(val),
590 Err(e) => return Err(e),
591 }
592 }
593 }
594
595 let frame = match self.frames.last_mut() {
597 Some(f) => f,
598 None => return Ok(self.stack.pop().unwrap_or(VmValue::Nil)),
599 };
600
601 if frame.ip >= frame.chunk.code.len() {
603 let val = self.stack.pop().unwrap_or(VmValue::Nil);
604 let popped_frame = self.frames.pop().unwrap();
605
606 if self.frames.is_empty() {
607 return Ok(val);
609 } else {
610 self.env = popped_frame.saved_env;
612 self.stack.truncate(popped_frame.stack_base);
613 self.stack.push(val);
614 continue;
615 }
616 }
617
618 let op = frame.chunk.code[frame.ip];
619 frame.ip += 1;
620
621 match self.execute_op(op).await {
622 Ok(Some(val)) => return Ok(val),
623 Ok(None) => continue,
624 Err(VmError::Return(val)) => {
625 if let Some(popped_frame) = self.frames.pop() {
627 let current_depth = self.frames.len();
629 self.exception_handlers
630 .retain(|h| h.frame_depth <= current_depth);
631
632 if self.frames.is_empty() {
633 return Ok(val);
634 }
635 self.env = popped_frame.saved_env;
636 self.stack.truncate(popped_frame.stack_base);
637 self.stack.push(val);
638 } else {
639 return Ok(val);
640 }
641 }
642 Err(e) => {
643 match self.handle_error(e) {
644 Ok(None) => continue, Ok(Some(val)) => return Ok(val),
646 Err(e) => return Err(e), }
648 }
649 }
650 }
651 }
652
653 async fn execute_op(&mut self, op: u8) -> Result<Option<VmValue>, VmError> {
658 if op == Op::Constant as u8 {
662 let frame = self.frames.last_mut().unwrap();
663 let idx = frame.chunk.read_u16(frame.ip) as usize;
664 frame.ip += 2;
665 let val = match &frame.chunk.constants[idx] {
666 Constant::Int(n) => VmValue::Int(*n),
667 Constant::Float(n) => VmValue::Float(*n),
668 Constant::String(s) => VmValue::String(Rc::from(s.as_str())),
669 Constant::Bool(b) => VmValue::Bool(*b),
670 Constant::Nil => VmValue::Nil,
671 Constant::Duration(ms) => VmValue::Duration(*ms),
672 };
673 self.stack.push(val);
674 } else if op == Op::Nil as u8 {
675 self.stack.push(VmValue::Nil);
676 } else if op == Op::True as u8 {
677 self.stack.push(VmValue::Bool(true));
678 } else if op == Op::False as u8 {
679 self.stack.push(VmValue::Bool(false));
680 } else if op == Op::GetVar as u8 {
681 let frame = self.frames.last_mut().unwrap();
682 let idx = frame.chunk.read_u16(frame.ip) as usize;
683 frame.ip += 2;
684 let name = match &frame.chunk.constants[idx] {
685 Constant::String(s) => s.clone(),
686 _ => return Err(VmError::TypeError("expected string constant".into())),
687 };
688 match self.env.get(&name) {
689 Some(val) => self.stack.push(val),
690 None => return Err(VmError::UndefinedVariable(name)),
691 }
692 } else if op == Op::DefLet as u8 {
693 let frame = self.frames.last_mut().unwrap();
694 let idx = frame.chunk.read_u16(frame.ip) as usize;
695 frame.ip += 2;
696 let name = Self::const_string(&frame.chunk.constants[idx])?;
697 let val = self.pop()?;
698 self.env.define(&name, val, false);
699 } else if op == Op::DefVar as u8 {
700 let frame = self.frames.last_mut().unwrap();
701 let idx = frame.chunk.read_u16(frame.ip) as usize;
702 frame.ip += 2;
703 let name = Self::const_string(&frame.chunk.constants[idx])?;
704 let val = self.pop()?;
705 self.env.define(&name, val, true);
706 } else if op == Op::SetVar as u8 {
707 let frame = self.frames.last_mut().unwrap();
708 let idx = frame.chunk.read_u16(frame.ip) as usize;
709 frame.ip += 2;
710 let name = Self::const_string(&frame.chunk.constants[idx])?;
711 let val = self.pop()?;
712 self.env.assign(&name, val)?;
713 } else if op == Op::Add as u8 {
714 let b = self.pop()?;
715 let a = self.pop()?;
716 self.stack.push(self.add(a, b));
717 } else if op == Op::Sub as u8 {
718 let b = self.pop()?;
719 let a = self.pop()?;
720 self.stack.push(self.sub(a, b));
721 } else if op == Op::Mul as u8 {
722 let b = self.pop()?;
723 let a = self.pop()?;
724 self.stack.push(self.mul(a, b));
725 } else if op == Op::Div as u8 {
726 let b = self.pop()?;
727 let a = self.pop()?;
728 self.stack.push(self.div(a, b)?);
729 } else if op == Op::Mod as u8 {
730 let b = self.pop()?;
731 let a = self.pop()?;
732 self.stack.push(self.modulo(a, b)?);
733 } else if op == Op::Negate as u8 {
734 let v = self.pop()?;
735 self.stack.push(match v {
736 VmValue::Int(n) => VmValue::Int(n.wrapping_neg()),
737 VmValue::Float(n) => VmValue::Float(-n),
738 _ => {
739 return Err(VmError::Runtime(format!(
740 "Cannot negate value of type {}",
741 v.type_name()
742 )))
743 }
744 });
745 } else if op == Op::Equal as u8 {
746 let b = self.pop()?;
747 let a = self.pop()?;
748 self.stack.push(VmValue::Bool(values_equal(&a, &b)));
749 } else if op == Op::NotEqual as u8 {
750 let b = self.pop()?;
751 let a = self.pop()?;
752 self.stack.push(VmValue::Bool(!values_equal(&a, &b)));
753 } else if op == Op::Less as u8 {
754 let b = self.pop()?;
755 let a = self.pop()?;
756 self.stack.push(VmValue::Bool(compare_values(&a, &b) < 0));
757 } else if op == Op::Greater as u8 {
758 let b = self.pop()?;
759 let a = self.pop()?;
760 self.stack.push(VmValue::Bool(compare_values(&a, &b) > 0));
761 } else if op == Op::LessEqual as u8 {
762 let b = self.pop()?;
763 let a = self.pop()?;
764 self.stack.push(VmValue::Bool(compare_values(&a, &b) <= 0));
765 } else if op == Op::GreaterEqual as u8 {
766 let b = self.pop()?;
767 let a = self.pop()?;
768 self.stack.push(VmValue::Bool(compare_values(&a, &b) >= 0));
769 } else if op == Op::Not as u8 {
770 let v = self.pop()?;
771 self.stack.push(VmValue::Bool(!v.is_truthy()));
772 } else if op == Op::Jump as u8 {
773 let frame = self.frames.last_mut().unwrap();
774 let target = frame.chunk.read_u16(frame.ip) as usize;
775 frame.ip = target;
776 } else if op == Op::JumpIfFalse as u8 {
777 let frame = self.frames.last_mut().unwrap();
778 let target = frame.chunk.read_u16(frame.ip) as usize;
779 frame.ip += 2;
780 let val = self.peek()?;
781 if !val.is_truthy() {
782 let frame = self.frames.last_mut().unwrap();
783 frame.ip = target;
784 }
785 } else if op == Op::JumpIfTrue as u8 {
786 let frame = self.frames.last_mut().unwrap();
787 let target = frame.chunk.read_u16(frame.ip) as usize;
788 frame.ip += 2;
789 let val = self.peek()?;
790 if val.is_truthy() {
791 let frame = self.frames.last_mut().unwrap();
792 frame.ip = target;
793 }
794 } else if op == Op::Pop as u8 {
795 self.pop()?;
796 } else if op == Op::Call as u8 {
797 let frame = self.frames.last_mut().unwrap();
798 let argc = frame.chunk.code[frame.ip] as usize;
799 frame.ip += 1;
800 let functions = frame.chunk.functions.clone();
802
803 let args: Vec<VmValue> = self.stack.split_off(self.stack.len().saturating_sub(argc));
805 let callee = self.pop()?;
806
807 match callee {
808 VmValue::String(name) => {
809 if name.as_ref() == "await" {
810 let task_id = args.first().and_then(|a| match a {
811 VmValue::TaskHandle(id) => Some(id.clone()),
812 _ => None,
813 });
814 if let Some(id) = task_id {
815 if let Some(handle) = self.spawned_tasks.remove(&id) {
816 let (result, task_output) = handle.await.map_err(|e| {
817 VmError::Runtime(format!("Task join error: {e}"))
818 })??;
819 self.output.push_str(&task_output);
820 self.stack.push(result);
821 } else {
822 self.stack.push(VmValue::Nil);
823 }
824 } else {
825 self.stack
826 .push(args.into_iter().next().unwrap_or(VmValue::Nil));
827 }
828 } else if name.as_ref() == "cancel" {
829 if let Some(VmValue::TaskHandle(id)) = args.first() {
830 if let Some(handle) = self.spawned_tasks.remove(id) {
831 handle.abort();
832 }
833 }
834 self.stack.push(VmValue::Nil);
835 } else if let Some(VmValue::Closure(closure)) = self.env.get(&name) {
836 self.push_closure_frame(&closure, &args, &functions)?;
838 } else if let Some(builtin) = self.builtins.get(name.as_ref()).cloned() {
840 let result = builtin(&args, &mut self.output)?;
841 self.stack.push(result);
842 } else if let Some(async_builtin) =
843 self.async_builtins.get(name.as_ref()).cloned()
844 {
845 let result = async_builtin(args).await?;
846 self.stack.push(result);
847 } else {
848 return Err(VmError::UndefinedBuiltin(name.to_string()));
849 }
850 }
851 VmValue::Closure(closure) => {
852 self.push_closure_frame(&closure, &args, &functions)?;
853 }
854 _ => {
855 return Err(VmError::TypeError(format!(
856 "Cannot call {}",
857 callee.display()
858 )));
859 }
860 }
861 } else if op == Op::TailCall as u8 {
862 let frame = self.frames.last_mut().unwrap();
863 let argc = frame.chunk.code[frame.ip] as usize;
864 frame.ip += 1;
865
866 let args: Vec<VmValue> = self.stack.split_off(self.stack.len().saturating_sub(argc));
867 let callee = self.pop()?;
868
869 let resolved_closure = match &callee {
871 VmValue::Closure(cl) => Some(Rc::clone(cl)),
872 VmValue::String(name) => {
873 if let Some(VmValue::Closure(cl)) = self.env.get(name) {
874 Some(cl)
875 } else {
876 None
877 }
878 }
879 _ => None,
880 };
881
882 if let Some(closure) = resolved_closure {
883 let popped = self.frames.pop().unwrap();
886 let stack_base = popped.stack_base;
887 let parent_env = popped.saved_env;
888
889 self.stack.truncate(stack_base);
891
892 let mut call_env = Self::merge_env_into_closure(&parent_env, &closure);
894 call_env.push_scope();
895 for (i, param) in closure.func.params.iter().enumerate() {
896 let val = args.get(i).cloned().unwrap_or(VmValue::Nil);
897 call_env.define(param, val, false);
898 }
899 self.env = call_env;
900
901 self.frames.push(CallFrame {
903 chunk: closure.func.chunk.clone(),
904 ip: 0,
905 stack_base,
906 saved_env: parent_env,
907 fn_name: closure.func.name.clone(),
908 });
909 } else {
911 match callee {
913 VmValue::String(name) => {
914 if let Some(builtin) = self.builtins.get(name.as_ref()).cloned() {
915 let result = builtin(&args, &mut self.output)?;
916 self.stack.push(result);
917 } else if let Some(async_builtin) =
918 self.async_builtins.get(name.as_ref()).cloned()
919 {
920 let result = async_builtin(args).await?;
921 self.stack.push(result);
922 } else {
923 return Err(VmError::UndefinedBuiltin(name.to_string()));
924 }
925 }
926 _ => {
927 return Err(VmError::TypeError(format!(
928 "Cannot call {}",
929 callee.display()
930 )));
931 }
932 }
933 }
935 } else if op == Op::Return as u8 {
936 let val = self.pop().unwrap_or(VmValue::Nil);
937 return Err(VmError::Return(val));
938 } else if op == Op::Closure as u8 {
939 let frame = self.frames.last_mut().unwrap();
940 let fn_idx = frame.chunk.read_u16(frame.ip) as usize;
941 frame.ip += 2;
942 let func = frame.chunk.functions[fn_idx].clone();
943 let closure = VmClosure {
944 func,
945 env: self.env.clone(),
946 };
947 self.stack.push(VmValue::Closure(Rc::new(closure)));
948 } else if op == Op::BuildList as u8 {
949 let frame = self.frames.last_mut().unwrap();
950 let count = frame.chunk.read_u16(frame.ip) as usize;
951 frame.ip += 2;
952 let items = self.stack.split_off(self.stack.len().saturating_sub(count));
953 self.stack.push(VmValue::List(Rc::new(items)));
954 } else if op == Op::BuildDict as u8 {
955 let frame = self.frames.last_mut().unwrap();
956 let count = frame.chunk.read_u16(frame.ip) as usize;
957 frame.ip += 2;
958 let pairs = self
959 .stack
960 .split_off(self.stack.len().saturating_sub(count * 2));
961 let mut map = BTreeMap::new();
962 for pair in pairs.chunks(2) {
963 if pair.len() == 2 {
964 let key = pair[0].display();
965 map.insert(key, pair[1].clone());
966 }
967 }
968 self.stack.push(VmValue::Dict(Rc::new(map)));
969 } else if op == Op::Subscript as u8 {
970 let idx = self.pop()?;
971 let obj = self.pop()?;
972 let result = match (&obj, &idx) {
973 (VmValue::List(items), VmValue::Int(i)) => {
974 if *i < 0 {
975 let pos = items.len() as i64 + *i;
976 if pos < 0 {
977 VmValue::Nil
978 } else {
979 items.get(pos as usize).cloned().unwrap_or(VmValue::Nil)
980 }
981 } else {
982 items.get(*i as usize).cloned().unwrap_or(VmValue::Nil)
983 }
984 }
985 (VmValue::Dict(map), _) => map.get(&idx.display()).cloned().unwrap_or(VmValue::Nil),
986 (VmValue::String(s), VmValue::Int(i)) => {
987 if *i < 0 {
988 let pos = s.chars().count() as i64 + *i;
989 if pos < 0 {
990 VmValue::Nil
991 } else {
992 s.chars()
993 .nth(pos as usize)
994 .map(|c| VmValue::String(Rc::from(c.to_string())))
995 .unwrap_or(VmValue::Nil)
996 }
997 } else {
998 s.chars()
999 .nth(*i as usize)
1000 .map(|c| VmValue::String(Rc::from(c.to_string())))
1001 .unwrap_or(VmValue::Nil)
1002 }
1003 }
1004 _ => {
1005 return Err(VmError::TypeError(format!(
1006 "cannot index into {} with {}",
1007 obj.type_name(),
1008 idx.type_name()
1009 )));
1010 }
1011 };
1012 self.stack.push(result);
1013 } else if op == Op::GetProperty as u8 {
1014 let frame = self.frames.last_mut().unwrap();
1015 let idx = frame.chunk.read_u16(frame.ip) as usize;
1016 frame.ip += 2;
1017 let name = Self::const_string(&frame.chunk.constants[idx])?;
1018 let obj = self.pop()?;
1019 let result = match &obj {
1020 VmValue::Dict(map) => map.get(&name).cloned().unwrap_or(VmValue::Nil),
1021 VmValue::List(items) => match name.as_str() {
1022 "count" => VmValue::Int(items.len() as i64),
1023 "empty" => VmValue::Bool(items.is_empty()),
1024 "first" => items.first().cloned().unwrap_or(VmValue::Nil),
1025 "last" => items.last().cloned().unwrap_or(VmValue::Nil),
1026 _ => VmValue::Nil,
1027 },
1028 VmValue::String(s) => match name.as_str() {
1029 "count" => VmValue::Int(s.chars().count() as i64),
1030 "empty" => VmValue::Bool(s.is_empty()),
1031 _ => VmValue::Nil,
1032 },
1033 VmValue::EnumVariant {
1034 variant, fields, ..
1035 } => match name.as_str() {
1036 "variant" => VmValue::String(Rc::from(variant.as_str())),
1037 "fields" => VmValue::List(Rc::new(fields.clone())),
1038 _ => VmValue::Nil,
1039 },
1040 VmValue::StructInstance { fields, .. } => {
1041 fields.get(&name).cloned().unwrap_or(VmValue::Nil)
1042 }
1043 VmValue::Nil => {
1044 return Err(VmError::TypeError(format!(
1045 "cannot access property `{name}` on nil"
1046 )));
1047 }
1048 _ => {
1049 return Err(VmError::TypeError(format!(
1050 "cannot access property `{name}` on {}",
1051 obj.type_name()
1052 )));
1053 }
1054 };
1055 self.stack.push(result);
1056 } else if op == Op::SetProperty as u8 {
1057 let frame = self.frames.last_mut().unwrap();
1058 let prop_idx = frame.chunk.read_u16(frame.ip) as usize;
1059 frame.ip += 2;
1060 let var_idx = frame.chunk.read_u16(frame.ip) as usize;
1061 frame.ip += 2;
1062 let prop_name = Self::const_string(&frame.chunk.constants[prop_idx])?;
1063 let var_name = Self::const_string(&frame.chunk.constants[var_idx])?;
1064 let new_value = self.pop()?;
1065 if let Some(obj) = self.env.get(&var_name) {
1066 match obj {
1067 VmValue::Dict(map) => {
1068 let mut new_map = (*map).clone();
1069 new_map.insert(prop_name, new_value);
1070 self.env
1071 .assign(&var_name, VmValue::Dict(Rc::new(new_map)))?;
1072 }
1073 VmValue::StructInstance {
1074 struct_name,
1075 fields,
1076 } => {
1077 let mut new_fields = fields.clone();
1078 new_fields.insert(prop_name, new_value);
1079 self.env.assign(
1080 &var_name,
1081 VmValue::StructInstance {
1082 struct_name,
1083 fields: new_fields,
1084 },
1085 )?;
1086 }
1087 _ => {
1088 return Err(VmError::TypeError(format!(
1089 "cannot set property `{prop_name}` on {}",
1090 obj.type_name()
1091 )));
1092 }
1093 }
1094 }
1095 } else if op == Op::SetSubscript as u8 {
1096 let frame = self.frames.last_mut().unwrap();
1097 let var_idx = frame.chunk.read_u16(frame.ip) as usize;
1098 frame.ip += 2;
1099 let var_name = Self::const_string(&frame.chunk.constants[var_idx])?;
1100 let index = self.pop()?;
1101 let new_value = self.pop()?;
1102 if let Some(obj) = self.env.get(&var_name) {
1103 match obj {
1104 VmValue::List(items) => {
1105 if let Some(i) = index.as_int() {
1106 let mut new_items = (*items).clone();
1107 let idx = if i < 0 {
1108 (new_items.len() as i64 + i).max(0) as usize
1109 } else {
1110 i as usize
1111 };
1112 if idx < new_items.len() {
1113 new_items[idx] = new_value;
1114 self.env
1115 .assign(&var_name, VmValue::List(Rc::new(new_items)))?;
1116 }
1117 }
1118 }
1119 VmValue::Dict(map) => {
1120 let key = index.display();
1121 let mut new_map = (*map).clone();
1122 new_map.insert(key, new_value);
1123 self.env
1124 .assign(&var_name, VmValue::Dict(Rc::new(new_map)))?;
1125 }
1126 _ => {}
1127 }
1128 }
1129 } else if op == Op::MethodCall as u8 {
1130 let frame = self.frames.last_mut().unwrap();
1131 let name_idx = frame.chunk.read_u16(frame.ip) as usize;
1132 frame.ip += 2;
1133 let argc = frame.chunk.code[frame.ip] as usize;
1134 frame.ip += 1;
1135 let method = Self::const_string(&frame.chunk.constants[name_idx])?;
1136 let functions = frame.chunk.functions.clone();
1137 let args: Vec<VmValue> = self.stack.split_off(self.stack.len().saturating_sub(argc));
1138 let obj = self.pop()?;
1139 let result = self.call_method(obj, &method, &args, &functions).await?;
1140 self.stack.push(result);
1141 } else if op == Op::Concat as u8 {
1142 let frame = self.frames.last_mut().unwrap();
1143 let count = frame.chunk.read_u16(frame.ip) as usize;
1144 frame.ip += 2;
1145 let parts = self.stack.split_off(self.stack.len().saturating_sub(count));
1146 let result: String = parts.iter().map(|p| p.display()).collect();
1147 self.stack.push(VmValue::String(Rc::from(result)));
1148 } else if op == Op::Pipe as u8 {
1149 let callable = self.pop()?;
1150 let value = self.pop()?;
1151 let functions = self.frames.last().unwrap().chunk.functions.clone();
1152 match callable {
1153 VmValue::Closure(closure) => {
1154 self.push_closure_frame(&closure, &[value], &functions)?;
1155 }
1156 VmValue::String(name) => {
1157 if let Some(VmValue::Closure(closure)) = self.env.get(&name) {
1158 self.push_closure_frame(&closure, &[value], &functions)?;
1159 } else if let Some(builtin) = self.builtins.get(name.as_ref()) {
1160 let result = builtin(&[value], &mut self.output)?;
1161 self.stack.push(result);
1162 } else if let Some(async_builtin) =
1163 self.async_builtins.get(name.as_ref()).cloned()
1164 {
1165 let result = async_builtin(vec![value]).await?;
1166 self.stack.push(result);
1167 } else {
1168 return Err(VmError::UndefinedBuiltin(name.to_string()));
1169 }
1170 }
1171 _ => {
1172 return Err(VmError::TypeError(format!(
1173 "cannot pipe into {}",
1174 callable.type_name()
1175 )));
1176 }
1177 }
1178 } else if op == Op::Dup as u8 {
1179 let val = self.peek()?.clone();
1180 self.stack.push(val);
1181 } else if op == Op::Swap as u8 {
1182 let len = self.stack.len();
1183 if len >= 2 {
1184 self.stack.swap(len - 1, len - 2);
1185 }
1186 } else if op == Op::IterInit as u8 {
1187 let iterable = self.pop()?;
1188 let items = match iterable {
1189 VmValue::List(items) => (*items).clone(),
1190 VmValue::Dict(map) => map
1191 .iter()
1192 .map(|(k, v)| {
1193 VmValue::Dict(Rc::new(BTreeMap::from([
1194 ("key".to_string(), VmValue::String(Rc::from(k.as_str()))),
1195 ("value".to_string(), v.clone()),
1196 ])))
1197 })
1198 .collect(),
1199 _ => Vec::new(),
1200 };
1201 self.iterators.push((items, 0));
1202 } else if op == Op::IterNext as u8 {
1203 let frame = self.frames.last_mut().unwrap();
1204 let target = frame.chunk.read_u16(frame.ip) as usize;
1205 frame.ip += 2;
1206 if let Some((items, idx)) = self.iterators.last_mut() {
1207 if *idx < items.len() {
1208 let item = items[*idx].clone();
1209 *idx += 1;
1210 self.stack.push(item);
1211 } else {
1212 self.iterators.pop();
1213 let frame = self.frames.last_mut().unwrap();
1214 frame.ip = target;
1215 }
1216 } else {
1217 let frame = self.frames.last_mut().unwrap();
1218 frame.ip = target;
1219 }
1220 } else if op == Op::PopIterator as u8 {
1221 self.iterators.pop();
1222 } else if op == Op::Throw as u8 {
1223 let val = self.pop()?;
1224 return Err(VmError::Thrown(val));
1225 } else if op == Op::TryCatchSetup as u8 {
1226 let frame = self.frames.last_mut().unwrap();
1227 let catch_offset = frame.chunk.read_u16(frame.ip) as usize;
1228 frame.ip += 2;
1229 let type_idx = frame.chunk.read_u16(frame.ip) as usize;
1231 frame.ip += 2;
1232 let error_type = match &frame.chunk.constants[type_idx] {
1233 Constant::String(s) => s.clone(),
1234 _ => String::new(),
1235 };
1236 self.exception_handlers.push(ExceptionHandler {
1237 catch_ip: catch_offset,
1238 stack_depth: self.stack.len(),
1239 frame_depth: self.frames.len(),
1240 error_type,
1241 });
1242 } else if op == Op::PopHandler as u8 {
1243 self.exception_handlers.pop();
1244 } else if op == Op::Parallel as u8 {
1245 let closure = self.pop()?;
1246 let count_val = self.pop()?;
1247 let count = match &count_val {
1248 VmValue::Int(n) => (*n).max(0) as usize,
1249 _ => 0,
1250 };
1251 if let VmValue::Closure(closure) = closure {
1252 let mut handles = Vec::with_capacity(count);
1253 for i in 0..count {
1254 let mut child = self.child_vm();
1255 let closure = closure.clone();
1256 handles.push(tokio::task::spawn_local(async move {
1257 let result = child
1258 .call_closure(&closure, &[VmValue::Int(i as i64)], &[])
1259 .await?;
1260 Ok((result, std::mem::take(&mut child.output)))
1261 }));
1262 }
1263 let mut results = vec![VmValue::Nil; count];
1264 for (i, handle) in handles.into_iter().enumerate() {
1265 let (val, task_output): (VmValue, String) = handle
1266 .await
1267 .map_err(|e| VmError::Runtime(format!("Parallel task error: {e}")))??;
1268 self.output.push_str(&task_output);
1269 results[i] = val;
1270 }
1271 self.stack.push(VmValue::List(Rc::new(results)));
1272 } else {
1273 self.stack.push(VmValue::Nil);
1274 }
1275 } else if op == Op::ParallelMap as u8 {
1276 let closure = self.pop()?;
1277 let list_val = self.pop()?;
1278 match (&list_val, &closure) {
1279 (VmValue::List(items), VmValue::Closure(closure)) => {
1280 let len = items.len();
1281 let mut handles = Vec::with_capacity(len);
1282 for item in items.iter() {
1283 let mut child = self.child_vm();
1284 let closure = closure.clone();
1285 let item = item.clone();
1286 handles.push(tokio::task::spawn_local(async move {
1287 let result = child.call_closure(&closure, &[item], &[]).await?;
1288 Ok((result, std::mem::take(&mut child.output)))
1289 }));
1290 }
1291 let mut results = Vec::with_capacity(len);
1292 for handle in handles {
1293 let (val, task_output): (VmValue, String) = handle
1294 .await
1295 .map_err(|e| VmError::Runtime(format!("Parallel map error: {e}")))??;
1296 self.output.push_str(&task_output);
1297 results.push(val);
1298 }
1299 self.stack.push(VmValue::List(Rc::new(results)));
1300 }
1301 _ => self.stack.push(VmValue::Nil),
1302 }
1303 } else if op == Op::Spawn as u8 {
1304 let closure = self.pop()?;
1305 if let VmValue::Closure(closure) = closure {
1306 self.task_counter += 1;
1307 let task_id = format!("vm_task_{}", self.task_counter);
1308 let mut child = self.child_vm();
1309 let handle = tokio::task::spawn_local(async move {
1310 let result = child.call_closure(&closure, &[], &[]).await?;
1311 Ok((result, std::mem::take(&mut child.output)))
1312 });
1313 self.spawned_tasks.insert(task_id.clone(), handle);
1314 self.stack.push(VmValue::TaskHandle(task_id));
1315 } else {
1316 self.stack.push(VmValue::Nil);
1317 }
1318 } else if op == Op::Import as u8 {
1319 let frame = self.frames.last_mut().unwrap();
1320 let path_idx = frame.chunk.read_u16(frame.ip) as usize;
1321 frame.ip += 2;
1322 let import_path = Self::const_string(&frame.chunk.constants[path_idx])?;
1323 self.execute_import(&import_path, None).await?;
1324 } else if op == Op::SelectiveImport as u8 {
1325 let frame = self.frames.last_mut().unwrap();
1326 let path_idx = frame.chunk.read_u16(frame.ip) as usize;
1327 frame.ip += 2;
1328 let names_idx = frame.chunk.read_u16(frame.ip) as usize;
1329 frame.ip += 2;
1330 let import_path = Self::const_string(&frame.chunk.constants[path_idx])?;
1331 let names_str = Self::const_string(&frame.chunk.constants[names_idx])?;
1332 let names: Vec<String> = names_str.split(',').map(|s| s.to_string()).collect();
1333 self.execute_import(&import_path, Some(&names)).await?;
1334 } else if op == Op::DeadlineSetup as u8 {
1335 let dur_val = self.pop()?;
1336 let ms = match &dur_val {
1337 VmValue::Duration(ms) => *ms,
1338 VmValue::Int(n) => (*n).max(0) as u64,
1339 _ => 30_000,
1340 };
1341 let deadline = Instant::now() + std::time::Duration::from_millis(ms);
1342 self.deadlines.push((deadline, self.frames.len()));
1343 } else if op == Op::DeadlineEnd as u8 {
1344 self.deadlines.pop();
1345 } else if op == Op::BuildEnum as u8 {
1346 let frame = self.frames.last_mut().unwrap();
1347 let enum_idx = frame.chunk.read_u16(frame.ip) as usize;
1348 frame.ip += 2;
1349 let variant_idx = frame.chunk.read_u16(frame.ip) as usize;
1350 frame.ip += 2;
1351 let field_count = frame.chunk.read_u16(frame.ip) as usize;
1352 frame.ip += 2;
1353 let enum_name = Self::const_string(&frame.chunk.constants[enum_idx])?;
1354 let variant = Self::const_string(&frame.chunk.constants[variant_idx])?;
1355 let fields = self
1356 .stack
1357 .split_off(self.stack.len().saturating_sub(field_count));
1358 self.stack.push(VmValue::EnumVariant {
1359 enum_name,
1360 variant,
1361 fields,
1362 });
1363 } else if op == Op::MatchEnum as u8 {
1364 let frame = self.frames.last_mut().unwrap();
1365 let enum_idx = frame.chunk.read_u16(frame.ip) as usize;
1366 frame.ip += 2;
1367 let variant_idx = frame.chunk.read_u16(frame.ip) as usize;
1368 frame.ip += 2;
1369 let enum_name = Self::const_string(&frame.chunk.constants[enum_idx])?;
1370 let variant_name = Self::const_string(&frame.chunk.constants[variant_idx])?;
1371 let val = self.pop()?;
1372 let matches = match &val {
1373 VmValue::EnumVariant {
1374 enum_name: en,
1375 variant: vn,
1376 ..
1377 } => *en == enum_name && *vn == variant_name,
1378 _ => false,
1379 };
1380 self.stack.push(val);
1382 self.stack.push(VmValue::Bool(matches));
1383 } else {
1384 return Err(VmError::InvalidInstruction(op));
1385 }
1386
1387 Ok(None)
1388 }
1389
1390 const MAX_FRAMES: usize = 512;
1391
1392 fn merge_env_into_closure(caller_env: &VmEnv, closure: &VmClosure) -> VmEnv {
1394 let mut call_env = closure.env.clone();
1395 for scope in &caller_env.scopes {
1396 for (name, (val, mutable)) in &scope.vars {
1397 if call_env.get(name).is_none() {
1398 call_env.define(name, val.clone(), *mutable);
1399 }
1400 }
1401 }
1402 call_env
1403 }
1404
1405 fn push_closure_frame(
1407 &mut self,
1408 closure: &VmClosure,
1409 args: &[VmValue],
1410 _parent_functions: &[CompiledFunction],
1411 ) -> Result<(), VmError> {
1412 if self.frames.len() >= Self::MAX_FRAMES {
1413 return Err(VmError::StackOverflow);
1414 }
1415 let saved_env = self.env.clone();
1416
1417 let mut call_env = Self::merge_env_into_closure(&saved_env, closure);
1418 call_env.push_scope();
1419
1420 for (i, param) in closure.func.params.iter().enumerate() {
1421 let val = args.get(i).cloned().unwrap_or(VmValue::Nil);
1422 call_env.define(param, val, false);
1423 }
1424
1425 self.env = call_env;
1426
1427 self.frames.push(CallFrame {
1428 chunk: closure.func.chunk.clone(),
1429 ip: 0,
1430 stack_base: self.stack.len(),
1431 saved_env,
1432 fn_name: closure.func.name.clone(),
1433 });
1434
1435 Ok(())
1436 }
1437
1438 fn pop(&mut self) -> Result<VmValue, VmError> {
1439 self.stack.pop().ok_or(VmError::StackUnderflow)
1440 }
1441
1442 fn peek(&self) -> Result<&VmValue, VmError> {
1443 self.stack.last().ok_or(VmError::StackUnderflow)
1444 }
1445
1446 fn const_string(c: &Constant) -> Result<String, VmError> {
1447 match c {
1448 Constant::String(s) => Ok(s.clone()),
1449 _ => Err(VmError::TypeError("expected string constant".into())),
1450 }
1451 }
1452
1453 fn call_closure<'a>(
1456 &'a mut self,
1457 closure: &'a VmClosure,
1458 args: &'a [VmValue],
1459 _parent_functions: &'a [CompiledFunction],
1460 ) -> Pin<Box<dyn Future<Output = Result<VmValue, VmError>> + 'a>> {
1461 Box::pin(async move {
1462 let saved_env = self.env.clone();
1463 let saved_frames = std::mem::take(&mut self.frames);
1464 let saved_handlers = std::mem::take(&mut self.exception_handlers);
1465 let saved_iterators = std::mem::take(&mut self.iterators);
1466 let saved_deadlines = std::mem::take(&mut self.deadlines);
1467
1468 let mut call_env = Self::merge_env_into_closure(&saved_env, closure);
1469 call_env.push_scope();
1470
1471 for (i, param) in closure.func.params.iter().enumerate() {
1472 let val = args.get(i).cloned().unwrap_or(VmValue::Nil);
1473 call_env.define(param, val, false);
1474 }
1475
1476 self.env = call_env;
1477 let result = self.run_chunk(&closure.func.chunk).await;
1478
1479 self.env = saved_env;
1480 self.frames = saved_frames;
1481 self.exception_handlers = saved_handlers;
1482 self.iterators = saved_iterators;
1483 self.deadlines = saved_deadlines;
1484
1485 result
1486 })
1487 }
1488
1489 fn call_method<'a>(
1490 &'a mut self,
1491 obj: VmValue,
1492 method: &'a str,
1493 args: &'a [VmValue],
1494 functions: &'a [CompiledFunction],
1495 ) -> Pin<Box<dyn Future<Output = Result<VmValue, VmError>> + 'a>> {
1496 Box::pin(async move {
1497 match &obj {
1498 VmValue::String(s) => match method {
1499 "count" => Ok(VmValue::Int(s.chars().count() as i64)),
1500 "empty" => Ok(VmValue::Bool(s.is_empty())),
1501 "contains" => Ok(VmValue::Bool(
1502 s.contains(&*args.first().map(|a| a.display()).unwrap_or_default()),
1503 )),
1504 "replace" if args.len() >= 2 => Ok(VmValue::String(Rc::from(
1505 s.replace(&args[0].display(), &args[1].display()),
1506 ))),
1507 "split" => {
1508 let sep = args.first().map(|a| a.display()).unwrap_or(",".into());
1509 Ok(VmValue::List(Rc::new(
1510 s.split(&*sep)
1511 .map(|p| VmValue::String(Rc::from(p)))
1512 .collect(),
1513 )))
1514 }
1515 "trim" => Ok(VmValue::String(Rc::from(s.trim()))),
1516 "starts_with" => Ok(VmValue::Bool(
1517 s.starts_with(&*args.first().map(|a| a.display()).unwrap_or_default()),
1518 )),
1519 "ends_with" => Ok(VmValue::Bool(
1520 s.ends_with(&*args.first().map(|a| a.display()).unwrap_or_default()),
1521 )),
1522 "lowercase" => Ok(VmValue::String(Rc::from(s.to_lowercase()))),
1523 "uppercase" => Ok(VmValue::String(Rc::from(s.to_uppercase()))),
1524 "substring" => {
1525 let start = args.first().and_then(|a| a.as_int()).unwrap_or(0);
1526 let len = s.chars().count() as i64;
1527 let start = start.max(0).min(len) as usize;
1528 let end =
1529 args.get(1).and_then(|a| a.as_int()).unwrap_or(len).min(len) as usize;
1530 let end = end.max(start);
1531 let substr: String = s.chars().skip(start).take(end - start).collect();
1532 Ok(VmValue::String(Rc::from(substr)))
1533 }
1534 "index_of" => {
1535 let needle = args.first().map(|a| a.display()).unwrap_or_default();
1536 Ok(VmValue::Int(
1537 s.find(&needle).map(|i| i as i64).unwrap_or(-1),
1538 ))
1539 }
1540 "chars" => Ok(VmValue::List(Rc::new(
1541 s.chars()
1542 .map(|c| VmValue::String(Rc::from(c.to_string())))
1543 .collect(),
1544 ))),
1545 "repeat" => {
1546 let n = args.first().and_then(|a| a.as_int()).unwrap_or(1);
1547 Ok(VmValue::String(Rc::from(s.repeat(n.max(0) as usize))))
1548 }
1549 "reverse" => Ok(VmValue::String(Rc::from(
1550 s.chars().rev().collect::<String>(),
1551 ))),
1552 "pad_left" => {
1553 let width = args.first().and_then(|a| a.as_int()).unwrap_or(0) as usize;
1554 let pad_char = args
1555 .get(1)
1556 .map(|a| a.display())
1557 .and_then(|s| s.chars().next())
1558 .unwrap_or(' ');
1559 let current_len = s.chars().count();
1560 if current_len >= width {
1561 Ok(VmValue::String(Rc::clone(s)))
1562 } else {
1563 let padding: String =
1564 std::iter::repeat_n(pad_char, width - current_len).collect();
1565 Ok(VmValue::String(Rc::from(format!("{padding}{s}"))))
1566 }
1567 }
1568 "pad_right" => {
1569 let width = args.first().and_then(|a| a.as_int()).unwrap_or(0) as usize;
1570 let pad_char = args
1571 .get(1)
1572 .map(|a| a.display())
1573 .and_then(|s| s.chars().next())
1574 .unwrap_or(' ');
1575 let current_len = s.chars().count();
1576 if current_len >= width {
1577 Ok(VmValue::String(Rc::clone(s)))
1578 } else {
1579 let padding: String =
1580 std::iter::repeat_n(pad_char, width - current_len).collect();
1581 Ok(VmValue::String(Rc::from(format!("{s}{padding}"))))
1582 }
1583 }
1584 _ => Ok(VmValue::Nil),
1585 },
1586 VmValue::List(items) => match method {
1587 "count" => Ok(VmValue::Int(items.len() as i64)),
1588 "empty" => Ok(VmValue::Bool(items.is_empty())),
1589 "map" => {
1590 if let Some(VmValue::Closure(closure)) = args.first() {
1591 let mut results = Vec::new();
1592 for item in items.iter() {
1593 results.push(
1594 self.call_closure(closure, &[item.clone()], functions)
1595 .await?,
1596 );
1597 }
1598 Ok(VmValue::List(Rc::new(results)))
1599 } else {
1600 Ok(VmValue::Nil)
1601 }
1602 }
1603 "filter" => {
1604 if let Some(VmValue::Closure(closure)) = args.first() {
1605 let mut results = Vec::new();
1606 for item in items.iter() {
1607 let result = self
1608 .call_closure(closure, &[item.clone()], functions)
1609 .await?;
1610 if result.is_truthy() {
1611 results.push(item.clone());
1612 }
1613 }
1614 Ok(VmValue::List(Rc::new(results)))
1615 } else {
1616 Ok(VmValue::Nil)
1617 }
1618 }
1619 "reduce" => {
1620 if args.len() >= 2 {
1621 if let VmValue::Closure(closure) = &args[1] {
1622 let mut acc = args[0].clone();
1623 for item in items.iter() {
1624 acc = self
1625 .call_closure(closure, &[acc, item.clone()], functions)
1626 .await?;
1627 }
1628 return Ok(acc);
1629 }
1630 }
1631 Ok(VmValue::Nil)
1632 }
1633 "find" => {
1634 if let Some(VmValue::Closure(closure)) = args.first() {
1635 for item in items.iter() {
1636 let result = self
1637 .call_closure(closure, &[item.clone()], functions)
1638 .await?;
1639 if result.is_truthy() {
1640 return Ok(item.clone());
1641 }
1642 }
1643 }
1644 Ok(VmValue::Nil)
1645 }
1646 "any" => {
1647 if let Some(VmValue::Closure(closure)) = args.first() {
1648 for item in items.iter() {
1649 let result = self
1650 .call_closure(closure, &[item.clone()], functions)
1651 .await?;
1652 if result.is_truthy() {
1653 return Ok(VmValue::Bool(true));
1654 }
1655 }
1656 Ok(VmValue::Bool(false))
1657 } else {
1658 Ok(VmValue::Bool(false))
1659 }
1660 }
1661 "all" => {
1662 if let Some(VmValue::Closure(closure)) = args.first() {
1663 for item in items.iter() {
1664 let result = self
1665 .call_closure(closure, &[item.clone()], functions)
1666 .await?;
1667 if !result.is_truthy() {
1668 return Ok(VmValue::Bool(false));
1669 }
1670 }
1671 Ok(VmValue::Bool(true))
1672 } else {
1673 Ok(VmValue::Bool(true))
1674 }
1675 }
1676 "flat_map" => {
1677 if let Some(VmValue::Closure(closure)) = args.first() {
1678 let mut results = Vec::new();
1679 for item in items.iter() {
1680 let result = self
1681 .call_closure(closure, &[item.clone()], functions)
1682 .await?;
1683 if let VmValue::List(inner) = result {
1684 results.extend(inner.iter().cloned());
1685 } else {
1686 results.push(result);
1687 }
1688 }
1689 Ok(VmValue::List(Rc::new(results)))
1690 } else {
1691 Ok(VmValue::Nil)
1692 }
1693 }
1694 "sort" => {
1695 let mut sorted: Vec<VmValue> = items.iter().cloned().collect();
1696 sorted.sort_by(|a, b| compare_values(a, b).cmp(&0));
1697 Ok(VmValue::List(Rc::new(sorted)))
1698 }
1699 "sort_by" => {
1700 if let Some(VmValue::Closure(closure)) = args.first() {
1701 let mut keyed: Vec<(VmValue, VmValue)> = Vec::new();
1702 for item in items.iter() {
1703 let key = self
1704 .call_closure(closure, &[item.clone()], functions)
1705 .await?;
1706 keyed.push((item.clone(), key));
1707 }
1708 keyed.sort_by(|(_, ka), (_, kb)| compare_values(ka, kb).cmp(&0));
1709 Ok(VmValue::List(Rc::new(
1710 keyed.into_iter().map(|(v, _)| v).collect(),
1711 )))
1712 } else {
1713 Ok(VmValue::Nil)
1714 }
1715 }
1716 "reverse" => {
1717 let mut rev: Vec<VmValue> = items.iter().cloned().collect();
1718 rev.reverse();
1719 Ok(VmValue::List(Rc::new(rev)))
1720 }
1721 "join" => {
1722 let sep = if args.is_empty() {
1723 String::new()
1724 } else {
1725 args[0].display()
1726 };
1727 let joined: String = items
1728 .iter()
1729 .map(|v| v.display())
1730 .collect::<Vec<_>>()
1731 .join(&sep);
1732 Ok(VmValue::String(Rc::from(joined)))
1733 }
1734 "contains" => {
1735 let needle = args.first().unwrap_or(&VmValue::Nil);
1736 Ok(VmValue::Bool(items.iter().any(|v| values_equal(v, needle))))
1737 }
1738 "index_of" => {
1739 let needle = args.first().unwrap_or(&VmValue::Nil);
1740 let idx = items.iter().position(|v| values_equal(v, needle));
1741 Ok(VmValue::Int(idx.map(|i| i as i64).unwrap_or(-1)))
1742 }
1743 "enumerate" => {
1744 let result: Vec<VmValue> = items
1745 .iter()
1746 .enumerate()
1747 .map(|(i, v)| {
1748 VmValue::Dict(Rc::new(BTreeMap::from([
1749 ("index".to_string(), VmValue::Int(i as i64)),
1750 ("value".to_string(), v.clone()),
1751 ])))
1752 })
1753 .collect();
1754 Ok(VmValue::List(Rc::new(result)))
1755 }
1756 "zip" => {
1757 if let Some(VmValue::List(other)) = args.first() {
1758 let result: Vec<VmValue> = items
1759 .iter()
1760 .zip(other.iter())
1761 .map(|(a, b)| VmValue::List(Rc::new(vec![a.clone(), b.clone()])))
1762 .collect();
1763 Ok(VmValue::List(Rc::new(result)))
1764 } else {
1765 Ok(VmValue::List(Rc::new(Vec::new())))
1766 }
1767 }
1768 "slice" => {
1769 let len = items.len() as i64;
1770 let start_raw = args.first().and_then(|a| a.as_int()).unwrap_or(0);
1771 let start = if start_raw < 0 {
1772 (len + start_raw).max(0) as usize
1773 } else {
1774 (start_raw.min(len)) as usize
1775 };
1776 let end = if args.len() > 1 {
1777 let end_raw = args[1].as_int().unwrap_or(len);
1778 if end_raw < 0 {
1779 (len + end_raw).max(0) as usize
1780 } else {
1781 (end_raw.min(len)) as usize
1782 }
1783 } else {
1784 len as usize
1785 };
1786 let end = end.max(start);
1787 Ok(VmValue::List(Rc::new(items[start..end].to_vec())))
1788 }
1789 "unique" => {
1790 let mut seen: Vec<VmValue> = Vec::new();
1791 let mut result = Vec::new();
1792 for item in items.iter() {
1793 if !seen.iter().any(|s| values_equal(s, item)) {
1794 seen.push(item.clone());
1795 result.push(item.clone());
1796 }
1797 }
1798 Ok(VmValue::List(Rc::new(result)))
1799 }
1800 "take" => {
1801 let n = args.first().and_then(|a| a.as_int()).unwrap_or(0).max(0) as usize;
1802 Ok(VmValue::List(Rc::new(
1803 items.iter().take(n).cloned().collect(),
1804 )))
1805 }
1806 "skip" => {
1807 let n = args.first().and_then(|a| a.as_int()).unwrap_or(0).max(0) as usize;
1808 Ok(VmValue::List(Rc::new(
1809 items.iter().skip(n).cloned().collect(),
1810 )))
1811 }
1812 "sum" => {
1813 let mut int_sum: i64 = 0;
1814 let mut has_float = false;
1815 let mut float_sum: f64 = 0.0;
1816 for item in items.iter() {
1817 match item {
1818 VmValue::Int(n) => {
1819 int_sum = int_sum.wrapping_add(*n);
1820 float_sum += *n as f64;
1821 }
1822 VmValue::Float(n) => {
1823 has_float = true;
1824 float_sum += n;
1825 }
1826 _ => {}
1827 }
1828 }
1829 if has_float {
1830 Ok(VmValue::Float(float_sum))
1831 } else {
1832 Ok(VmValue::Int(int_sum))
1833 }
1834 }
1835 "min" => {
1836 if items.is_empty() {
1837 return Ok(VmValue::Nil);
1838 }
1839 let mut min_val = items[0].clone();
1840 for item in &items[1..] {
1841 if compare_values(item, &min_val) < 0 {
1842 min_val = item.clone();
1843 }
1844 }
1845 Ok(min_val)
1846 }
1847 "max" => {
1848 if items.is_empty() {
1849 return Ok(VmValue::Nil);
1850 }
1851 let mut max_val = items[0].clone();
1852 for item in &items[1..] {
1853 if compare_values(item, &max_val) > 0 {
1854 max_val = item.clone();
1855 }
1856 }
1857 Ok(max_val)
1858 }
1859 "flatten" => {
1860 let mut result = Vec::new();
1861 for item in items.iter() {
1862 if let VmValue::List(inner) = item {
1863 result.extend(inner.iter().cloned());
1864 } else {
1865 result.push(item.clone());
1866 }
1867 }
1868 Ok(VmValue::List(Rc::new(result)))
1869 }
1870 "push" => {
1871 let mut new_list: Vec<VmValue> = items.iter().cloned().collect();
1872 if let Some(item) = args.first() {
1873 new_list.push(item.clone());
1874 }
1875 Ok(VmValue::List(Rc::new(new_list)))
1876 }
1877 "pop" => {
1878 let mut new_list: Vec<VmValue> = items.iter().cloned().collect();
1879 new_list.pop();
1880 Ok(VmValue::List(Rc::new(new_list)))
1881 }
1882 _ => Ok(VmValue::Nil),
1883 },
1884 VmValue::Dict(map) => match method {
1885 "keys" => Ok(VmValue::List(Rc::new(
1886 map.keys()
1887 .map(|k| VmValue::String(Rc::from(k.as_str())))
1888 .collect(),
1889 ))),
1890 "values" => Ok(VmValue::List(Rc::new(map.values().cloned().collect()))),
1891 "entries" => Ok(VmValue::List(Rc::new(
1892 map.iter()
1893 .map(|(k, v)| {
1894 VmValue::Dict(Rc::new(BTreeMap::from([
1895 ("key".to_string(), VmValue::String(Rc::from(k.as_str()))),
1896 ("value".to_string(), v.clone()),
1897 ])))
1898 })
1899 .collect(),
1900 ))),
1901 "count" => Ok(VmValue::Int(map.len() as i64)),
1902 "has" => Ok(VmValue::Bool(map.contains_key(
1903 &args.first().map(|a| a.display()).unwrap_or_default(),
1904 ))),
1905 "merge" => {
1906 if let Some(VmValue::Dict(other)) = args.first() {
1907 let mut result = (**map).clone();
1908 result.extend(other.iter().map(|(k, v)| (k.clone(), v.clone())));
1909 Ok(VmValue::Dict(Rc::new(result)))
1910 } else {
1911 Ok(VmValue::Dict(Rc::clone(map)))
1912 }
1913 }
1914 "map_values" => {
1915 if let Some(VmValue::Closure(closure)) = args.first() {
1916 let mut result = BTreeMap::new();
1917 for (k, v) in map.iter() {
1918 let mapped =
1919 self.call_closure(closure, &[v.clone()], functions).await?;
1920 result.insert(k.clone(), mapped);
1921 }
1922 Ok(VmValue::Dict(Rc::new(result)))
1923 } else {
1924 Ok(VmValue::Nil)
1925 }
1926 }
1927 "filter" => {
1928 if let Some(VmValue::Closure(closure)) = args.first() {
1929 let mut result = BTreeMap::new();
1930 for (k, v) in map.iter() {
1931 let keep =
1932 self.call_closure(closure, &[v.clone()], functions).await?;
1933 if keep.is_truthy() {
1934 result.insert(k.clone(), v.clone());
1935 }
1936 }
1937 Ok(VmValue::Dict(Rc::new(result)))
1938 } else {
1939 Ok(VmValue::Nil)
1940 }
1941 }
1942 "remove" => {
1943 let key = args.first().map(|a| a.display()).unwrap_or_default();
1944 let mut result = (**map).clone();
1945 result.remove(&key);
1946 Ok(VmValue::Dict(Rc::new(result)))
1947 }
1948 "get" => {
1949 let key = args.first().map(|a| a.display()).unwrap_or_default();
1950 let default = args.get(1).cloned().unwrap_or(VmValue::Nil);
1951 Ok(map.get(&key).cloned().unwrap_or(default))
1952 }
1953 _ => Ok(VmValue::Nil),
1954 },
1955 _ => Ok(VmValue::Nil),
1956 }
1957 })
1958 }
1959
1960 fn add(&self, a: VmValue, b: VmValue) -> VmValue {
1963 match (&a, &b) {
1964 (VmValue::Int(x), VmValue::Int(y)) => VmValue::Int(x.wrapping_add(*y)),
1965 (VmValue::Float(x), VmValue::Float(y)) => VmValue::Float(x + y),
1966 (VmValue::Int(x), VmValue::Float(y)) => VmValue::Float(*x as f64 + y),
1967 (VmValue::Float(x), VmValue::Int(y)) => VmValue::Float(x + *y as f64),
1968 (VmValue::String(x), _) => VmValue::String(Rc::from(format!("{x}{}", b.display()))),
1969 (VmValue::List(x), VmValue::List(y)) => {
1970 let mut result = (**x).clone();
1971 result.extend(y.iter().cloned());
1972 VmValue::List(Rc::new(result))
1973 }
1974 (VmValue::Dict(x), VmValue::Dict(y)) => {
1975 let mut result = (**x).clone();
1976 result.extend(y.iter().map(|(k, v)| (k.clone(), v.clone())));
1977 VmValue::Dict(Rc::new(result))
1978 }
1979 _ => VmValue::String(Rc::from(format!("{}{}", a.display(), b.display()))),
1980 }
1981 }
1982
1983 fn sub(&self, a: VmValue, b: VmValue) -> VmValue {
1984 match (&a, &b) {
1985 (VmValue::Int(x), VmValue::Int(y)) => VmValue::Int(x.wrapping_sub(*y)),
1986 (VmValue::Float(x), VmValue::Float(y)) => VmValue::Float(x - y),
1987 (VmValue::Int(x), VmValue::Float(y)) => VmValue::Float(*x as f64 - y),
1988 (VmValue::Float(x), VmValue::Int(y)) => VmValue::Float(x - *y as f64),
1989 _ => VmValue::Nil,
1990 }
1991 }
1992
1993 fn mul(&self, a: VmValue, b: VmValue) -> VmValue {
1994 match (&a, &b) {
1995 (VmValue::Int(x), VmValue::Int(y)) => VmValue::Int(x.wrapping_mul(*y)),
1996 (VmValue::Float(x), VmValue::Float(y)) => VmValue::Float(x * y),
1997 (VmValue::Int(x), VmValue::Float(y)) => VmValue::Float(*x as f64 * y),
1998 (VmValue::Float(x), VmValue::Int(y)) => VmValue::Float(x * *y as f64),
1999 _ => VmValue::Nil,
2000 }
2001 }
2002
2003 fn div(&self, a: VmValue, b: VmValue) -> Result<VmValue, VmError> {
2004 match (&a, &b) {
2005 (VmValue::Int(_), VmValue::Int(y)) if *y == 0 => Err(VmError::DivisionByZero),
2006 (VmValue::Int(x), VmValue::Int(y)) => Ok(VmValue::Int(x / y)),
2007 (VmValue::Float(_), VmValue::Float(y)) if *y == 0.0 => Err(VmError::DivisionByZero),
2008 (VmValue::Float(x), VmValue::Float(y)) => Ok(VmValue::Float(x / y)),
2009 (VmValue::Int(_), VmValue::Float(y)) if *y == 0.0 => Err(VmError::DivisionByZero),
2010 (VmValue::Int(x), VmValue::Float(y)) => Ok(VmValue::Float(*x as f64 / y)),
2011 (VmValue::Float(_), VmValue::Int(y)) if *y == 0 => Err(VmError::DivisionByZero),
2012 (VmValue::Float(x), VmValue::Int(y)) => Ok(VmValue::Float(x / *y as f64)),
2013 _ => Err(VmError::Runtime(format!(
2014 "Cannot divide {} by {}",
2015 a.type_name(),
2016 b.type_name()
2017 ))),
2018 }
2019 }
2020
2021 fn modulo(&self, a: VmValue, b: VmValue) -> Result<VmValue, VmError> {
2022 match (&a, &b) {
2023 (VmValue::Int(_), VmValue::Int(y)) if *y == 0 => Err(VmError::DivisionByZero),
2024 (VmValue::Int(x), VmValue::Int(y)) => Ok(VmValue::Int(x % y)),
2025 (VmValue::Float(_), VmValue::Float(y)) if *y == 0.0 => Err(VmError::DivisionByZero),
2026 (VmValue::Float(x), VmValue::Float(y)) => Ok(VmValue::Float(x % y)),
2027 (VmValue::Int(_), VmValue::Float(y)) if *y == 0.0 => Err(VmError::DivisionByZero),
2028 (VmValue::Int(x), VmValue::Float(y)) => Ok(VmValue::Float(*x as f64 % y)),
2029 (VmValue::Float(_), VmValue::Int(y)) if *y == 0 => Err(VmError::DivisionByZero),
2030 (VmValue::Float(x), VmValue::Int(y)) => Ok(VmValue::Float(x % *y as f64)),
2031 _ => Err(VmError::Runtime(format!(
2032 "Cannot modulo {} by {}",
2033 a.type_name(),
2034 b.type_name()
2035 ))),
2036 }
2037 }
2038
2039 pub fn format_runtime_error(&self, error: &VmError) -> String {
2051 let source = match &self.source_text {
2052 Some(s) => s.as_str(),
2053 None => return format!("error: {error}"),
2054 };
2055 let filename = self.source_file.as_deref().unwrap_or("<unknown>");
2056
2057 let error_msg = format!("{error}");
2058 let mut out = String::new();
2059
2060 out.push_str(&format!("error: {error_msg}\n"));
2062
2063 let frames: Vec<(&str, usize)> = self
2065 .frames
2066 .iter()
2067 .map(|f| {
2068 let line = if f.ip > 0 && f.ip - 1 < f.chunk.lines.len() {
2069 f.chunk.lines[f.ip - 1] as usize
2070 } else {
2071 0
2072 };
2073 (f.fn_name.as_str(), line)
2074 })
2075 .collect();
2076
2077 if let Some((_name, line)) = frames.last() {
2078 let line = *line;
2079 if line > 0 {
2080 let gutter_width = line.to_string().len();
2081 out.push_str(&format!(
2082 "{:>width$}--> {filename}:{line}:1\n",
2083 " ",
2084 width = gutter_width + 1,
2085 ));
2086 if let Some(source_line) = source.lines().nth(line.saturating_sub(1)) {
2088 out.push_str(&format!("{:>width$} |\n", " ", width = gutter_width + 1));
2089 out.push_str(&format!(
2090 "{:>width$} | {source_line}\n",
2091 line,
2092 width = gutter_width + 1,
2093 ));
2094 out.push_str(&format!("{:>width$} |\n", " ", width = gutter_width + 1));
2095 }
2096 }
2097 }
2098
2099 if frames.len() > 1 {
2101 for (name, line) in frames.iter().rev().skip(1) {
2102 let display_name = if name.is_empty() { "pipeline" } else { name };
2103 if *line > 0 {
2104 out.push_str(&format!(
2105 " = note: called from {display_name} at {filename}:{line}\n"
2106 ));
2107 }
2108 }
2109 }
2110
2111 out
2112 }
2113}
2114
2115impl Default for Vm {
2116 fn default() -> Self {
2117 Self::new()
2118 }
2119}
2120
2121#[cfg(test)]
2122mod tests {
2123 use super::*;
2124 use crate::compiler::Compiler;
2125 use crate::stdlib::register_vm_stdlib;
2126 use harn_lexer::Lexer;
2127 use harn_parser::Parser;
2128
2129 fn run_harn(source: &str) -> (String, VmValue) {
2130 let rt = tokio::runtime::Builder::new_current_thread()
2131 .enable_all()
2132 .build()
2133 .unwrap();
2134 rt.block_on(async {
2135 let local = tokio::task::LocalSet::new();
2136 local
2137 .run_until(async {
2138 let mut lexer = Lexer::new(source);
2139 let tokens = lexer.tokenize().unwrap();
2140 let mut parser = Parser::new(tokens);
2141 let program = parser.parse().unwrap();
2142 let chunk = Compiler::new().compile(&program).unwrap();
2143
2144 let mut vm = Vm::new();
2145 register_vm_stdlib(&mut vm);
2146 let result = vm.execute(&chunk).await.unwrap();
2147 (vm.output().to_string(), result)
2148 })
2149 .await
2150 })
2151 }
2152
2153 fn run_output(source: &str) -> String {
2154 run_harn(source).0.trim_end().to_string()
2155 }
2156
2157 fn run_harn_result(source: &str) -> Result<(String, VmValue), VmError> {
2158 let rt = tokio::runtime::Builder::new_current_thread()
2159 .enable_all()
2160 .build()
2161 .unwrap();
2162 rt.block_on(async {
2163 let local = tokio::task::LocalSet::new();
2164 local
2165 .run_until(async {
2166 let mut lexer = Lexer::new(source);
2167 let tokens = lexer.tokenize().unwrap();
2168 let mut parser = Parser::new(tokens);
2169 let program = parser.parse().unwrap();
2170 let chunk = Compiler::new().compile(&program).unwrap();
2171
2172 let mut vm = Vm::new();
2173 register_vm_stdlib(&mut vm);
2174 let result = vm.execute(&chunk).await?;
2175 Ok((vm.output().to_string(), result))
2176 })
2177 .await
2178 })
2179 }
2180
2181 #[test]
2182 fn test_arithmetic() {
2183 let out =
2184 run_output("pipeline t(task) { log(2 + 3)\nlog(10 - 4)\nlog(3 * 5)\nlog(10 / 3) }");
2185 assert_eq!(out, "[harn] 5\n[harn] 6\n[harn] 15\n[harn] 3");
2186 }
2187
2188 #[test]
2189 fn test_mixed_arithmetic() {
2190 let out = run_output("pipeline t(task) { log(3 + 1.5)\nlog(10 - 2.5) }");
2191 assert_eq!(out, "[harn] 4.5\n[harn] 7.5");
2192 }
2193
2194 #[test]
2195 fn test_comparisons() {
2196 let out =
2197 run_output("pipeline t(task) { log(1 < 2)\nlog(2 > 3)\nlog(1 == 1)\nlog(1 != 2) }");
2198 assert_eq!(out, "[harn] true\n[harn] false\n[harn] true\n[harn] true");
2199 }
2200
2201 #[test]
2202 fn test_let_var() {
2203 let out = run_output("pipeline t(task) { let x = 42\nlog(x)\nvar y = 1\ny = 2\nlog(y) }");
2204 assert_eq!(out, "[harn] 42\n[harn] 2");
2205 }
2206
2207 #[test]
2208 fn test_if_else() {
2209 let out = run_output(
2210 r#"pipeline t(task) { if true { log("yes") } if false { log("wrong") } else { log("no") } }"#,
2211 );
2212 assert_eq!(out, "[harn] yes\n[harn] no");
2213 }
2214
2215 #[test]
2216 fn test_while_loop() {
2217 let out = run_output("pipeline t(task) { var i = 0\n while i < 5 { i = i + 1 }\n log(i) }");
2218 assert_eq!(out, "[harn] 5");
2219 }
2220
2221 #[test]
2222 fn test_for_in() {
2223 let out = run_output("pipeline t(task) { for item in [1, 2, 3] { log(item) } }");
2224 assert_eq!(out, "[harn] 1\n[harn] 2\n[harn] 3");
2225 }
2226
2227 #[test]
2228 fn test_fn_decl_and_call() {
2229 let out = run_output("pipeline t(task) { fn add(a, b) { return a + b }\nlog(add(3, 4)) }");
2230 assert_eq!(out, "[harn] 7");
2231 }
2232
2233 #[test]
2234 fn test_closure() {
2235 let out = run_output("pipeline t(task) { let double = { x -> x * 2 }\nlog(double(5)) }");
2236 assert_eq!(out, "[harn] 10");
2237 }
2238
2239 #[test]
2240 fn test_closure_capture() {
2241 let out = run_output(
2242 "pipeline t(task) { let base = 10\nfn offset(x) { return x + base }\nlog(offset(5)) }",
2243 );
2244 assert_eq!(out, "[harn] 15");
2245 }
2246
2247 #[test]
2248 fn test_string_concat() {
2249 let out = run_output(
2250 r#"pipeline t(task) { let a = "hello" + " " + "world"
2251log(a) }"#,
2252 );
2253 assert_eq!(out, "[harn] hello world");
2254 }
2255
2256 #[test]
2257 fn test_list_map() {
2258 let out = run_output(
2259 "pipeline t(task) { let doubled = [1, 2, 3].map({ x -> x * 2 })\nlog(doubled) }",
2260 );
2261 assert_eq!(out, "[harn] [2, 4, 6]");
2262 }
2263
2264 #[test]
2265 fn test_list_filter() {
2266 let out = run_output(
2267 "pipeline t(task) { let big = [1, 2, 3, 4, 5].filter({ x -> x > 3 })\nlog(big) }",
2268 );
2269 assert_eq!(out, "[harn] [4, 5]");
2270 }
2271
2272 #[test]
2273 fn test_list_reduce() {
2274 let out = run_output(
2275 "pipeline t(task) { let sum = [1, 2, 3, 4].reduce(0, { acc, x -> acc + x })\nlog(sum) }",
2276 );
2277 assert_eq!(out, "[harn] 10");
2278 }
2279
2280 #[test]
2281 fn test_dict_access() {
2282 let out = run_output(
2283 r#"pipeline t(task) { let d = {name: "test", value: 42}
2284log(d.name)
2285log(d.value) }"#,
2286 );
2287 assert_eq!(out, "[harn] test\n[harn] 42");
2288 }
2289
2290 #[test]
2291 fn test_dict_methods() {
2292 let out = run_output(
2293 r#"pipeline t(task) { let d = {a: 1, b: 2}
2294log(d.keys())
2295log(d.values())
2296log(d.has("a"))
2297log(d.has("z")) }"#,
2298 );
2299 assert_eq!(
2300 out,
2301 "[harn] [a, b]\n[harn] [1, 2]\n[harn] true\n[harn] false"
2302 );
2303 }
2304
2305 #[test]
2306 fn test_pipe_operator() {
2307 let out = run_output(
2308 "pipeline t(task) { fn double(x) { return x * 2 }\nlet r = 5 |> double\nlog(r) }",
2309 );
2310 assert_eq!(out, "[harn] 10");
2311 }
2312
2313 #[test]
2314 fn test_pipe_with_closure() {
2315 let out = run_output(
2316 r#"pipeline t(task) { let r = "hello world" |> { s -> s.split(" ") }
2317log(r) }"#,
2318 );
2319 assert_eq!(out, "[harn] [hello, world]");
2320 }
2321
2322 #[test]
2323 fn test_nil_coalescing() {
2324 let out = run_output(
2325 r#"pipeline t(task) { let a = nil ?? "fallback"
2326log(a)
2327let b = "present" ?? "fallback"
2328log(b) }"#,
2329 );
2330 assert_eq!(out, "[harn] fallback\n[harn] present");
2331 }
2332
2333 #[test]
2334 fn test_logical_operators() {
2335 let out =
2336 run_output("pipeline t(task) { log(true && false)\nlog(true || false)\nlog(!true) }");
2337 assert_eq!(out, "[harn] false\n[harn] true\n[harn] false");
2338 }
2339
2340 #[test]
2341 fn test_match() {
2342 let out = run_output(
2343 r#"pipeline t(task) { let x = "b"
2344match x { "a" -> { log("first") } "b" -> { log("second") } "c" -> { log("third") } } }"#,
2345 );
2346 assert_eq!(out, "[harn] second");
2347 }
2348
2349 #[test]
2350 fn test_subscript() {
2351 let out = run_output("pipeline t(task) { let arr = [10, 20, 30]\nlog(arr[1]) }");
2352 assert_eq!(out, "[harn] 20");
2353 }
2354
2355 #[test]
2356 fn test_string_methods() {
2357 let out = run_output(
2358 r#"pipeline t(task) { log("hello world".replace("world", "harn"))
2359log("a,b,c".split(","))
2360log(" hello ".trim())
2361log("hello".starts_with("hel"))
2362log("hello".ends_with("lo"))
2363log("hello".substring(1, 3)) }"#,
2364 );
2365 assert_eq!(
2366 out,
2367 "[harn] hello harn\n[harn] [a, b, c]\n[harn] hello\n[harn] true\n[harn] true\n[harn] el"
2368 );
2369 }
2370
2371 #[test]
2372 fn test_list_properties() {
2373 let out = run_output(
2374 "pipeline t(task) { let list = [1, 2, 3]\nlog(list.count)\nlog(list.empty)\nlog(list.first)\nlog(list.last) }",
2375 );
2376 assert_eq!(out, "[harn] 3\n[harn] false\n[harn] 1\n[harn] 3");
2377 }
2378
2379 #[test]
2380 fn test_recursive_function() {
2381 let out = run_output(
2382 "pipeline t(task) { fn fib(n) { if n <= 1 { return n } return fib(n - 1) + fib(n - 2) }\nlog(fib(10)) }",
2383 );
2384 assert_eq!(out, "[harn] 55");
2385 }
2386
2387 #[test]
2388 fn test_ternary() {
2389 let out = run_output(
2390 r#"pipeline t(task) { let x = 5
2391let r = x > 0 ? "positive" : "non-positive"
2392log(r) }"#,
2393 );
2394 assert_eq!(out, "[harn] positive");
2395 }
2396
2397 #[test]
2398 fn test_for_in_dict() {
2399 let out = run_output(
2400 "pipeline t(task) { let d = {a: 1, b: 2}\nfor entry in d { log(entry.key) } }",
2401 );
2402 assert_eq!(out, "[harn] a\n[harn] b");
2403 }
2404
2405 #[test]
2406 fn test_list_any_all() {
2407 let out = run_output(
2408 "pipeline t(task) { let nums = [2, 4, 6]\nlog(nums.any({ x -> x > 5 }))\nlog(nums.all({ x -> x > 0 }))\nlog(nums.all({ x -> x > 3 })) }",
2409 );
2410 assert_eq!(out, "[harn] true\n[harn] true\n[harn] false");
2411 }
2412
2413 #[test]
2414 fn test_disassembly() {
2415 let mut lexer = Lexer::new("pipeline t(task) { log(2 + 3) }");
2416 let tokens = lexer.tokenize().unwrap();
2417 let mut parser = Parser::new(tokens);
2418 let program = parser.parse().unwrap();
2419 let chunk = Compiler::new().compile(&program).unwrap();
2420 let disasm = chunk.disassemble("test");
2421 assert!(disasm.contains("CONSTANT"));
2422 assert!(disasm.contains("ADD"));
2423 assert!(disasm.contains("CALL"));
2424 }
2425
2426 #[test]
2429 fn test_try_catch_basic() {
2430 let out = run_output(
2431 r#"pipeline t(task) { try { throw "oops" } catch(e) { log("caught: " + e) } }"#,
2432 );
2433 assert_eq!(out, "[harn] caught: oops");
2434 }
2435
2436 #[test]
2437 fn test_try_no_error() {
2438 let out = run_output(
2439 r#"pipeline t(task) {
2440var result = 0
2441try { result = 42 } catch(e) { result = 0 }
2442log(result)
2443}"#,
2444 );
2445 assert_eq!(out, "[harn] 42");
2446 }
2447
2448 #[test]
2449 fn test_throw_uncaught() {
2450 let result = run_harn_result(r#"pipeline t(task) { throw "boom" }"#);
2451 assert!(result.is_err());
2452 }
2453
2454 fn run_vm(source: &str) -> String {
2457 let rt = tokio::runtime::Builder::new_current_thread()
2458 .enable_all()
2459 .build()
2460 .unwrap();
2461 rt.block_on(async {
2462 let local = tokio::task::LocalSet::new();
2463 local
2464 .run_until(async {
2465 let mut lexer = Lexer::new(source);
2466 let tokens = lexer.tokenize().unwrap();
2467 let mut parser = Parser::new(tokens);
2468 let program = parser.parse().unwrap();
2469 let chunk = Compiler::new().compile(&program).unwrap();
2470 let mut vm = Vm::new();
2471 register_vm_stdlib(&mut vm);
2472 vm.execute(&chunk).await.unwrap();
2473 vm.output().to_string()
2474 })
2475 .await
2476 })
2477 }
2478
2479 fn run_vm_err(source: &str) -> String {
2480 let rt = tokio::runtime::Builder::new_current_thread()
2481 .enable_all()
2482 .build()
2483 .unwrap();
2484 rt.block_on(async {
2485 let local = tokio::task::LocalSet::new();
2486 local
2487 .run_until(async {
2488 let mut lexer = Lexer::new(source);
2489 let tokens = lexer.tokenize().unwrap();
2490 let mut parser = Parser::new(tokens);
2491 let program = parser.parse().unwrap();
2492 let chunk = Compiler::new().compile(&program).unwrap();
2493 let mut vm = Vm::new();
2494 register_vm_stdlib(&mut vm);
2495 match vm.execute(&chunk).await {
2496 Err(e) => format!("{}", e),
2497 Ok(_) => panic!("Expected error"),
2498 }
2499 })
2500 .await
2501 })
2502 }
2503
2504 #[test]
2505 fn test_hello_world() {
2506 let out = run_vm(r#"pipeline default(task) { log("hello") }"#);
2507 assert_eq!(out, "[harn] hello\n");
2508 }
2509
2510 #[test]
2511 fn test_arithmetic_new() {
2512 let out = run_vm("pipeline default(task) { log(2 + 3) }");
2513 assert_eq!(out, "[harn] 5\n");
2514 }
2515
2516 #[test]
2517 fn test_string_concat_new() {
2518 let out = run_vm(r#"pipeline default(task) { log("a" + "b") }"#);
2519 assert_eq!(out, "[harn] ab\n");
2520 }
2521
2522 #[test]
2523 fn test_if_else_new() {
2524 let out = run_vm("pipeline default(task) { if true { log(1) } else { log(2) } }");
2525 assert_eq!(out, "[harn] 1\n");
2526 }
2527
2528 #[test]
2529 fn test_for_loop_new() {
2530 let out = run_vm("pipeline default(task) { for i in [1, 2, 3] { log(i) } }");
2531 assert_eq!(out, "[harn] 1\n[harn] 2\n[harn] 3\n");
2532 }
2533
2534 #[test]
2535 fn test_while_loop_new() {
2536 let out = run_vm("pipeline default(task) { var i = 0\nwhile i < 3 { log(i)\ni = i + 1 } }");
2537 assert_eq!(out, "[harn] 0\n[harn] 1\n[harn] 2\n");
2538 }
2539
2540 #[test]
2541 fn test_function_call_new() {
2542 let out =
2543 run_vm("pipeline default(task) { fn add(a, b) { return a + b }\nlog(add(2, 3)) }");
2544 assert_eq!(out, "[harn] 5\n");
2545 }
2546
2547 #[test]
2548 fn test_closure_new() {
2549 let out = run_vm("pipeline default(task) { let f = { x -> x * 2 }\nlog(f(5)) }");
2550 assert_eq!(out, "[harn] 10\n");
2551 }
2552
2553 #[test]
2554 fn test_recursion() {
2555 let out = run_vm("pipeline default(task) { fn fact(n) { if n <= 1 { return 1 }\nreturn n * fact(n - 1) }\nlog(fact(5)) }");
2556 assert_eq!(out, "[harn] 120\n");
2557 }
2558
2559 #[test]
2560 fn test_try_catch_new() {
2561 let out = run_vm(r#"pipeline default(task) { try { throw "err" } catch (e) { log(e) } }"#);
2562 assert_eq!(out, "[harn] err\n");
2563 }
2564
2565 #[test]
2566 fn test_try_no_error_new() {
2567 let out = run_vm("pipeline default(task) { try { log(1) } catch (e) { log(2) } }");
2568 assert_eq!(out, "[harn] 1\n");
2569 }
2570
2571 #[test]
2572 fn test_list_map_new() {
2573 let out =
2574 run_vm("pipeline default(task) { let r = [1, 2, 3].map({ x -> x * 2 })\nlog(r) }");
2575 assert_eq!(out, "[harn] [2, 4, 6]\n");
2576 }
2577
2578 #[test]
2579 fn test_list_filter_new() {
2580 let out = run_vm(
2581 "pipeline default(task) { let r = [1, 2, 3, 4].filter({ x -> x > 2 })\nlog(r) }",
2582 );
2583 assert_eq!(out, "[harn] [3, 4]\n");
2584 }
2585
2586 #[test]
2587 fn test_dict_access_new() {
2588 let out = run_vm("pipeline default(task) { let d = {name: \"Alice\"}\nlog(d.name) }");
2589 assert_eq!(out, "[harn] Alice\n");
2590 }
2591
2592 #[test]
2593 fn test_string_interpolation() {
2594 let out = run_vm("pipeline default(task) { let x = 42\nlog(\"val=${x}\") }");
2595 assert_eq!(out, "[harn] val=42\n");
2596 }
2597
2598 #[test]
2599 fn test_match_new() {
2600 let out = run_vm(
2601 "pipeline default(task) { let x = \"b\"\nmatch x { \"a\" -> { log(1) } \"b\" -> { log(2) } } }",
2602 );
2603 assert_eq!(out, "[harn] 2\n");
2604 }
2605
2606 #[test]
2607 fn test_json_roundtrip() {
2608 let out = run_vm("pipeline default(task) { let s = json_stringify({a: 1})\nlog(s) }");
2609 assert!(out.contains("\"a\""));
2610 assert!(out.contains("1"));
2611 }
2612
2613 #[test]
2614 fn test_type_of() {
2615 let out = run_vm("pipeline default(task) { log(type_of(42))\nlog(type_of(\"hi\")) }");
2616 assert_eq!(out, "[harn] int\n[harn] string\n");
2617 }
2618
2619 #[test]
2620 fn test_stack_overflow() {
2621 let err = run_vm_err("pipeline default(task) { fn f() { f() }\nf() }");
2622 assert!(
2623 err.contains("stack") || err.contains("overflow") || err.contains("recursion"),
2624 "Expected stack overflow error, got: {}",
2625 err
2626 );
2627 }
2628
2629 #[test]
2630 fn test_division_by_zero() {
2631 let err = run_vm_err("pipeline default(task) { log(1 / 0) }");
2632 assert!(
2633 err.contains("Division by zero") || err.contains("division"),
2634 "Expected division by zero error, got: {}",
2635 err
2636 );
2637 }
2638
2639 #[test]
2640 fn test_try_catch_nested() {
2641 let out = run_output(
2642 r#"pipeline t(task) {
2643try {
2644 try {
2645 throw "inner"
2646 } catch(e) {
2647 log("inner caught: " + e)
2648 throw "outer"
2649 }
2650} catch(e) {
2651 log("outer caught: " + e)
2652}
2653}"#,
2654 );
2655 assert_eq!(
2656 out,
2657 "[harn] inner caught: inner\n[harn] outer caught: outer"
2658 );
2659 }
2660
2661 #[test]
2664 fn test_parallel_basic() {
2665 let out = run_output(
2666 "pipeline t(task) { let results = parallel(3) { i -> i * 10 }\nlog(results) }",
2667 );
2668 assert_eq!(out, "[harn] [0, 10, 20]");
2669 }
2670
2671 #[test]
2672 fn test_parallel_no_variable() {
2673 let out = run_output("pipeline t(task) { let results = parallel(3) { 42 }\nlog(results) }");
2674 assert_eq!(out, "[harn] [42, 42, 42]");
2675 }
2676
2677 #[test]
2678 fn test_parallel_map_basic() {
2679 let out = run_output(
2680 "pipeline t(task) { let results = parallel_map([1, 2, 3]) { x -> x * x }\nlog(results) }",
2681 );
2682 assert_eq!(out, "[harn] [1, 4, 9]");
2683 }
2684
2685 #[test]
2686 fn test_spawn_await() {
2687 let out = run_output(
2688 r#"pipeline t(task) {
2689let handle = spawn { log("spawned") }
2690let result = await(handle)
2691log("done")
2692}"#,
2693 );
2694 assert_eq!(out, "[harn] spawned\n[harn] done");
2695 }
2696
2697 #[test]
2698 fn test_spawn_cancel() {
2699 let out = run_output(
2700 r#"pipeline t(task) {
2701let handle = spawn { log("should be cancelled") }
2702cancel(handle)
2703log("cancelled")
2704}"#,
2705 );
2706 assert_eq!(out, "[harn] cancelled");
2707 }
2708
2709 #[test]
2710 fn test_spawn_returns_value() {
2711 let out = run_output("pipeline t(task) { let h = spawn { 42 }\nlet r = await(h)\nlog(r) }");
2712 assert_eq!(out, "[harn] 42");
2713 }
2714
2715 #[test]
2718 fn test_deadline_success() {
2719 let out = run_output(
2720 r#"pipeline t(task) {
2721let result = deadline 5s { log("within deadline")
272242 }
2723log(result)
2724}"#,
2725 );
2726 assert_eq!(out, "[harn] within deadline\n[harn] 42");
2727 }
2728
2729 #[test]
2730 fn test_deadline_exceeded() {
2731 let result = run_harn_result(
2732 r#"pipeline t(task) {
2733deadline 1ms {
2734 var i = 0
2735 while i < 1000000 { i = i + 1 }
2736}
2737}"#,
2738 );
2739 assert!(result.is_err());
2740 }
2741
2742 #[test]
2743 fn test_deadline_caught_by_try() {
2744 let out = run_output(
2745 r#"pipeline t(task) {
2746try {
2747 deadline 1ms {
2748 var i = 0
2749 while i < 1000000 { i = i + 1 }
2750 }
2751} catch(e) {
2752 log("caught")
2753}
2754}"#,
2755 );
2756 assert_eq!(out, "[harn] caught");
2757 }
2758}