pub use crate::vm::{SlotType, UserData, VM};
pub static VERSION_STRING: &str = "safe_wren-0.1";
pub type ForeignMethodFn = fn(vm: &mut VM);
pub type FinalizerFn = fn(data: *mut std::ffi::c_void);
pub type ResolveModuleFn = fn(vm: &VM, importer: &str, name: &str) -> String;
pub struct LoadModuleResult {
pub source: String,
}
pub type LoadModuleFn = fn(vm: &VM, name: &str) -> Option<LoadModuleResult>;
pub type BindForeignMethodFn = fn(
vm: &VM,
module: &str,
class_name: &str,
is_static: bool,
signature: &str,
) -> Option<ForeignMethodFn>;
pub type WriteFn = fn(vm: &VM, text: &str);
pub enum ErrorType {
Compile,
Runtime,
StackTrace,
}
pub type ErrorFn = fn(vm: &VM, error_type: ErrorType, module: &str, line: usize, message: &str);
pub struct ForeignClassMethods {
pub allocate: ForeignMethodFn,
pub finalize: Option<FinalizerFn>,
}
pub type BindForeignClassFn =
fn(vm: &VM, module_name: &str, class_name: &str) -> ForeignClassMethods;
#[derive(Default)]
pub struct Configuration {
pub resolve_module_fn: Option<ResolveModuleFn>,
pub load_module_fn: Option<LoadModuleFn>,
pub bind_foreign_method_fn: Option<BindForeignMethodFn>,
pub bind_foreign_class_fn: Option<BindForeignClassFn>,
pub write_fn: Option<WriteFn>,
pub error_fn: Option<ErrorFn>,
pub debug_level: Option<DebugLevel>,
}
#[allow(dead_code)]
pub enum DebugLevel {
NonCore,
All,
}
pub enum InterpretResult {
Success,
CompileError,
RuntimeError,
}
impl VM {
pub fn interpret_bytes(&mut self, module: &str, source_bytes: Vec<u8>) -> InterpretResult {
match String::from_utf8(source_bytes) {
Ok(source) => self.interpret(module, source),
Err(error) => {
let utf8_error = error.utf8_error();
let source_bytes = error.as_bytes();
let first_bad_byte = utf8_error.valid_up_to() + 1;
let valid_bytes = &source_bytes[..first_bad_byte];
let newline_count = valid_bytes.iter().filter(|&n| *n == b'\n').count();
let line = newline_count + 1;
let bad_byte = source_bytes[first_bad_byte];
self.report_compile_error(
module,
line,
&format!("Error: Invalid byte 0x{:02x}.", bad_byte),
);
InterpretResult::CompileError
}
}
}
fn report_compile_error(&self, module: &str, line: usize, message: &str) {
if let Some(error_fn) = self.config.error_fn {
error_fn(self, ErrorType::Compile, module, line, message);
}
}
pub fn interpret(&mut self, module: &str, source: String) -> InterpretResult {
match self.compile_source(module, source) {
Err(error) => {
self.report_compile_error(module, error.line, &error.error.to_string());
InterpretResult::CompileError
}
Ok(closure) => {
match self.run(closure) {
Err(e) => {
if let Some(error_fn) = self.config.error_fn {
error_fn(
self,
ErrorType::Runtime,
module,
e.stack_trace.frames[0].line,
&e.msg,
);
for frame in &e.stack_trace.frames {
error_fn(
self,
ErrorType::StackTrace,
&frame.module,
frame.line,
&frame.fn_name,
);
}
}
InterpretResult::RuntimeError
}
Ok(_) => InterpretResult::Success,
}
}
}
}
}
pub type Slot = usize;