use std::any::Any;
use std::collections::HashMap;
use std::fmt;
use crate::script_engine::VMContext;
#[derive(Debug)]
pub struct InstructionMetadata {
inner: Box<dyn Any + Send + Sync>,
}
impl InstructionMetadata {
pub fn new<T: Any + Send + Sync>(value: T) -> Self {
Self {
inner: Box::new(value),
}
}
pub fn get<T: Any>(&self) -> Option<&T> {
self.inner.downcast_ref::<T>()
}
pub fn get_mut<T: Any>(&mut self) -> Option<&mut T> {
self.inner.downcast_mut::<T>()
}
pub fn is<T: Any>(&self) -> bool {
self.inner.is::<T>()
}
}
#[derive(Debug)]
pub enum InstructionData {
None,
U32(u32),
U64(u64),
I32(i32),
String(String),
Custom(Box<dyn Any + Send + Sync>),
}
impl InstructionData {
#[inline]
pub fn none() -> Self {
InstructionData::None
}
#[inline]
pub fn u32(value: u32) -> Self {
InstructionData::U32(value)
}
#[inline]
pub fn u64(value: u64) -> Self {
InstructionData::U64(value)
}
#[inline]
pub fn i32(value: i32) -> Self {
InstructionData::I32(value)
}
#[inline]
pub fn string<S: Into<String>>(value: S) -> Self {
InstructionData::String(value.into())
}
#[inline]
pub fn custom<T: Any + Send + Sync>(value: T) -> Self {
InstructionData::Custom(Box::new(value))
}
pub fn get_custom_ref<T: Any>(&self) -> Option<&T> {
if let InstructionData::Custom(boxed) = self {
boxed.downcast_ref::<T>()
} else {
None
}
}
pub fn get_custom_mut<T: Any>(&mut self) -> Option<&mut T> {
if let InstructionData::Custom(boxed) = self {
boxed.downcast_mut::<T>()
} else {
None
}
}
pub fn is_custom_type<T: Any>(&self) -> bool {
if let InstructionData::Custom(boxed) = self {
boxed.is::<T>()
} else {
false
}
}
pub fn extract_custom<T: Any>(&self, error_msg: &str) -> Result<&T, ScriptError> {
self.get_custom_ref::<T>()
.ok_or_else(|| ScriptError::ExecutionError(error_msg.into()))
}
}
#[derive(Debug)]
pub struct CompiledInstruction {
pub name: String,
pub data: InstructionData,
pub metadata: Option<InstructionMetadata>,
}
impl fmt::Display for CompiledInstruction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)
}
}
#[derive(Debug)]
pub enum ScriptError {
ParseError(String),
CompilationError(String),
ExecutionError(String),
Interrupted(String),
InvalidLoopStructure,
NotImplemented,
NoWindowHandle,
}
impl fmt::Display for ScriptError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ParseError(msg) => write!(f, "Parse error: {}", msg),
Self::CompilationError(msg) => write!(f, "Compilation error: {}", msg),
Self::ExecutionError(msg) => write!(f, "Execution error: {}", msg),
Self::Interrupted(msg) => write!(f, "Interrupted: {}", msg),
Self::InvalidLoopStructure => write!(f, "Invalid loop structure"),
Self::NotImplemented => write!(f, "Not implemented"),
Self::NoWindowHandle => write!(f, "No window handle available"),
}
}
}
impl std::error::Error for ScriptError {}
pub trait InstructionHandler: Send + Sync {
fn name(&self) -> &str;
fn parse(&self, args: &[&str]) -> Result<InstructionData, ScriptError>;
fn execute(
&self,
vm: &mut VMContext,
data: &InstructionData,
metadata: Option<&InstructionMetadata>,
) -> Result<(), ScriptError>;
fn declare_block_structure(&self) -> Option<BlockStructure> {
None }
}
#[derive(Debug, Clone)]
pub enum BlockStructure {
SimplePair {
start_name: &'static str,
end_name: &'static str,
},
ConditionalTriple {
if_name: &'static str,
else_name: &'static str,
end_name: &'static str,
},
Custom {
start_name: &'static str,
middle_names: Vec<&'static str>,
end_name: &'static str,
},
}
#[derive(Default)]
pub struct InstructionRegistry {
handlers: HashMap<String, Box<dyn InstructionHandler>>,
}
impl InstructionRegistry {
pub fn new() -> Self {
Self {
handlers: HashMap::new(),
}
}
pub fn register<H: InstructionHandler + 'static>(
&mut self,
handler: H,
) -> Result<(), ScriptError> {
let boxed = Box::new(handler);
let name = boxed.name().to_string();
if self.handlers.insert(name.clone(), boxed).is_some() {
Err(ScriptError::CompilationError(format!(
"Instruction '{}' already registered",
name
)))
} else {
Ok(())
}
}
pub fn get_handler(&self, name: &str) -> Option<&dyn InstructionHandler> {
self.handlers.get(name).map(|h| h.as_ref())
}
pub fn has_instruction(&self, name: &str) -> bool {
self.handlers.contains_key(name)
}
pub fn list_instructions(&self) -> Vec<String> {
let mut names: Vec<String> = self.handlers.keys().cloned().collect();
names.sort();
names
}
}
impl std::fmt::Debug for InstructionRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut all_names: Vec<&str> = self.handlers.keys().map(|s| s.as_str()).collect();
all_names.sort();
f.debug_struct("InstructionRegistry")
.field("handlers", &self.handlers.len())
.field("instructions", &all_names)
.finish()
}
}