Skip to main content

gc/
runner.rs

1//! Shared execution path for the `monkey-gc` CLI (design doc §7).
2//!
3//! Both CLI inputs — `.monkey` source and `.mbc` snapshots — funnel into
4//! [`run_bytecode`] once a [`Bytecode`] is in hand, so results and runtime
5//! errors render identically on both paths. This deliberately bypasses
6//! `gc::eval_source`: exporting the final value back to an [`object::Object`]
7//! fails for class instances and drops the runtime-error `Span`.
8
9use compiler::compiler::{Bytecode, Compiler};
10
11use crate::vm::{GcClassifiedRuntimeError, GcRuntimeError, GcVM};
12
13/// Parse and compile Monkey source, reporting the first error as a string.
14pub fn compile_source(source: &str) -> Result<Bytecode, String> {
15    let program = parser::parse(source).map_err(|errors| {
16        errors
17            .first()
18            .cloned()
19            .unwrap_or_else(|| "unknown parse error".to_string())
20    })?;
21    let mut compiler = Compiler::new();
22    compiler.compile(&program)
23}
24
25/// Execute bytecode on a fresh VM and render the final popped value the way
26/// the REPL does. `instruction_budget` is `usize::MAX` for normal runs; the
27/// CLI's `--max-instructions` threads a finite budget through here.
28pub fn run_bytecode(
29    bytecode: Bytecode,
30    instruction_budget: usize,
31) -> Result<String, GcRuntimeError> {
32    let mut vm = GcVM::new(bytecode);
33    vm.run_with_budget(instruction_budget)?;
34    Ok(vm.last_result_string())
35}
36
37/// Execute bytecode on a fresh VM while capturing all `puts`/`print` output.
38/// The output is returned even when execution fails after producing it.
39pub fn run_bytecode_with_output(
40    bytecode: Bytecode,
41    instruction_budget: usize,
42) -> (Result<String, GcClassifiedRuntimeError>, String) {
43    let mut vm = GcVM::new(bytecode);
44    vm.set_capture_output(true);
45    let result = vm
46        .run_with_budget_classified(instruction_budget)
47        .map(|()| vm.last_result_string());
48    let output = vm.take_output();
49    (result, output)
50}