Skip to main content

runmat_vm/interpreter/dispatch/
exceptions.rs

1use crate::bytecode::program::Bytecode;
2use crate::interpreter::errors::{attach_span_at, ensure_runtime_error_identifier};
3use crate::runtime::call_stack::error_namespace;
4use runmat_builtins::{MException, Value};
5use runmat_runtime::RuntimeError;
6
7pub enum ExceptionHandling {
8    Caught,
9    Uncaught(Box<RuntimeError>),
10}
11
12pub fn parse_exception(err: &RuntimeError) -> MException {
13    if let Some(identifier) = err.identifier() {
14        return MException::new(identifier.to_string(), err.message().to_string());
15    }
16    let message = err.message();
17    if let Some(idx) = message.rfind(": ") {
18        let (id, msg) = message.split_at(idx);
19        let message = msg.trim_start_matches(':').trim().to_string();
20        let ident = if id.trim().is_empty() {
21            format!("{}:error", error_namespace())
22        } else {
23            id.trim().to_string()
24        };
25        return MException::new(ident, message);
26    }
27    if let Some(idx) = message.rfind(':') {
28        let (id, msg) = message.split_at(idx);
29        let message = msg.trim_start_matches(':').trim().to_string();
30        let ident = if id.trim().is_empty() {
31            format!("{}:error", error_namespace())
32        } else {
33            id.trim().to_string()
34        };
35        return MException::new(ident, message);
36    }
37    MException::new(format!("{}:error", error_namespace()), message.to_string())
38}
39
40pub fn redirect_exception_to_catch(
41    err: RuntimeError,
42    try_stack: &mut Vec<(usize, Option<usize>)>,
43    vars: &mut Vec<Value>,
44    last_exception: &mut Option<MException>,
45    pc: &mut usize,
46    refresh_vars: impl Fn(&[Value]),
47) -> ExceptionHandling {
48    if let Some((catch_pc, catch_var)) = try_stack.pop() {
49        if let Some(var_idx) = catch_var {
50            if var_idx >= vars.len() {
51                vars.resize(var_idx + 1, Value::Num(0.0));
52                refresh_vars(vars);
53            }
54            let mex = parse_exception(&err);
55            *last_exception = Some(mex.clone());
56            vars[var_idx] = Value::MException(mex);
57        }
58        *pc = catch_pc;
59        ExceptionHandling::Caught
60    } else {
61        ExceptionHandling::Uncaught(Box::new(err))
62    }
63}
64
65pub fn prepare_vm_error(
66    bytecode: &Bytecode,
67    pc: usize,
68    err: impl Into<RuntimeError>,
69) -> RuntimeError {
70    let err: RuntimeError = ensure_runtime_error_identifier(err.into());
71    attach_span_at(bytecode, pc, err)
72}