runmat_vm/interpreter/dispatch/
exceptions.rs1use 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}