12 个稳定版本 (4 个主要版本)
| 4.0.0 | 2024年7月17日 |
|---|---|
| 3.0.1 | 2024年7月17日 |
| 2.2.0 | 2024年7月15日 |
| 1.5.0 |
|
| 0.1.0 | 2024年7月1日 |
#184 in Rust 模式
1,700 每月下载量
55KB
1K SLoC
fn_vm
一个轻量级的基于框架/寄存器的虚拟机,可用于运行函数。
All problems in computer science can be solved by another level of indirection...
Except for the problem of too many layers of indirection.
灵感来自 iridium
工作原理
这基于这样的想法:如果我的虚拟机和抽象语法树使用相同的底层类型来表示值,但不仅仅是字节数组,我将能够构建一个更简单的运行时来连接这两个部分。此外,我不想实现我在大多数其他虚拟机中看到的所有指令,因为我目前只需要调用几个 Rust 函数。
fn_vm 可以存储任何实现 VMValue 特质的值,所有原始类型都有一个实现。
默认情况下,所有操作都在第一个框架中发生;每个框架都是一个函数定义,可以通过调用指令进入/退出。一旦使用值寄存器,该寄存器就会被删除(除非指令旨在为后续步骤(如 COPY)提供寄存器)
因此,您获得的是一些类型、一个 builder 和虚拟机(由框架组成)。
pub type VMFunction<T> = fn(&mut VM<T>, args: Vec<T>) -> Result<Option<T>, VMError>;
pub type HCFFunction<T> = fn(&mut VM<T>) -> Result<(), VMError>;
pub trait LazyCompiler<T: VMValue<T>> {
fn compile(&self, length: Length) -> Result<Frame<T>, VMError>;
}
#[derive(Clone, Debug, PartialEq)]
pub enum FrameValue<T: VMValue<T>> {
Value(T),
Frame(Frame<T>),
}
#[derive(Clone, Debug, PartialEq)]
pub struct Frame<T: VMValue<T>> {
pub instructions: Vec<u8>,
pub pc: usize,
pub locals: IndexMap<String, FrameValue<T>>,
pub parent: Option<usize>,
}
pub struct VM<T: VMValue<T>> {
pub fp: usize,
pub frames: Vec<Frame<T>>,
pub functions: Vec<VMFunction<T>>,
pub registers: HashMap<Length, T>,
pub index_registers: HashMap<Length, Length>,
pub bit_registers: HashMap<Length, bool>,
pub stack: Vec<usize>,
pub lazy_compiler: Option<Box<dyn LazyCompiler<T>>>,
pub program_data: Bytes,
pub hcf_trigger: Option<HCFFunction<T>>,
}
pub trait VMValue<T: VMValue<T>>: Display + Debug + Clone + PartialEq + Logical<T> + Add<Output=T> + Mul<Output=T> + Div<Output=T> + PartialOrd + Sub<Output=T> + BitAnd<Output=T> + BitOr<Output=T> + BitXor<Output=T> + Rem<Output=T> + Not<Output=T> + Neg<Output=T> + Shr<Output=T> + Shl<Output=T> {
}
指令
键
- r 是输入寄存器,通常输出寄存器是传入的第一个寄存器
- a、b 或 i 前缀是布尔或索引寄存器。
- f# 是框架索引
- l# 是调用延迟编译器的索引
变量
- let: 默认不可变变量
- mut: 可变变量
- frame: 可变框架,其值等于一个变量
此虚拟机提供了以下指令
- NOP: 无操作,将程序计数器移到下一个指令。
NOP - ADD: 将两个寄存器相加,存储输出。
ADD r1 r2 - MOD: 两个寄存器求模,存储输出。
MOD r1 r2 - SUB: 从两个寄存器中减去,存储输出。
SUB r1 r2 - 乘法:将两个寄存器的值相乘,并将结果存储。
MUL r1 r2 - 除法:将两个寄存器的值相除,并将结果存储。
DIV r1 r2 - 右移:将两个寄存器的值右移,并将结果存储。
SHR r1 r2 - 左移:将两个寄存器的值左移,并将结果存储。
SHL r1 r2 - 异或:将两个寄存器的值进行按位异或操作,并将结果存储。
XOR r1 r2 - 与运算:将两个寄存器的值进行按位与操作,并将结果存储。
BAND r1 r2 - 或运算:将两个寄存器的值进行按位或操作,并将结果存储。
BOR r1 r2 - 异或运算:将两个寄存器的值进行按位异或操作,并将结果存储。
BXOR r1 r2 - 逻辑异或:将两个寄存器的值进行逻辑异或操作,并将结果存储。
LXOR r1 r2 - 逻辑与:将两个寄存器的值进行逻辑与操作,并将结果存储。
LAND r1 r2 - 逻辑或:将两个寄存器的值进行逻辑或操作,并将结果存储。
LOR r1 r2 - 非:对寄存器的值进行非操作,并将结果存储。
NOT r1 - 布尔非:对寄存器的值进行布尔非操作,并将结果存储。
BNOT r1 - 负:对寄存器的值进行负操作,并将结果存储。
NEG r1 - 与运算:将两个寄存器的值进行与操作,并将结果存储。
AND r1 r2 - 或运算:将两个寄存器的值进行或操作,并将结果存储。
OR r1 r2 - 等于:比较两个寄存器的值,并将结果存储。
EQ r1 r2 - 等于(位比较):比较两个位寄存器的值,并将结果存储。
BEQ br1 br2 - 等于(索引比较):比较两个索引寄存器的值,并将结果存储。
IEQ ir1 ir2 - 不等于:比较两个寄存器的值,并将结果存储。
NEQ r1 r2 - 不等于(位比较):比较两个位寄存器的值,并将结果存储。
BNEQ br1 br2 - 不等于(索引比较):比较两个索引寄存器的值,并将结果存储。
INEQ ir1 ir2 - 反转:将寄存器中的值进行反转,并将结果存储。
REV r1 - 小于:比较两个寄存器的值,并将结果存储。
LT r1 r2 - 小于等于:比较两个寄存器的值,并将结果存储。
LTE r1 r2 - 大于:比较两个寄存器的值,并将结果存储。
GT r1 r2 - 大于等于:比较两个寄存器的值,并将结果存储。
GTE r1 r2 - 复制:将一个寄存器的值复制到另一个寄存器中,
COPY r1 r2 - 复制索引寄存器:从索引寄存器到。
CIR ir1 ir2 - 复制位寄存器:从位寄存器到。
CBR br1 br2 - 调用:调用一个帧,用于调用函数。
CALL f# - 调用索引寄存器:调用存储在索引寄存器#中的帧,用于调用由CFR创建的帧。
CALLR ir1 - 返回:从一个帧中返回,
RET <from> <to> - 获取局部值:
GLV r1 <name> - MVL: 从当前帧将局部值移动到fr1(跳过存在性检查),
MVL fr1 <name> - CPL: 从当前帧复制局部值到fr1(跳过存在性检查),
CPL fr1 <name> - CPM: 从当前帧复制局部值到fr1(跳过存在性检查),作为可变的。
CPM fr1 <name> - DLV: 从帧中删除局部值,
DLV fr1 <name> - DFV: 从当前帧中删除局部值,
DLV <name> - SLR: 从寄存器设置局部值,
SLR r1 <name> - SMR: 从寄存器设置局部可变值,
SMR r1 <name> - SLF: 从帧寄存器设置局部可变值,
SMF fr1 <name> - CFR: 创建帧,
CFR ir1 <len> [instructions] - DFR: 删除帧(软删除,否则其他所有帧索引都会损坏),重置帧并将指令设置为IVF。
DFR f# - DFI: 删除索引寄存器中的帧。
DFI ir1,// ir1, 删除帧 - FN: 调用提供的函数,
FN <op> <len> [registers] <out_register> - IVF: 无效帧,不直接使用。调用DFR的结果,
IVF - PSH: 将r1推入value_stack,
PSH r1 - PSHV: 将r1推入value_stack,
PSHV <value> - POP: 从value_stack弹出到r1,
POP r1 - HCF: 停止并引发火灾,停止VM(如果接收到此命令,则可以传递可选的hcf_trigger)。
HCF - LZY: 调用懒编译器以生成包含下一组指令的帧,并将fp移动到该帧,需要将
lazy_compiler设置为true。LZY l#pub trait LazyCompiler<T: Clone + PartialEq> { fn compile(&self, length: Length) -> Result<Frame<T>, VMError>; }
使用IVD作为任何无效命令的默认命令。
FN
这是迄今为止最复杂的指令,因为它实际上是一个传递到你的指令。
以下是一个使用构建器调用它的示例
注意:into()用于将长度转换为长度,从small_len到支持up to usize args。
fn run() {
let vm = VMBuilder::new()
.set_value(5.into(), 42) // store 42 in r5
.set_value(4.into(), 42) // store 42 in r4
.add_function_instruction(0, vec![5.into(), 4.into()], 3.into())
.add_function(move |_, args| {
let a = args[0];
let b = args[1];
Ok(Some(a + b))
})
.build();
vm.run().unwrap();
assert_eq!(vm.registers[3], 84);
}
依赖项
~2–2.8MB
~48K SLoC