Skip to main content

gc/
lib.rs

1#[cfg(test)]
2mod gc_test;
3#[cfg(test)]
4mod report_test;
5#[cfg(test)]
6mod runner_test;
7#[cfg(test)]
8mod value_test;
9#[cfg(test)]
10mod vm_test;
11
12pub mod frame;
13pub mod header;
14pub mod heap;
15pub mod list;
16pub mod malloc;
17pub mod report;
18pub mod runner;
19// The file is named gc_runtime.rs for editor clarity, but the module keeps
20// its historical public path `gc::runtime`.
21#[path = "gc_runtime.rs"]
22pub mod runtime;
23pub mod value;
24pub mod vm;
25
26pub use frame::Frame;
27pub use header::{GcId, GcObjectHeader, GcObjectType, GcPhase, RefCountHeader, RefCountId};
28pub use heap::{GcHeap, GcRef};
29pub use malloc::{MallocState, DEFAULT_GC_THRESHOLD, MALLOC_OVERHEAD};
30pub use report::{
31    EdgeRelation, FinalFate, FreeCycleStats, GcCollectionReport, GcObjectSummary, GcPhaseStats,
32    GcStatsBundle, GlobalRoot, HashKeyKind, HeapSnapshot, ObjectDecision, RestorationWitness,
33    ScanStats, TrialDecision, TrialDeletionStats, ValueKindCounts, VisitedEdge,
34};
35pub use runner::{compile_source, run_bytecode, run_bytecode_with_output};
36pub use runtime::{GcObject, GcRuntime, MarkFunc};
37pub use value::{
38    export_object, import_object, try_export_object, value_to_string, GcClosure, Value, ValueKind,
39};
40pub use vm::{
41    GcClassifiedRuntimeError, GcRuntimeError, GcRuntimeErrorKind, GcVM, DEFAULT_INSTRUCTION_BUDGET,
42};
43
44use compiler::compiler::{Bytecode, Compiler};
45use object::Object;
46use parser::ast::Node;
47use parser::lexer::token::Span;
48use serde::Serialize;
49
50#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
51#[serde(rename_all = "lowercase")]
52pub enum GcRunStage {
53    Parse,
54    Compile,
55    Runtime,
56}
57
58#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
59#[serde(rename_all = "camelCase")]
60pub struct GcRunSuccess {
61    pub result: String,
62    pub report: GcCollectionReport,
63}
64
65/// Parse, compile, or runtime failure returned by the established report API.
66#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
67#[serde(rename_all = "camelCase")]
68pub struct GcRunError {
69    pub stage: GcRunStage,
70    pub message: String,
71    pub span: Option<Span>,
72}
73
74/// Report failure with a stable, machine-readable error category.
75#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
76#[serde(rename_all = "camelCase")]
77pub struct GcClassifiedRunError {
78    pub stage: GcRunStage,
79    pub kind: String,
80    pub message: String,
81    pub span: Option<Span>,
82}
83
84impl From<GcClassifiedRunError> for GcRunError {
85    fn from(error: GcClassifiedRunError) -> Self {
86        Self {
87            stage: error.stage,
88            message: error.message,
89            span: error.span,
90        }
91    }
92}
93
94/// Compile Monkey source using the existing bytecode compiler.
95pub fn compile(program: &Node) -> Result<Bytecode, String> {
96    let mut compiler = Compiler::new();
97    compiler.compile(program)
98}
99
100/// Compile and execute on the GC-backed VM.
101pub fn eval(program: &Node) -> Result<Object, String> {
102    let bytecode = compile(program)?;
103    let mut vm = GcVM::new(bytecode);
104    vm.run_with_budget(usize::MAX)
105        .map_err(|error| error.message)?;
106    vm.try_export_last_result()
107}
108
109/// Parse, compile, and execute Monkey source.
110pub fn eval_source(source: &str) -> Result<Object, String> {
111    let program = parser::parse(source).map_err(|errors| errors[0].clone())?;
112    eval(&program)
113}
114
115/// Parse, compile, execute with deterministic GC settings, then collect cycles.
116pub fn run_source_with_report(
117    source: &str,
118    instruction_budget: usize,
119) -> Result<GcRunSuccess, GcRunError> {
120    run_source_with_report_classified(source, instruction_budget).map_err(Into::into)
121}
122
123/// Parse, compile, and execute Monkey source while classifying failures at
124/// their raise sites.
125pub fn run_source_with_report_classified(
126    source: &str,
127    instruction_budget: usize,
128) -> Result<GcRunSuccess, GcClassifiedRunError> {
129    let program = parser::parse(source).map_err(|errors| GcClassifiedRunError {
130        stage: GcRunStage::Parse,
131        kind: "syntax".to_string(),
132        message: errors
133            .first()
134            .cloned()
135            .unwrap_or_else(|| "unknown parse error".to_string()),
136        span: None,
137    })?;
138    let mut compiler = Compiler::new();
139    let bytecode = compiler
140        .compile(&program)
141        .map_err(|message| GcClassifiedRunError {
142            stage: GcRunStage::Compile,
143            kind: "compile".to_string(),
144            message,
145            span: None,
146        })?;
147    let global_names = compiler.symbol_table.global_symbols();
148    let mut vm = GcVM::new(bytecode);
149    vm.set_global_names(global_names);
150    vm.heap_mut().set_gc_threshold(usize::MAX);
151    vm.run_with_budget_classified(instruction_budget)
152        .map_err(|error| GcClassifiedRunError {
153            stage: GcRunStage::Runtime,
154            kind: error.kind.as_str().to_string(),
155            message: error.message,
156            span: error.span,
157        })?;
158    let result = vm.last_result_string();
159    let report = vm.collect_garbage();
160    Ok(GcRunSuccess {
161        result,
162        report,
163    })
164}