mod run_bridge;
mod state_bridge;
use run_bridge::{build_debug_state, build_debug_state_from_snapshot, byte_offset_to_line_column};
use serde::{Deserialize, Serialize};
use crate::error::{render_error_report, render_report_at_line, RunError};
use crate::interpreter::DebugSnapshot;
use crate::Interpreter;
use vb6parse::files::ModuleFile;
use vb6parse::io::SourceFile;
#[derive(Clone, Serialize, Deserialize)]
pub struct WasmRunError {
pub message: String,
pub pretty_report: Option<String>,
pub error_number: Option<i32>,
pub is_debug_pause: bool,
pub line: Option<usize>,
pub procedure: Option<String>,
pub param_index: Option<usize>,
pub param_name: Option<String>,
}
#[derive(Serialize, Deserialize)]
pub struct WasmVariableInfo {
pub name: String,
pub type_name: String,
pub value: String,
}
#[derive(Serialize, Deserialize)]
pub struct WasmDebugState {
pub current_steps: u64,
pub current_line: usize,
pub current_procedure: Option<String>,
pub stack_depth: usize,
pub globals: Vec<WasmVariableInfo>,
pub locals: Vec<WasmVariableInfo>,
pub cursor: Option<[u32; 4]>,
}
#[derive(Serialize, Deserialize)]
pub struct WasmDebugTrace {
pub successful: bool,
pub error: Option<WasmRunError>,
pub snapshots: Vec<WasmRunOutput>,
}
#[derive(Serialize, Deserialize)]
pub struct WasmRunOutput {
pub successful: bool,
pub output_lines: Vec<String>,
pub output_text: String,
pub steps: u64,
pub terminated: bool,
pub paused: bool,
pub error: Option<WasmRunError>,
pub debug: WasmDebugState,
}
fn parse_module(code: &str) -> Result<ModuleFile, WasmRunError> {
let source_file = SourceFile::from_string("playground.bas", code);
match ModuleFile::parse(&source_file).ok_or_errors() {
Ok(module) => Ok(module),
Err(errors) => {
let first = errors.first();
let message = first
.map(|error| error.kind.to_string())
.unwrap_or_else(|| "Failed to parse the input code as a VB6 module.".to_string());
let line = first.map(|error| {
byte_offset_to_line_column(error.source_content, error.error_offset as usize).0
});
let pretty_report = first.zip(line).and_then(|(error, line)| {
render_report_at_line("playground.bas", code, line, &error.kind.to_string())
});
Err(WasmRunError {
message,
pretty_report,
error_number: None,
is_debug_pause: false,
line,
procedure: None,
param_index: None,
param_name: None,
})
}
}
}
fn convert_run_error(error: RunError, code: &str, line_offset: usize) -> WasmRunError {
let pretty_report = render_error_report("playground.bas", code, &error, line_offset);
let param_index = error.builtin_call.as_ref().map(|c| c.param_index);
let param_name = error.builtin_call.as_ref().map(|c| c.param_name.clone());
WasmRunError {
message: error.to_string(),
pretty_report,
error_number: (!error.is_debug_pause()).then_some(error.error.number),
is_debug_pause: error.is_debug_pause(),
line: error.line,
procedure: error.procedure,
param_index,
param_name,
}
}
fn build_output(interpreter: &Interpreter, error: Option<WasmRunError>) -> WasmRunOutput {
let paused = error.as_ref().is_some_and(|error| error.is_debug_pause);
WasmRunOutput {
successful: error.is_none() || paused,
output_lines: interpreter.output().to_vec(),
output_text: interpreter.output_text(),
steps: interpreter.steps(),
terminated: interpreter.is_terminated(),
paused,
error,
debug: build_debug_state(interpreter),
}
}
fn build_output_from_snapshot(
snapshot: &DebugSnapshot,
paused: bool,
successful: bool,
error: Option<WasmRunError>,
code: &str,
delta: u32,
) -> WasmRunOutput {
WasmRunOutput {
successful,
output_lines: snapshot.output_lines.clone(),
output_text: snapshot.output_text.clone(),
steps: snapshot.steps,
terminated: snapshot.terminated,
paused,
error,
debug: build_debug_state_from_snapshot(snapshot, code, delta),
}
}