hara-native 0.1.13

HAL-free native host runtime and package launcher for Hara
Documentation
//! Global and multi-arity instruction helpers.

use crate::lang::data::Symbol;
use std::rc::Rc;

use super::{constant_string, Machine, Program, Value, VmClosure, VmMultiArity, VmSlot};

impl Machine {
    #[inline(never)]
    pub(super) fn exec_build_collection(
        &mut self,
        program: &Program,
        count: u16,
        map: bool,
        set: bool,
    ) -> Result<(), String> {
        let count = usize::from(count);
        if self.stack.len() < count {
            return Err("stack underflow".into());
        }
        let start = self.stack.len() - count;
        let values = self
            .stack
            .drain(start..)
            .map(|value| Machine::into_value(self.program.clone(), value))
            .collect::<Vec<_>>();
        let value = if map {
            crate::core::vm_build_map(values)?
        } else if set {
            crate::core::vm_build_set(values)?
        } else {
            crate::core::vm_build_vector(values)?
        };
        let _ = program;
        self.stack.push(value.into());
        Ok(())
    }

    #[inline(never)]
    pub(super) fn exec_build_list(&mut self, count: u16, concatenate: bool) -> Result<(), String> {
        let count = usize::from(count);
        if self.stack.len() < count {
            return Err("stack underflow".into());
        }
        let start = self.stack.len() - count;
        let values = self
            .stack
            .drain(start..)
            .map(|value| Machine::into_value(self.program.clone(), value))
            .collect::<Vec<_>>();
        let value = if concatenate {
            crate::core::vm_concat_list(values)?
        } else {
            crate::core::vm_build_list(values)
        };
        self.stack.push(value.into());
        Ok(())
    }

    #[inline(never)]
    pub(super) fn exec_to_vector(&mut self) -> Result<(), String> {
        let Some(value) = self.stack.pop() else {
            return Err("stack underflow".into());
        };
        let value = Machine::into_value(self.program.clone(), value);
        self.stack.push(crate::core::vm_to_vector(value)?.into());
        Ok(())
    }

    #[inline(never)]
    pub(super) fn exec_get_global(&mut self, program: &Program, index: u32) -> Result<(), String> {
        let Some(name) = constant_string(program, index) else {
            return Err(format!("constant index {index} out of range"));
        };
        let var = crate::core::vm_resolve_global(name)?;
        let value = var.deref_value();
        let slot = Machine::callable_key(&value)
            .and_then(|key| self.vm_globals.get(&key).cloned())
            .unwrap_or_else(|| value.into());
        self.stack.push(slot);
        Ok(())
    }

    #[inline(never)]
    pub(super) fn exec_def_global(
        &mut self,
        program: &Program,
        name: u32,
        metadata: Option<u16>,
    ) -> Result<(), String> {
        let Some(name) = constant_string(program, name) else {
            return Err(format!("constant index {name} out of range"));
        };
        let Some(value) = self.stack.pop() else {
            return Err("stack underflow".to_string());
        };
        let metadata = metadata.map(|index| program.var_metadata[usize::from(index)].clone());
        let runtime_value = Machine::into_value(self.program.clone(), value.clone());
        let var = crate::core::vm_def_global(name, runtime_value.clone(), metadata)?;
        self.remember_vm_global(&runtime_value, value.clone());
        self.stack.push(Value::Var(var).into());
        Ok(())
    }

    #[inline(never)]
    pub(super) fn exec_def_macro(
        &mut self,
        program: &Program,
        name: u32,
        metadata: Option<u16>,
    ) -> Result<(), String> {
        let Some(name) = constant_string(program, name) else {
            return Err(format!("constant index {name} out of range"));
        };
        let Some(value) = self.stack.pop() else {
            return Err("stack underflow".to_string());
        };
        let metadata = metadata.map(|index| program.var_metadata[usize::from(index)].clone());
        let runtime_value = Machine::into_value(self.program.clone(), value.clone());
        crate::core::vm_def_macro(name, runtime_value.clone(), metadata)?;
        self.remember_vm_global(&runtime_value, value.clone());
        self.stack.push(value);
        Ok(())
    }

