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