diatom 0.5.2

The diatom programming language
Documentation
use crate::{file_manager::Loc, gc::Gc, interpreter::Func, IoWrite};

use self::{error::VmError, op::*};

pub mod error;
pub mod op;
use enum_dispatch::enum_dispatch;

#[derive(Clone, Copy)]
pub struct Ip {
    pub func_id: usize,
    pub inst: usize,
}

#[enum_dispatch(VmInst)]
pub trait Instruction {
    fn exec<Buffer: IoWrite>(
        &self,
        ip: Ip,
        gc: &mut Gc<Buffer>,
        out: &mut Buffer,
    ) -> Result<Ip, VmError>;
    fn decompile<Buffer: IoWrite>(&self, decompiled: &mut String, gc: &Gc<Buffer>);
}

#[allow(clippy::enum_variant_names)]
#[enum_dispatch]
pub enum VmInst {
    OpAdd,
    OpSub,
    OpMul,
    OpDiv,
    OpIDiv,
    OpMove,
    OpNot,
    OpNeg,
    OpJump,
    OpBranchTrue,
    OpBranchFalse,
    OpIs,
    OpEq,
    OpNe,
    OpLt,
    OpLe,
    OpGt,
    OpGe,
    OpPow,
    OpIndex,
    OpRem,
    OpCall,
    OpRet,
    OpGetTable,
    OpSetTable,
    OpGetTuple,
    OpSetTuple,
    OpSetIndex,
    OpMakeTable,
    OpSetMeta,
    OpMakeTuple,
    OpMakeList,
    OpAllocReg,
    OpMakeClosure,
    OpLoadConstant,
    OpYield,
    OpDummy,
}

pub struct Vm {
    ip: Ip,
}

impl Vm {
    pub fn new() -> Self {
        Self {
            ip: Ip {
                func_id: 0,
                inst: 0,
            },
        }
    }

    pub fn exec<Buffer: IoWrite>(
        &mut self,
        byte_code: &[Func],
        gc: &mut Gc<Buffer>,
        out: &mut Buffer,
    ) -> (VmError, Vec<Loc>) {
        loop {
            let Ip { func_id, inst } = self.ip;
            debug_assert!(byte_code.len() > func_id);
            let func = unsafe { byte_code.get_unchecked(func_id) };
            debug_assert!(func.insts.len() > inst);
            self.ip = match unsafe { func.insts.get_unchecked(inst) }
                .exec(self.ip, gc, out)
                .map_err(|err| {
                    let trace = gc.clean_call_stack();
                    let trace = trace
                        .into_iter()
                        .map(|Ip { func_id, inst }| {
                            let op = &byte_code[func_id].insts[inst - 1];
                            if let VmInst::OpCall(OpCall { loc, .. }) = op {
                                loc.clone()
                            } else {
                                unreachable!()
                            }
                        })
                        .collect();
                    (err, trace)
                }) {
                Ok(ip) => ip,
                Err(err) => return err,
            };
        }
    }

    pub fn reset_ip(&mut self) {
        self.ip = Ip {
            func_id: 0,
            inst: 0,
        }
    }
}