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