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::{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}