use std::convert::TryInto;
pub type Word = u64;
pub type mmmfloat = f64;
trait MmmFloatWord: Sized {
fn to_word(self) -> Word;
fn from_word(word: Word) -> Self;
}
impl MmmFloatWord for f64 {
#[inline(always)] fn to_word(self) -> Word { self.to_bits() }
#[inline(always)] fn from_word(word: Word) -> Self { f64::from_bits(word) }
}
impl MmmFloatWord for f32 {
#[inline(always)] fn to_word(self) -> Word { self.to_bits() as Word }
#[inline(always)] fn from_word(word: Word) -> Self { f32::from_bits(word as u32) }
}
#[inline(always)]
fn f64_to_word(value: mmmfloat) -> Word { value.to_word() }
#[inline(always)]
fn word_to_f64(value: Word) -> mmmfloat { mmmfloat::from_word(value) }
#[inline(always)]
fn i64_to_word(value: i64) -> Word { u64::from_ne_bytes(value.to_ne_bytes()) }
#[inline(always)]
fn word_to_i64(value: Word) -> i64 { i64::from_ne_bytes(value.to_ne_bytes()) }
#[inline(always)]
fn copy_words<const N: usize>(slice: &[Word]) -> Result<[Word; N], String> {
slice
.try_into()
.map_err(|_| format!("expected {} words, got {}", N, slice.len()))
}
#[inline(always)]
fn vec_to_words<const N: usize>(words: Vec<Word>) -> Result<[Word; N], String> {
copy_words::<N>(&words)
}
#[inline(always)]
fn truthy(value: Word) -> bool { word_to_f64(value) > 0.0 }
const FUNCTION_HANDLE_TAG: Word = 1 << 63;
const CLOSURE_HANDLE_TAG: Word = 1 << 62;
const MEMORY_HANDLE_TAG: Word = 1 << 61;
fn encode_function(index: usize) -> Word { FUNCTION_HANDLE_TAG | index as Word }
fn encode_closure(index: usize) -> Word { CLOSURE_HANDLE_TAG | index as Word }
fn encode_memory(index: usize) -> Word { MEMORY_HANDLE_TAG | index as Word }
fn decode_memory(handle: Word) -> Option<usize> {
if handle & MEMORY_HANDLE_TAG != 0 {
Some((handle & !MEMORY_HANDLE_TAG) as usize)
} else {
None
}
}
fn decode_function(handle: Word) -> Option<usize> {
if handle & FUNCTION_HANDLE_TAG != 0 {
Some((handle & !FUNCTION_HANDLE_TAG) as usize)
} else {
Some(handle as usize)
}
}
fn decode_closure(handle: Word) -> Option<usize> {
if handle & FUNCTION_HANDLE_TAG != 0 {
None
} else if handle & CLOSURE_HANDLE_TAG != 0 {
Some((handle & !CLOSURE_HANDLE_TAG) as usize)
} else {
None
}
}
fn parse_specialized_arity(name: &str, prefix: &str, default: usize) -> Option<usize> {
name.strip_prefix(prefix)
.map(|suffix| suffix.parse::<usize>().ok())
.unwrap_or(Some(default))
}
pub trait MimiumHost {
fn call_ext(&mut self, name: &str, args: &[Word], ret_words: usize) -> Result<Vec<Word>, String>;
fn current_time(&mut self) -> mmmfloat {
0.0
}
fn sample_rate(&mut self) -> mmmfloat {
48_000.0
}
}
#[derive(Default)]
pub struct PanicHost;
impl MimiumHost for PanicHost {
fn call_ext(&mut self, name: &str, _args: &[Word], _ret_words: usize) -> Result<Vec<Word>, String> {
Err(format!("external function '{}' is not available in the generated Rust host", name))
}
}
#[derive(Clone, Default)]
struct StateStorage {
pos: usize,
rawdata: Vec<Word>,
}
impl StateStorage {
fn new(size: usize) -> Self {
Self {
pos: 0,
rawdata: vec![0; size],
}
}
fn ensure(&mut self, size: usize) {
let needed = self.pos.saturating_add(size);
if self.rawdata.len() < needed {
self.rawdata.resize(needed, 0);
}
}
#[inline(always)]
fn push_pos(&mut self, offset: usize) {
self.pos += offset;
}
#[inline(always)]
fn pop_pos(&mut self, offset: usize) {
self.pos -= offset;
}
fn get_state(&mut self, size: usize) -> Vec<Word> {
self.ensure(size);
self.rawdata[self.pos..self.pos + size].to_vec()
}
#[inline(always)]
fn get_state_slice(&self, size: usize) -> &[Word] {
debug_assert!(self.pos + size <= self.rawdata.len(), "state read out of bounds: pos={} size={} len={}", self.pos, size, self.rawdata.len());
unsafe { self.rawdata.get_unchecked(self.pos..self.pos + size) }
}
#[inline(always)]
fn get_state_word(&self) -> Word {
debug_assert!(self.pos < self.rawdata.len(), "state read out of bounds: pos={} len={}", self.pos, self.rawdata.len());
unsafe { *self.rawdata.get_unchecked(self.pos) }
}
#[inline(always)]
fn set_state(&mut self, src: &[Word], size: usize) {
debug_assert!(self.pos + size <= self.rawdata.len(), "state write out of bounds: pos={} size={} len={}", self.pos, size, self.rawdata.len());
unsafe { self.rawdata.get_unchecked_mut(self.pos..self.pos + size) }.copy_from_slice(&src[..size]);
}
#[inline(always)]
fn set_state_word(&mut self, src: Word) {
debug_assert!(self.pos < self.rawdata.len(), "state write out of bounds: pos={} len={}", self.pos, self.rawdata.len());
unsafe { *self.rawdata.get_unchecked_mut(self.pos) = src; }
}
#[inline(always)]
fn mem(&mut self, src: Word) -> Word {
debug_assert!(self.pos < self.rawdata.len(), "mem out of bounds: pos={} len={}", self.pos, self.rawdata.len());
let prev = unsafe { *self.rawdata.get_unchecked(self.pos) };
unsafe { *self.rawdata.get_unchecked_mut(self.pos) = src; }
prev
}
fn delay(&mut self, input: Word, time_raw: Word, max_len: usize) -> Word {
let total_words = max_len.saturating_add(2);
self.ensure(total_words);
if max_len == 0 {
return 0;
}
let delay_samples = word_to_f64(time_raw)
.clamp(0.0, max_len.saturating_sub(1) as mmmfloat) as usize;
let read_slot = self.pos;
let write_slot = self.pos + 1;
let data_start = self.pos + 2;
let write_idx = (self.rawdata[write_slot] as usize) % max_len;
let read_idx = (write_idx + max_len - delay_samples) % max_len;
let result = self.rawdata[data_start + read_idx];
self.rawdata[data_start + write_idx] = input;
self.rawdata[read_slot] = read_idx as u64;
self.rawdata[write_slot] = ((write_idx + 1) % max_len) as u64;
result
}
}
#[derive(Clone, Default)]
struct Pointer {
slot: usize,
offset: usize,
}
#[derive(Default)]
struct MemoryStore {
slots: Vec<Vec<Word>>,
ptrs: Vec<Pointer>,
}
impl MemoryStore {
fn alloc(&mut self, size: usize) -> Word {
let slot = self.slots.len();
self.slots.push(vec![0; size]);
self.ptrs.push(Pointer { slot, offset: 0 });
encode_memory(self.ptrs.len())
}
fn ptr(&self, handle: Word) -> Result<&Pointer, String> {
let index = decode_memory(handle)
.and_then(|value| value.checked_sub(1))
.ok_or_else(|| format!("invalid memory handle {}", handle))?;
self.ptrs
.get(index)
.ok_or_else(|| format!("invalid memory handle {}", handle))
}
fn get_element(&mut self, base: Word, tuple_offset: usize) -> Result<Word, String> {
let pointer = self.ptr(base)?.clone();
self.ptrs.push(Pointer {
slot: pointer.slot,
offset: pointer.offset + tuple_offset,
});
Ok(encode_memory(self.ptrs.len()))
}
#[inline(always)]
fn load_word(&self, ptr: Word) -> Result<Word, String> {
let Some(index) = decode_memory(ptr).and_then(|value| value.checked_sub(1)) else {
return Ok(ptr);
};
let pointer = self
.ptrs
.get(index)
.ok_or_else(|| format!("invalid memory handle {}", ptr))?;
let slot = self
.slots
.get(pointer.slot)
.ok_or_else(|| format!("invalid memory slot {}", pointer.slot))?;
slot.get(pointer.offset)
.copied()
.ok_or_else(|| format!("load out of bounds: offset={} len={}", pointer.offset, slot.len()))
}
#[inline(always)]
fn load(&self, ptr: Word, size: usize) -> Result<Vec<Word>, String> {
let Some(index) = decode_memory(ptr).and_then(|value| value.checked_sub(1)) else {
if size == 1 {
return Ok(vec![ptr]);
}
return Err(format!("invalid memory handle {}", ptr));
};
let Some(pointer) = self.ptrs.get(index) else {
if size == 1 {
return Ok(vec![ptr]);
}
return Err(format!("invalid memory handle {}", ptr));
};
let slot = self
.slots
.get(pointer.slot)
.ok_or_else(|| format!("invalid memory slot {}", pointer.slot))?;
let end = pointer.offset + size;
if end > slot.len() {
return Err(format!(
"load out of bounds: offset={} size={} len={}",
pointer.offset,
size,
slot.len()
));
}
Ok(slot[pointer.offset..end].to_vec())
}
#[inline(always)]
fn store_word(&mut self, ptr: Word, src: Word) -> Result<(), String> {
let pointer = self.ptr(ptr)?.clone();
let slot = self
.slots
.get_mut(pointer.slot)
.ok_or_else(|| format!("invalid memory slot {}", pointer.slot))?;
let len = slot.len();
if pointer.offset >= len {
return Err(format!("store out of bounds: offset={} len={}", pointer.offset, len));
}
slot[pointer.offset] = src;
Ok(())
}
#[inline(always)]
fn store(&mut self, ptr: Word, src: &[Word], size: usize) -> Result<(), String> {
let pointer = self.ptr(ptr)?.clone();
let slot = self
.slots
.get_mut(pointer.slot)
.ok_or_else(|| format!("invalid memory slot {}", pointer.slot))?;
let end = pointer.offset + size;
if end > slot.len() {
return Err(format!(
"store out of bounds: offset={} size={} len={}",
pointer.offset,
size,
slot.len()
));
}
slot[pointer.offset..end].copy_from_slice(&src[..size]);
Ok(())
}
}
#[derive(Clone, Default)]
struct ArrayObject {
elem_size_words: usize,
data: Vec<Word>,
}
#[derive(Default)]
struct ArrayStorage {
arrays: Vec<ArrayObject>,
}
impl ArrayStorage {
fn alloc_array(&mut self, len: usize, elem_size_words: usize) -> Word {
self.arrays.push(ArrayObject {
elem_size_words,
data: vec![0; len.saturating_mul(elem_size_words)],
});
self.arrays.len() as Word
}
fn alloc_array_with_data(&mut self, data: Vec<Word>, elem_size_words: usize) -> Word {
self.arrays.push(ArrayObject {
elem_size_words,
data,
});
self.arrays.len() as Word
}
fn get(&self, handle: Word) -> Result<&ArrayObject, String> {
let index = handle
.checked_sub(1)
.ok_or_else(|| "invalid array handle 0".to_string())? as usize;
self.arrays
.get(index)
.ok_or_else(|| format!("invalid array handle {}", handle))
}
fn get_mut(&mut self, handle: Word) -> Result<&mut ArrayObject, String> {
let index = handle
.checked_sub(1)
.ok_or_else(|| "invalid array handle 0".to_string())? as usize;
self.arrays
.get_mut(index)
.ok_or_else(|| format!("invalid array handle {}", handle))
}
}
#[derive(Clone)]
struct ClosureObject {
function: Word,
upvalues: Vec<Word>,
indirect: Vec<bool>,
state_storage: StateStorage,
}
#[derive(Default)]
struct ClosureStorage {
closures: Vec<ClosureObject>,
}
impl ClosureStorage {
fn alloc(
&mut self,
function: Word,
upvalues: Vec<Word>,
indirect: Vec<bool>,
state_size: usize,
) -> Result<Word, String> {
if upvalues.len() != indirect.len() {
return Err(format!(
"closure upvalue metadata mismatch: {} values, {} flags",
upvalues.len(),
indirect.len()
));
}
let index = self.closures.len();
self.closures.push(ClosureObject {
function,
upvalues,
indirect,
state_storage: StateStorage::new(state_size),
});
Ok(encode_closure(index))
}
fn get(&self, handle: Word) -> Result<&ClosureObject, String> {
let index = decode_closure(handle)
.ok_or_else(|| format!("invalid closure handle {}", handle))?;
self.closures
.get(index)
.ok_or_else(|| format!("invalid closure handle {}", handle))
}
fn get_mut(&mut self, handle: Word) -> Result<&mut ClosureObject, String> {
let index = decode_closure(handle)
.ok_or_else(|| format!("invalid closure handle {}", handle))?;
self.closures
.get_mut(index)
.ok_or_else(|| format!("invalid closure handle {}", handle))
}
}
pub struct MimiumProgram<H: MimiumHost = PanicHost> {
pub host: H,
globals: Vec<Vec<Word>>,
function_states: Vec<StateStorage>,
current_function_state: Option<usize>,
state_storage_stack: Vec<Word>,
memory: MemoryStore,
closures: ClosureStorage,
arrays: ArrayStorage,
strings: Vec<String>,
}
impl MimiumProgram<PanicHost> {
pub fn new() -> Self {
Self::with_host(PanicHost)
}
}
impl<H: MimiumHost> MimiumProgram<H> {
pub fn with_host(host: H) -> Self {
Self {
host,
globals: vec![
/*__GLOBAL_SLOTS__*/
],
function_states: vec![
/*__FUNCTION_STATES__*/
],
current_function_state: None,
state_storage_stack: Vec::new(),
memory: MemoryStore::default(),
closures: ClosureStorage::default(),
arrays: ArrayStorage::default(),
strings: Vec::new(),
}
}
/*__CALL_DSP__*/
/*__CALL_MAIN__*/
fn call_function_handle(&mut self, handle: Word, args: &[Word]) -> Vec<Word> {
self.call_function_handle_with_memory(handle, args)
}
#[inline(always)]
fn get_current_statestorage(&mut self) -> &mut StateStorage {
if let Some(&closure_handle) = self.state_storage_stack.last() {
let index = unsafe { decode_closure(closure_handle).unwrap_unchecked() };
unsafe { &mut self.closures.closures.get_unchecked_mut(index).state_storage }
} else {
let function_index = unsafe { self.current_function_state.unwrap_unchecked() };
unsafe { self.function_states.get_unchecked_mut(function_index) }
}
}
fn get_current_closure(&self) -> Option<Word> {
self.state_storage_stack.last().copied()
}
fn call_ext(&mut self, name: &str, args: &[Word], ret_words: usize) -> Result<Vec<Word>, String> {
match name {
"min" => {
let lhs = word_to_f64(*args.get(0).ok_or_else(|| "min expects 2 args".to_string())?);
let rhs = word_to_f64(*args.get(1).ok_or_else(|| "min expects 2 args".to_string())?);
Ok(vec![f64_to_word(lhs.min(rhs))])
}
"max" => {
let lhs = word_to_f64(*args.get(0).ok_or_else(|| "max expects 2 args".to_string())?);
let rhs = word_to_f64(*args.get(1).ok_or_else(|| "max expects 2 args".to_string())?);
Ok(vec![f64_to_word(lhs.max(rhs))])
}
"probe" => Ok(args.first().copied().into_iter().collect()),
"probeln" => Ok(args.first().copied().into_iter().collect()),
"len" => {
let handle = *args.get(0).ok_or_else(|| "len expects 1 arg".to_string())?;
if handle == 0 {
Ok(vec![f64_to_word(0.0)])
} else {
let array = self.arrays.get(handle)?;
Ok(vec![f64_to_word(array.data.len() as mmmfloat)])
}
}
_ if name == "split_head" || name.starts_with("split_head$arity") => {
let elem_words = parse_specialized_arity(name, "split_head$arity", 1)
.ok_or_else(|| format!("invalid split_head specialization: {}", name))?;
let handle = *args.get(0).ok_or_else(|| format!("{} expects 1 arg", name))?;
if handle == 0 {
let mut result = vec![0; elem_words];
result.push(0);
return Ok(result);
}
let array = self.arrays.get(handle)?.clone();
if array.data.len() < elem_words {
return Err(format!("{}: array shorter than one element", name));
}
if array.data.len() % elem_words != 0 {
return Err(format!(
"{}: array length {} is not divisible by elem_words {}",
name,
array.data.len(),
elem_words
));
}
let head_words = array.data[..elem_words].to_vec();
let rest_data = array.data[elem_words..].to_vec();
let rest_handle = self
.arrays
.alloc_array_with_data(rest_data, array.elem_size_words);
let mut result = head_words;
result.push(rest_handle);
Ok(result)
}
_ if name == "split_tail" || name.starts_with("split_tail$arity") => {
let elem_words = parse_specialized_arity(name, "split_tail$arity", 1)
.ok_or_else(|| format!("invalid split_tail specialization: {}", name))?;
let handle = *args.get(0).ok_or_else(|| format!("{} expects 1 arg", name))?;
if handle == 0 {
let mut result = vec![0];
result.resize(elem_words + 1, 0);
return Ok(result);
}
let array = self.arrays.get(handle)?.clone();
if array.data.len() < elem_words {
return Err(format!("{}: array shorter than one element", name));
}
if array.data.len() % elem_words != 0 {
return Err(format!(
"{}: array length {} is not divisible by elem_words {}",
name,
array.data.len(),
elem_words
));
}
let tail_start = array.data.len() - elem_words;
let tail_words = array.data[tail_start..].to_vec();
let rest_data = array.data[..tail_start].to_vec();
let rest_handle = self
.arrays
.alloc_array_with_data(rest_data, array.elem_size_words);
let mut result = vec![rest_handle];
result.extend_from_slice(&tail_words);
Ok(result)
}
_ if name == "prepend" || name.starts_with("prepend$arity") => {
let elem_words = parse_specialized_arity(name, "prepend$arity", 1)
.ok_or_else(|| format!("invalid prepend specialization: {}", name))?;
let handle = *args
.get(elem_words)
.ok_or_else(|| format!("{} expects element + array args", name))?;
let mut data = args[..elem_words].to_vec();
if handle != 0 {
let array = self.arrays.get(handle)?.clone();
if array.elem_size_words != elem_words {
return Err(format!(
"{}: elem size mismatch, expected {} got {}",
name, elem_words, array.elem_size_words
));
}
data.extend_from_slice(&array.data);
}
Ok(vec![self.arrays.alloc_array_with_data(data, elem_words)])
}
_ if name == "append" || name.starts_with("append$arity") => {
let elem_words = parse_specialized_arity(name, "append$arity", 1)
.ok_or_else(|| format!("invalid append specialization: {}", name))?;
let handle = *args
.first()
.ok_or_else(|| format!("{} expects array + element args", name))?;
let mut data = if handle == 0 {
Vec::new()
} else {
let array = self.arrays.get(handle)?.clone();
if array.elem_size_words != elem_words {
return Err(format!(
"{}: elem size mismatch, expected {} got {}",
name, elem_words, array.elem_size_words
));
}
array.data
};
data.extend_from_slice(&args[1..1 + elem_words]);
Ok(vec![self.arrays.alloc_array_with_data(data, elem_words)])
}
_ => self.host.call_ext(name, args, ret_words),
}
}
fn alloc_string(&mut self, value: &str) -> Word {
self.strings.push(value.to_string());
self.strings.len() as Word
}
fn call_function_handle_with_memory(
&mut self,
handle: Word,
args: &[Word],
) -> Vec<Word> {
let previous_function_state = self.current_function_state;
let (dispatch_handle, current_closure) = if decode_closure(handle).is_some() {
let function = self
.closures
.get(handle)
.unwrap_or_else(|err| unreachable!("{err}"))
.function;
self.state_storage_stack.push(handle);
(function, Some(handle))
} else {
(handle, None)
};
if let Some(function_index) = decode_function(dispatch_handle) {
self.current_function_state = Some(function_index);
}
let result = match decode_function(dispatch_handle) {
/*__HANDLE_DISPATCH__*/
Some(index) => unreachable!("unknown function handle {}", index),
None => unreachable!("unsupported callable handle {}", handle),
};
if current_closure.is_some() {
self.state_storage_stack
.pop()
.unwrap_or_else(|| unreachable!("closure state stack underflow"));
}
self.current_function_state = previous_function_state;
result
}
fn load_upvalue(
&self,
closure_handle: Word,
index: usize,
size: usize,
) -> Result<Vec<Word>, String> {
let closure = self.closures.get(closure_handle)?;
let value = *closure
.upvalues
.get(index)
.ok_or_else(|| format!("invalid upvalue index {}", index))?;
let indirect = *closure
.indirect
.get(index)
.ok_or_else(|| format!("missing upvalue metadata {}", index))?;
if indirect {
self.memory.load(value, size)
} else if size == 1 {
Ok(vec![value])
} else {
Err(format!(
"direct upvalue {} does not support {} words in the initial Rust backend",
index, size
))
}
}
fn load_upvalue_word(&self, closure_handle: Word, index: usize) -> Result<Word, String> {
let closure = self.closures.get(closure_handle)?;
let value = *closure
.upvalues
.get(index)
.ok_or_else(|| format!("invalid upvalue index {}", index))?;
let indirect = *closure
.indirect
.get(index)
.ok_or_else(|| format!("missing upvalue metadata {}", index))?;
if indirect {
self.memory.load_word(value)
} else {
Ok(value)
}
}
fn store_upvalue(
&mut self,
closure_handle: Word,
index: usize,
src: &[Word],
size: usize,
) -> Result<(), String> {
let indirect = *self
.closures
.get(closure_handle)?
.indirect
.get(index)
.ok_or_else(|| format!("missing upvalue metadata {}", index))?;
if indirect {
let ptr = *self
.closures
.get(closure_handle)?
.upvalues
.get(index)
.ok_or_else(|| format!("invalid upvalue index {}", index))?;
self.memory.store(ptr, src, size)
} else if size == 1 {
let slot = self
.closures
.get_mut(closure_handle)?
.upvalues
.get_mut(index)
.ok_or_else(|| format!("invalid upvalue index {}", index))?;
*slot = src[0];
Ok(())
} else {
Err(format!(
"direct upvalue {} does not support {} words in the initial Rust backend",
index, size
))
}
}
fn store_upvalue_word(
&mut self,
closure_handle: Word,
index: usize,
src: Word,
) -> Result<(), String> {
let indirect = *self
.closures
.get(closure_handle)?
.indirect
.get(index)
.ok_or_else(|| format!("missing upvalue metadata {}", index))?;
if indirect {
let ptr = *self
.closures
.get(closure_handle)?
.upvalues
.get(index)
.ok_or_else(|| format!("invalid upvalue index {}", index))?;
self.memory.store_word(ptr, src)
} else {
let slot = self
.closures
.get_mut(closure_handle)?
.upvalues
.get_mut(index)
.ok_or_else(|| format!("invalid upvalue index {}", index))?;
*slot = src;
Ok(())
}
}
/*__FUNCTIONS__*/
}