    #[inline(never)]
    pub(super) fn exec_set_global(&mut self, program: &Program, index: u32) -> Result<(), String> {
        let Some(name) = constant_string(program, index) else {
            return Err(format!("constant index {index} out of range"));
        };
        let Some(value) = self.stack.pop() else {
            return Err("stack underflow".to_string());
        };
        let var = crate::core::namespace_registry().and_then(|registry| {
            registry
                .resolve(&Symbol::parse(name))
                .ok_or_else(|| format!("unbound var: {name}"))
        })?;
        if !crate::core::binding_is_local(&var) {
            return Err(format!(
                "Cannot replace referred Var without ns omission: {name}"
            ));
        }
        let runtime_value = Machine::into_value(self.program.clone(), value.clone());
        var.reset_value(runtime_value.clone());
        self.remember_vm_global(&runtime_value, value.clone());
        self.stack.push(value);
        Ok(())
    }

    #[inline(never)]
    pub(super) fn exec_var_global(&mut self, program: &Program, index: u32) -> Result<(), String> {
        let Some(name) = constant_string(program, index) else {
            return Err(format!("constant index {index} out of range"));
        };
        let var = crate::core::vm_resolve_global(name)?;
        self.stack.push(Value::Var(var).into());
        Ok(())
    }

    #[inline(never)]
    pub(super) fn exec_declare_global(
        &mut self,
        program: &Program,
        index: u32,
    ) -> Result<(), String> {
        let Some(name) = constant_string(program, index) else {
            return Err(format!("constant index {index} out of range"));
        };
        crate::core::vm_declare_global(name)?;
        self.stack.push(VmSlot::Nil);
        Ok(())
    }

    #[inline(never)]
    pub(super) fn exec_mutable_field_get(
        &mut self,
        program: &Program,
        index: u32,
    ) -> Result<(), String> {
        let Some(field) = constant_string(program, index) else {
            return Err(format!("constant index {index} out of range"));
        };
        let Some(value) = self.stack.pop() else {
            return Err("stack underflow".to_string());
        };
        let value = Machine::into_value(self.program.clone(), value);
        let value = crate::core::mutable_field_value(&value, field)?;
        self.stack.push(value.into());
        Ok(())
    }

    #[inline(never)]
    pub(super) fn exec_mutable_field_set(
        &mut self,
        program: &Program,
        index: u32,
    ) -> Result<(), String> {
        let Some(field) = constant_string(program, index) else {
            return Err(format!("constant index {index} out of range"));
        };
        let Some(replacement) = self.stack.pop() else {
            return Err("stack underflow".to_string());
        };
        let Some(receiver) = self.stack.pop() else {
            return Err("stack underflow".to_string());
        };
        let replacement = Machine::into_value(self.program.clone(), replacement);
        let receiver = Machine::into_value(self.program.clone(), receiver);
        let value = crate::core::mutable_field_set(&receiver, field, replacement)?;
        self.stack.push(value.into());
        Ok(())
    }

    #[inline(never)]
    pub(super) fn exec_instance_of(&mut self) -> Result<(), String> {
        let Some(value) = self.stack.pop() else {
            return Err("stack underflow".to_string());
        };
        let Some(ty) = self.stack.pop() else {
            return Err("stack underflow".to_string());
        };
        let ty = Machine::into_value(self.program.clone(), ty);
        let value = Machine::into_value(self.program.clone(), value);
        let value = crate::core::named_instance_of(&ty, &value)?;
        self.stack.push(value.into());
        Ok(())
    }

    #[inline(never)]
    pub(super) fn exec_make_multi_arity(
        &mut self,
        program: &Program,
        name: u32,
        count: u8,
    ) -> Result<(), String> {
        let count = usize::from(count);
        if self.stack.len() < count {
            return Err("stack underflow".to_string());
        }
        let Some(name) = constant_string(program, name).map(str::to_owned) else {
            return Err(format!("constant index {name} out of range"));
        };
        let start = self.stack.len() - count;
        if self.stack[start..]
            .iter()
            .any(|value| !matches!(value, VmSlot::InlineClosure { .. } | VmSlot::Closure(_)))
        {
            return Err("multi-arity clauses must be functions".to_string());
        }
        let clauses = self
            .stack
            .drain(start..)
            .map(|value| match value {
                VmSlot::InlineClosure { prototype, .. } => Rc::new(VmClosure {
                    prototype,
                    captures: Vec::new(),
                }),
                VmSlot::Closure(closure) => closure,
                _ => unreachable!("checked above"),
            })
            .collect();
        self.stack
            .push(VmSlot::MultiArity(Rc::new(VmMultiArity { name, clauses })));
        Ok(())
    }
}