use petr_core::SelectorWord;
use crate::{DispatchError, DispatchResult};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OpFrame {
pub text: String,
pub acc: i64,
}
impl OpFrame {
pub fn text(text: impl Into<String>) -> Self { Self { text: text.into(), acc: 0 } }
pub fn acc(acc: i64) -> Self { Self { text: String::new(), acc } }
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct OpArg {
pub n: i64,
pub text: String,
}
impl OpArg {
pub fn n(n: i64) -> Self { Self { n, text: String::new() } }
pub fn text(text: impl Into<String>) -> Self { Self { n: 0, text: text.into() } }
}
pub type OpFn = fn(OpFrame, &OpArg) -> DispatchResult<OpFrame>;
#[derive(Clone)]
pub struct OpEntry {
pub func: OpFn,
pub arg: OpArg,
pub name: String,
}
impl core::fmt::Debug for OpEntry {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("OpEntry").field("name", &self.name).field("arg", &self.arg).finish()
}
}
#[derive(Debug, Clone)]
pub struct OperationTable {
entries: Vec<Option<OpEntry>>,
}
impl Default for OperationTable {
fn default() -> Self { Self::new() }
}
impl OperationTable {
pub fn new() -> Self { Self { entries: vec![None; 256] } }
pub fn register(&mut self, selector: u8, name: impl Into<String>, func: OpFn, arg: OpArg) {
self.entries[selector as usize] = Some(OpEntry { func, arg, name: name.into() });
}
pub fn register_word(&mut self, selector: SelectorWord, name: impl Into<String>, func: OpFn, arg: OpArg) {
self.register(selector.function_id(), name, func, arg);
}
pub fn execute(&self, selector: u8, frame: OpFrame) -> DispatchResult<OpFrame> {
match &self.entries[selector as usize] {
Some(entry) => (entry.func)(frame, &entry.arg),
None => Err(DispatchError::OperationNotFound(selector)),
}
}
pub fn execute_word(&self, selector: SelectorWord, frame: OpFrame) -> DispatchResult<OpFrame> {
self.execute(selector.function_id(), frame)
}
pub fn execute_or_nop(&self, selector: u8, frame: OpFrame) -> OpFrame {
if let Some(entry) = &self.entries[selector as usize] {
(entry.func)(frame, &entry.arg).unwrap_or_else(|_| OpFrame::text("<op-error>"))
} else {
frame
}
}
pub fn contains(&self, selector: u8) -> bool { self.entries[selector as usize].is_some() }
}