Experiment with optimizations and benches
This commit is contained in:
parent
85274bfd8d
commit
20f451fe6c
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -322,7 +322,6 @@ dependencies = [
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"slab",
|
||||
"smallvec",
|
||||
"smartstring",
|
||||
]
|
||||
@ -727,15 +726,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
|
@ -23,7 +23,6 @@ smartstring = { version = "1.0.1", features = [
|
||||
"serde",
|
||||
], default-features = false }
|
||||
bitflags = { version = "2.6.0", features = ["serde"] }
|
||||
slab = "0.4.9"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.3.4", features = ["html_reports"] }
|
||||
@ -32,6 +31,10 @@ criterion = { version = "0.3.4", features = ["html_reports"] }
|
||||
name = "addictive_addition"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "fibonacci"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "logic_and"
|
||||
path = "tests/logic/and.rs"
|
||||
|
@ -3,7 +3,7 @@ use std::time::Duration;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use dust_lang::run;
|
||||
|
||||
const SOURCE: &str = "
|
||||
const SOURCE: &str = r"
|
||||
let mut i = 0
|
||||
|
||||
while i < 5_000_000 {
|
||||
|
32
dust-lang/benches/fibonacci.rs
Normal file
32
dust-lang/benches/fibonacci.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use dust_lang::run;
|
||||
|
||||
const SOURCE: &str = r"
|
||||
fn fib (n: int) -> int {
|
||||
if n <= 0 { return 0 }
|
||||
if n == 1 { return 1 }
|
||||
|
||||
fib(n - 1) + fib(n - 2)
|
||||
}
|
||||
|
||||
fib(25)
|
||||
";
|
||||
|
||||
fn addictive_addition(source: &str) {
|
||||
let _ = run(source).unwrap();
|
||||
}
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("fibonacci");
|
||||
|
||||
group.measurement_time(Duration::from_secs(15));
|
||||
group.bench_function("fibonacci", |b| {
|
||||
b.iter(|| addictive_addition(black_box(SOURCE)))
|
||||
});
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
@ -407,10 +407,10 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
||||
|
||||
let info_line = format!(
|
||||
"{} instructions, {} constants, {} locals, returns {}",
|
||||
self.chunk.len(),
|
||||
self.chunk.constants().len(),
|
||||
self.chunk.locals().len(),
|
||||
self.chunk.r#type().return_type
|
||||
self.chunk.instructions.len(),
|
||||
self.chunk.constants.len(),
|
||||
self.chunk.locals.len(),
|
||||
self.chunk.r#type.return_type
|
||||
);
|
||||
|
||||
self.write_centered_with_border_dim(&info_line)?;
|
||||
|
@ -30,22 +30,29 @@ pub struct Chunk {
|
||||
instructions: SmallVec<[(Instruction, Span); 32]>,
|
||||
constants: SmallVec<[ConcreteValue; 16]>,
|
||||
locals: SmallVec<[Local; 8]>,
|
||||
|
||||
stack_size: usize,
|
||||
}
|
||||
|
||||
impl Chunk {
|
||||
pub fn new(name: Option<DustString>) -> Self {
|
||||
pub fn new(
|
||||
name: Option<DustString>,
|
||||
r#type: FunctionType,
|
||||
instructions: SmallVec<[(Instruction, Span); 32]>,
|
||||
constants: SmallVec<[ConcreteValue; 16]>,
|
||||
locals: SmallVec<[Local; 8]>,
|
||||
stack_size: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
instructions: SmallVec::new(),
|
||||
constants: SmallVec::new(),
|
||||
locals: SmallVec::new(),
|
||||
r#type: FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Type::None,
|
||||
},
|
||||
r#type,
|
||||
instructions,
|
||||
constants,
|
||||
locals,
|
||||
stack_size,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_data(
|
||||
name: Option<DustString>,
|
||||
r#type: FunctionType,
|
||||
@ -59,6 +66,7 @@ impl Chunk {
|
||||
instructions: instructions.into(),
|
||||
constants: constants.into(),
|
||||
locals: locals.into(),
|
||||
stack_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,10 +78,6 @@ impl Chunk {
|
||||
&self.r#type
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.instructions.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.instructions.is_empty()
|
||||
}
|
||||
@ -91,21 +95,10 @@ impl Chunk {
|
||||
}
|
||||
|
||||
pub fn stack_size(&self) -> usize {
|
||||
self.instructions()
|
||||
.iter()
|
||||
.rev()
|
||||
.filter_map(|(instruction, _)| {
|
||||
if instruction.yields_value() {
|
||||
Some(instruction.a as usize + 1)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
self.stack_size
|
||||
}
|
||||
|
||||
pub fn disassembler<'a, W: Write>(&'a self, writer: &'a mut W) -> Disassembler<W> {
|
||||
pub fn disassembler<'a, W: Write>(&'a self, writer: &'a mut W) -> Disassembler<'a, W> {
|
||||
Disassembler::new(self, writer)
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ pub struct Compiler<'src> {
|
||||
instructions: SmallVec<[(Instruction, Type, Span); 32]>,
|
||||
constants: SmallVec<[ConcreteValue; 16]>,
|
||||
locals: SmallVec<[(Local, Type); 8]>,
|
||||
stack_size: usize,
|
||||
|
||||
lexer: Lexer<'src>,
|
||||
|
||||
@ -89,6 +90,7 @@ impl<'src> Compiler<'src> {
|
||||
instructions: SmallVec::new(),
|
||||
constants: SmallVec::new(),
|
||||
locals: SmallVec::new(),
|
||||
stack_size: 0,
|
||||
lexer,
|
||||
current_token,
|
||||
current_position,
|
||||
@ -124,7 +126,14 @@ impl<'src> Compiler<'src> {
|
||||
.map(|(local, _)| local)
|
||||
.collect::<SmallVec<[Local; 8]>>();
|
||||
|
||||
Chunk::with_data(self.self_name, r#type, instructions, self.constants, locals)
|
||||
Chunk::new(
|
||||
self.self_name,
|
||||
r#type,
|
||||
instructions,
|
||||
self.constants,
|
||||
locals,
|
||||
self.stack_size,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn compile(&mut self) -> Result<(), CompileError> {
|
||||
@ -151,7 +160,7 @@ impl<'src> Compiler<'src> {
|
||||
.rev()
|
||||
.find_map(|(instruction, _, _)| {
|
||||
if instruction.yields_value() {
|
||||
Some(instruction.a + 1)
|
||||
Some(instruction.a_field() + 1)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -381,6 +390,12 @@ impl<'src> Compiler<'src> {
|
||||
position.to_string()
|
||||
);
|
||||
|
||||
if instruction.yields_value() {
|
||||
let destination = instruction.a_field() as usize;
|
||||
|
||||
self.stack_size = (destination + 1).max(self.stack_size);
|
||||
}
|
||||
|
||||
self.instructions.push((instruction, r#type, position));
|
||||
}
|
||||
|
||||
@ -597,9 +612,9 @@ impl<'src> Compiler<'src> {
|
||||
instruction: &Instruction,
|
||||
) -> Result<(Argument, bool), CompileError> {
|
||||
let (argument, push_back) = match instruction.operation() {
|
||||
Operation::LoadConstant => (Argument::Constant(instruction.b), false),
|
||||
Operation::LoadConstant => (Argument::Constant(instruction.b_field()), false),
|
||||
Operation::GetLocal => {
|
||||
let local_index = instruction.b;
|
||||
let local_index = instruction.b_field();
|
||||
let (local, _) = self.get_local(local_index)?;
|
||||
|
||||
(Argument::Register(local.register_index), false)
|
||||
@ -617,12 +632,12 @@ impl<'src> Compiler<'src> {
|
||||
| Operation::LessEqual
|
||||
| Operation::Negate
|
||||
| Operation::Not
|
||||
| Operation::Call => (Argument::Register(instruction.a), true),
|
||||
| Operation::Call => (Argument::Register(instruction.a_field()), true),
|
||||
Operation::CallNative => {
|
||||
let function = NativeFunction::from(instruction.b);
|
||||
let function = NativeFunction::from(instruction.b_field());
|
||||
|
||||
if function.returns_value() {
|
||||
(Argument::Register(instruction.a), true)
|
||||
(Argument::Register(instruction.a_field()), true)
|
||||
} else {
|
||||
return Err(CompileError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
@ -882,9 +897,9 @@ impl<'src> Compiler<'src> {
|
||||
.iter()
|
||||
.rev()
|
||||
.nth(2)
|
||||
.map_or(0, |(instruction, _, _)| instruction.a);
|
||||
.map_or(0, |(instruction, _, _)| instruction.a_field());
|
||||
|
||||
left_instruction.a = destination;
|
||||
left_instruction.set_a_field(destination);
|
||||
}
|
||||
|
||||
let jump_index = self.instructions.len().saturating_sub(1);
|
||||
@ -932,7 +947,7 @@ impl<'src> Compiler<'src> {
|
||||
self.parse_sub_expression(&rule.precedence)?;
|
||||
|
||||
let (mut right_instruction, _, _) = self.instructions.last_mut().unwrap();
|
||||
right_instruction.a = left_instruction.a;
|
||||
right_instruction.set_a_field(left_instruction.a_field());
|
||||
|
||||
if is_logic_chain {
|
||||
let expression_length = self.instructions.len() - jump_index - 1;
|
||||
@ -1005,11 +1020,11 @@ impl<'src> Compiler<'src> {
|
||||
if self
|
||||
.instructions
|
||||
.last()
|
||||
.map_or(false, |(instruction, _, _)| instruction.is_math())
|
||||
.is_some_and(|(instruction, _, _)| instruction.is_math())
|
||||
{
|
||||
let (math_instruction, _, _) = self.instructions.last_mut().unwrap();
|
||||
|
||||
math_instruction.a = local_register_index;
|
||||
math_instruction.set_a_field(local_register_index);
|
||||
} else {
|
||||
let register = self.next_register() - 1;
|
||||
let set_local = Instruction::from(SetLocal {
|
||||
@ -1194,7 +1209,7 @@ impl<'src> Compiler<'src> {
|
||||
{
|
||||
let (mut loader, _, _) = self.instructions.last_mut().unwrap();
|
||||
|
||||
loader.c = true as u8;
|
||||
loader.set_c_field(true as u8);
|
||||
} else {
|
||||
if_block_distance += 1;
|
||||
let jump = Instruction::from(Jump {
|
||||
|
@ -40,8 +40,8 @@ pub fn optimize_test_with_explicit_booleans(compiler: &mut Compiler) {
|
||||
|
||||
let first_loader = compiler.instructions.iter().nth_back(1).unwrap();
|
||||
let second_loader = compiler.instructions.last().unwrap();
|
||||
let first_boolean = first_loader.0.b != 0;
|
||||
let second_boolean = second_loader.0.b != 0;
|
||||
let first_boolean = first_loader.0.b_field() != 0;
|
||||
let second_boolean = second_loader.0.b_field() != 0;
|
||||
|
||||
if first_boolean && !second_boolean {
|
||||
compiler.instructions.pop();
|
||||
@ -83,10 +83,10 @@ pub fn optimize_test_with_loader_arguments(compiler: &mut Compiler) {
|
||||
|
||||
let first_loader = &mut compiler.instructions.iter_mut().nth_back(1).unwrap().0;
|
||||
|
||||
first_loader.c = true as u8;
|
||||
first_loader.set_c_field(true as u8);
|
||||
|
||||
let first_loader_destination = first_loader.a;
|
||||
let first_loader_destination = first_loader.a_field();
|
||||
let second_loader = &mut compiler.instructions.last_mut().unwrap().0;
|
||||
|
||||
second_loader.a = first_loader_destination;
|
||||
second_loader.set_a_field(first_loader_destination);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ pub struct Add {
|
||||
|
||||
impl From<&Instruction> for Add {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let destination = instruction.a_field();
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Add {
|
||||
@ -23,10 +23,9 @@ impl From<Add> for Instruction {
|
||||
fn from(add: Add) -> Self {
|
||||
let operation = Operation::Add;
|
||||
let a = add.destination;
|
||||
let (b, b_options) = add.left.as_index_and_b_options();
|
||||
let (c, c_options) = add.right.as_index_and_c_options();
|
||||
let metadata = operation as u8 | b_options.bits() | c_options.bits();
|
||||
let (b, b_is_constant) = add.left.as_index_and_constant_flag();
|
||||
let (c, c_is_constant) = add.right.as_index_and_constant_flag();
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false)
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ pub struct Call {
|
||||
|
||||
impl From<&Instruction> for Call {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let destination = instruction.a_field();
|
||||
let function = instruction.b_as_argument();
|
||||
let argument_count = instruction.c;
|
||||
let argument_count = instruction.c_field();
|
||||
|
||||
Call {
|
||||
destination,
|
||||
@ -23,10 +23,9 @@ impl From<&Instruction> for Call {
|
||||
impl From<Call> for Instruction {
|
||||
fn from(call: Call) -> Self {
|
||||
let a = call.destination;
|
||||
let (b, b_options) = call.function.as_index_and_b_options();
|
||||
let (b, b_is_constant) = call.function.as_index_and_constant_flag();
|
||||
let c = call.argument_count;
|
||||
let metadata = Operation::Call as u8 | b_options.bits();
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(Operation::Call, a, b, c, b_is_constant, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -8,24 +8,24 @@ pub struct CallNative {
|
||||
|
||||
impl From<&Instruction> for CallNative {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let function = NativeFunction::from(instruction.b);
|
||||
let destination = instruction.a_field();
|
||||
let function = NativeFunction::from(instruction.b_field());
|
||||
|
||||
CallNative {
|
||||
destination,
|
||||
function,
|
||||
argument_count: instruction.c,
|
||||
argument_count: instruction.c_field(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CallNative> for Instruction {
|
||||
fn from(call_native: CallNative) -> Self {
|
||||
let metadata = Operation::CallNative as u8;
|
||||
let operation = Operation::CallNative;
|
||||
let a = call_native.destination;
|
||||
let b = call_native.function as u8;
|
||||
let c = call_native.argument_count;
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, c, false, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -8,17 +8,17 @@ pub struct Close {
|
||||
impl From<&Instruction> for Close {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
Close {
|
||||
from: instruction.b,
|
||||
to: instruction.c,
|
||||
from: instruction.b_field(),
|
||||
to: instruction.c_field(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Close> for Instruction {
|
||||
fn from(close: Close) -> Self {
|
||||
let metadata = Operation::Close as u8;
|
||||
let operation = Operation::Close;
|
||||
let (a, b, c) = (0, close.from, close.to);
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, c, false, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ pub struct Divide {
|
||||
|
||||
impl From<&Instruction> for Divide {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let destination = instruction.a_field();
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Divide {
|
||||
@ -21,11 +21,11 @@ impl From<&Instruction> for Divide {
|
||||
|
||||
impl From<Divide> for Instruction {
|
||||
fn from(divide: Divide) -> Self {
|
||||
let operation = Operation::Divide;
|
||||
let a = divide.destination;
|
||||
let (b, b_options) = divide.left.as_index_and_b_options();
|
||||
let (c, c_options) = divide.right.as_index_and_c_options();
|
||||
let metadata = Operation::Divide as u8 | b_options.bits() | c_options.bits();
|
||||
let (b, b_is_constant) = divide.left.as_index_and_constant_flag();
|
||||
let (c, c_is_constant) = divide.right.as_index_and_constant_flag();
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
use super::InstructionOptions;
|
||||
|
||||
pub struct Equal {
|
||||
pub destination: u8,
|
||||
pub value: bool,
|
||||
@ -11,8 +9,8 @@ pub struct Equal {
|
||||
|
||||
impl From<&Instruction> for Equal {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let value = instruction.d();
|
||||
let destination = instruction.a_field();
|
||||
let value = instruction.d_field();
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Equal {
|
||||
@ -26,17 +24,12 @@ impl From<&Instruction> for Equal {
|
||||
|
||||
impl From<Equal> for Instruction {
|
||||
fn from(equal: Equal) -> Self {
|
||||
let operation = Operation::Equal;
|
||||
let a = equal.destination;
|
||||
let (b, b_options) = equal.left.as_index_and_b_options();
|
||||
let (c, c_options) = equal.right.as_index_and_c_options();
|
||||
let d_options = if equal.value {
|
||||
InstructionOptions::D_IS_TRUE
|
||||
} else {
|
||||
InstructionOptions::empty()
|
||||
};
|
||||
let metadata =
|
||||
Operation::Equal as u8 | b_options.bits() | c_options.bits() | d_options.bits();
|
||||
let (b, b_is_constant) = equal.left.as_index_and_constant_flag();
|
||||
let (c, c_is_constant) = equal.right.as_index_and_constant_flag();
|
||||
let d = equal.value;
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, d)
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ pub struct GetLocal {
|
||||
|
||||
impl From<&Instruction> for GetLocal {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let local_index = instruction.b;
|
||||
let destination = instruction.a_field();
|
||||
let local_index = instruction.b_field();
|
||||
|
||||
GetLocal {
|
||||
destination,
|
||||
@ -19,11 +19,10 @@ impl From<&Instruction> for GetLocal {
|
||||
|
||||
impl From<GetLocal> for Instruction {
|
||||
fn from(get_local: GetLocal) -> Self {
|
||||
let operation = Operation::GetLocal;
|
||||
let a = get_local.destination;
|
||||
let b = get_local.local_index;
|
||||
let c = 0;
|
||||
let metadata = Operation::GetLocal as u8;
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, 0, false, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -8,19 +8,18 @@ pub struct Jump {
|
||||
impl From<&Instruction> for Jump {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
Jump {
|
||||
offset: instruction.b,
|
||||
is_positive: instruction.c != 0,
|
||||
offset: instruction.b_field(),
|
||||
is_positive: instruction.c_field() != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Jump> for Instruction {
|
||||
fn from(jump: Jump) -> Self {
|
||||
let metadata = Operation::Jump as u8;
|
||||
let a = 0;
|
||||
let operation = Operation::Jump;
|
||||
let b = jump.offset;
|
||||
let c = jump.is_positive as u8;
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, 0, b, c, false, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
use super::InstructionOptions;
|
||||
|
||||
pub struct Less {
|
||||
pub destination: u8,
|
||||
pub value: bool,
|
||||
@ -11,8 +9,8 @@ pub struct Less {
|
||||
|
||||
impl From<&Instruction> for Less {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let value = instruction.d();
|
||||
let destination = instruction.a_field();
|
||||
let value = instruction.d_field();
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Less {
|
||||
@ -26,17 +24,12 @@ impl From<&Instruction> for Less {
|
||||
|
||||
impl From<Less> for Instruction {
|
||||
fn from(less: Less) -> Self {
|
||||
let operation = Operation::Less;
|
||||
let a = less.destination;
|
||||
let (b, b_options) = less.left.as_index_and_b_options();
|
||||
let (c, c_options) = less.right.as_index_and_c_options();
|
||||
let d_options = if less.value {
|
||||
InstructionOptions::D_IS_TRUE
|
||||
} else {
|
||||
InstructionOptions::empty()
|
||||
};
|
||||
let metadata =
|
||||
Operation::Less as u8 | b_options.bits() | c_options.bits() | d_options.bits();
|
||||
let (b, b_is_constant) = less.left.as_index_and_constant_flag();
|
||||
let (c, c_is_constant) = less.right.as_index_and_constant_flag();
|
||||
let d = less.value;
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, d)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
use super::InstructionOptions;
|
||||
|
||||
pub struct LessEqual {
|
||||
pub destination: u8,
|
||||
pub value: bool,
|
||||
@ -11,8 +9,8 @@ pub struct LessEqual {
|
||||
|
||||
impl From<&Instruction> for LessEqual {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let value = instruction.d();
|
||||
let destination = instruction.a_field();
|
||||
let value = instruction.d_field();
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
LessEqual {
|
||||
@ -26,17 +24,12 @@ impl From<&Instruction> for LessEqual {
|
||||
|
||||
impl From<LessEqual> for Instruction {
|
||||
fn from(less_equal: LessEqual) -> Self {
|
||||
let operation = Operation::LessEqual;
|
||||
let a = less_equal.destination;
|
||||
let (b, b_options) = less_equal.left.as_index_and_b_options();
|
||||
let (c, c_options) = less_equal.right.as_index_and_c_options();
|
||||
let d_options = if less_equal.value {
|
||||
InstructionOptions::D_IS_TRUE
|
||||
} else {
|
||||
InstructionOptions::empty()
|
||||
};
|
||||
let metadata =
|
||||
Operation::LessEqual as u8 | b_options.bits() | c_options.bits() | d_options.bits();
|
||||
let (b, b_options) = less_equal.left.as_index_and_constant_flag();
|
||||
let (c, c_options) = less_equal.right.as_index_and_constant_flag();
|
||||
let d = less_equal.value;
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, c, b_options, c_options, d)
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ pub struct LoadBoolean {
|
||||
|
||||
impl From<&Instruction> for LoadBoolean {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let value = instruction.b != 0;
|
||||
let jump_next = instruction.c != 0;
|
||||
let destination = instruction.a_field();
|
||||
let value = instruction.b_field() != 0;
|
||||
let jump_next = instruction.c_field() != 0;
|
||||
|
||||
LoadBoolean {
|
||||
destination,
|
||||
@ -22,11 +22,11 @@ impl From<&Instruction> for LoadBoolean {
|
||||
|
||||
impl From<LoadBoolean> for Instruction {
|
||||
fn from(load_boolean: LoadBoolean) -> Self {
|
||||
let metadata = Operation::LoadBoolean as u8;
|
||||
let operation = Operation::LoadBoolean;
|
||||
let a = load_boolean.destination;
|
||||
let b = load_boolean.value as u8;
|
||||
let c = load_boolean.jump_next as u8;
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, c, false, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ pub struct LoadConstant {
|
||||
|
||||
impl From<&Instruction> for LoadConstant {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let constant_index = instruction.b;
|
||||
let jump_next = instruction.c != 0;
|
||||
let destination = instruction.a_field();
|
||||
let constant_index = instruction.b_field();
|
||||
let jump_next = instruction.c_field() != 0;
|
||||
|
||||
LoadConstant {
|
||||
destination,
|
||||
@ -22,11 +22,11 @@ impl From<&Instruction> for LoadConstant {
|
||||
|
||||
impl From<LoadConstant> for Instruction {
|
||||
fn from(load_constant: LoadConstant) -> Self {
|
||||
let metadata = Operation::LoadConstant as u8;
|
||||
let operation = Operation::LoadConstant;
|
||||
let a = load_constant.destination;
|
||||
let b = load_constant.constant_index;
|
||||
let c = load_constant.jump_next as u8;
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, c, false, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ pub struct LoadList {
|
||||
|
||||
impl From<&Instruction> for LoadList {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let start_register = instruction.b;
|
||||
let destination = instruction.a_field();
|
||||
let start_register = instruction.b_field();
|
||||
|
||||
LoadList {
|
||||
destination,
|
||||
@ -19,11 +19,10 @@ impl From<&Instruction> for LoadList {
|
||||
|
||||
impl From<LoadList> for Instruction {
|
||||
fn from(load_list: LoadList) -> Self {
|
||||
let metadata = Operation::LoadList as u8;
|
||||
let operation = Operation::LoadList;
|
||||
let a = load_list.destination;
|
||||
let b = load_list.start_register;
|
||||
let c = 0;
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, 0, false, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ pub struct LoadSelf {
|
||||
|
||||
impl From<&Instruction> for LoadSelf {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let destination = instruction.a_field();
|
||||
|
||||
LoadSelf { destination }
|
||||
}
|
||||
@ -14,11 +14,9 @@ impl From<&Instruction> for LoadSelf {
|
||||
|
||||
impl From<LoadSelf> for Instruction {
|
||||
fn from(load_self: LoadSelf) -> Self {
|
||||
let metadata = Operation::LoadSelf as u8;
|
||||
let operation = Operation::LoadSelf;
|
||||
let a = load_self.destination;
|
||||
let b = 0;
|
||||
let c = 0;
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, 0, 0, false, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +109,6 @@ mod multiply;
|
||||
mod negate;
|
||||
mod not;
|
||||
mod operation;
|
||||
mod options;
|
||||
mod r#return;
|
||||
mod set_local;
|
||||
mod subtract;
|
||||
@ -135,7 +134,6 @@ pub use multiply::Multiply;
|
||||
pub use negate::Negate;
|
||||
pub use not::Not;
|
||||
pub use operation::Operation;
|
||||
pub use options::InstructionOptions;
|
||||
pub use r#move::Move;
|
||||
pub use r#return::Return;
|
||||
pub use set_local::SetLocal;
|
||||
@ -152,22 +150,92 @@ use crate::NativeFunction;
|
||||
///
|
||||
/// See the [module-level documentation](index.html) for more information.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct Instruction {
|
||||
pub metadata: u8,
|
||||
pub struct Instruction(u32);
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct InstructionData {
|
||||
pub operation: Operation,
|
||||
pub a: u8,
|
||||
pub b: u8,
|
||||
pub c: u8,
|
||||
pub b_is_constant: bool,
|
||||
pub c_is_constant: bool,
|
||||
pub d: bool,
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
pub fn operation(&self) -> Operation {
|
||||
let operation_bits = self.metadata & 0b0001_1111;
|
||||
pub fn new(
|
||||
operation: Operation,
|
||||
a: u8,
|
||||
b: u8,
|
||||
c: u8,
|
||||
b_is_constant: bool,
|
||||
c_is_constant: bool,
|
||||
d: bool,
|
||||
) -> Instruction {
|
||||
let bits = operation as u32
|
||||
| ((b_is_constant as u32) << 5)
|
||||
| ((c_is_constant as u32) << 6)
|
||||
| ((d as u32) << 7)
|
||||
| ((a as u32) << 8)
|
||||
| ((b as u32) << 16)
|
||||
| ((c as u32) << 24);
|
||||
|
||||
Operation::from(operation_bits)
|
||||
Instruction(bits)
|
||||
}
|
||||
|
||||
pub fn d(&self) -> bool {
|
||||
self.metadata & 0b1000_0000 != 0
|
||||
pub fn operation(&self) -> Operation {
|
||||
let operation_bits = self.0 & 0b0001_1111;
|
||||
|
||||
Operation::from(operation_bits as u8)
|
||||
}
|
||||
|
||||
pub fn b_is_constant(&self) -> bool {
|
||||
(self.0 >> 5) & 1 == 1
|
||||
}
|
||||
|
||||
pub fn c_is_constant(&self) -> bool {
|
||||
(self.0 >> 6) & 1 == 1
|
||||
}
|
||||
|
||||
pub fn d_field(&self) -> bool {
|
||||
(self.0 >> 7) & 1 == 1
|
||||
}
|
||||
|
||||
pub fn a_field(&self) -> u8 {
|
||||
(self.0 >> 8) as u8
|
||||
}
|
||||
|
||||
pub fn b_field(&self) -> u8 {
|
||||
(self.0 >> 16) as u8
|
||||
}
|
||||
|
||||
pub fn c_field(&self) -> u8 {
|
||||
(self.0 >> 24) as u8
|
||||
}
|
||||
|
||||
pub fn set_a_field(&mut self, bits: u8) {
|
||||
self.0 = (self.0 & 0xFFFF00FF) | ((bits as u32) << 8);
|
||||
}
|
||||
|
||||
pub fn set_b_field(&mut self, bits: u8) {
|
||||
self.0 = (self.0 & 0xFFFF00FF) | ((bits as u32) << 16);
|
||||
}
|
||||
|
||||
pub fn set_c_field(&mut self, bits: u8) {
|
||||
self.0 = (self.0 & 0xFF00FFFF) | ((bits as u32) << 24);
|
||||
}
|
||||
|
||||
pub fn decode(self) -> InstructionData {
|
||||
InstructionData {
|
||||
operation: self.operation(),
|
||||
a: self.a_field(),
|
||||
b: self.b_field(),
|
||||
c: self.c_field(),
|
||||
b_is_constant: self.b_is_constant(),
|
||||
c_is_constant: self.c_is_constant(),
|
||||
d: self.d_field(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#move(from: u8, to: u8) -> Instruction {
|
||||
@ -373,7 +441,7 @@ impl Instruction {
|
||||
|
||||
pub fn as_argument(&self) -> Option<Argument> {
|
||||
match self.operation() {
|
||||
Operation::LoadConstant => Some(Argument::Constant(self.b)),
|
||||
Operation::LoadConstant => Some(Argument::Constant(self.b_field())),
|
||||
Operation::LoadBoolean
|
||||
| Operation::LoadList
|
||||
| Operation::LoadSelf
|
||||
@ -388,12 +456,12 @@ impl Instruction {
|
||||
| Operation::LessEqual
|
||||
| Operation::Negate
|
||||
| Operation::Not
|
||||
| Operation::Call => Some(Argument::Register(self.a)),
|
||||
| Operation::Call => Some(Argument::Register(self.a_field())),
|
||||
Operation::CallNative => {
|
||||
let function = NativeFunction::from(self.b);
|
||||
let function = NativeFunction::from(self.b_field());
|
||||
|
||||
if function.returns_value() {
|
||||
Some(Argument::Register(self.a))
|
||||
Some(Argument::Register(self.a_field()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -403,28 +471,23 @@ impl Instruction {
|
||||
}
|
||||
|
||||
pub fn b_as_argument(&self) -> Argument {
|
||||
let b_is_constant = self.metadata & 0b0010_0000 != 0;
|
||||
|
||||
if b_is_constant {
|
||||
Argument::Constant(self.b)
|
||||
if self.b_is_constant() {
|
||||
Argument::Constant(self.b_field())
|
||||
} else {
|
||||
Argument::Register(self.b)
|
||||
Argument::Register(self.b_field())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn b_and_c_as_arguments(&self) -> (Argument, Argument) {
|
||||
let b_is_constant = self.metadata & 0b0010_0000 != 0;
|
||||
let c_is_constant = self.metadata & 0b0100_0000 != 0;
|
||||
|
||||
let left = if b_is_constant {
|
||||
Argument::Constant(self.b)
|
||||
let left = if self.b_is_constant() {
|
||||
Argument::Constant(self.b_field())
|
||||
} else {
|
||||
Argument::Register(self.b)
|
||||
Argument::Register(self.b_field())
|
||||
};
|
||||
let right = if c_is_constant {
|
||||
Argument::Constant(self.c)
|
||||
let right = if self.c_is_constant() {
|
||||
Argument::Constant(self.c_field())
|
||||
} else {
|
||||
Argument::Register(self.c)
|
||||
Argument::Register(self.c_field())
|
||||
};
|
||||
|
||||
(left, right)
|
||||
@ -451,7 +514,7 @@ impl Instruction {
|
||||
| Operation::LessEqual
|
||||
| Operation::Call => true,
|
||||
Operation::CallNative => {
|
||||
let function = NativeFunction::from(self.b);
|
||||
let function = NativeFunction::from(self.b_field());
|
||||
|
||||
function.returns_value()
|
||||
}
|
||||
@ -655,7 +718,7 @@ impl Instruction {
|
||||
if is_positive {
|
||||
format!("JUMP +{offset}")
|
||||
} else {
|
||||
format!("JUMP -{}", offset - 1)
|
||||
format!("JUMP -{offset}")
|
||||
}
|
||||
}
|
||||
Operation::Call => {
|
||||
@ -751,17 +814,10 @@ impl Argument {
|
||||
matches!(self, Argument::Register(_))
|
||||
}
|
||||
|
||||
pub fn as_index_and_b_options(&self) -> (u8, InstructionOptions) {
|
||||
pub fn as_index_and_constant_flag(&self) -> (u8, bool) {
|
||||
match self {
|
||||
Argument::Constant(index) => (*index, InstructionOptions::B_IS_CONSTANT),
|
||||
Argument::Register(index) => (*index, InstructionOptions::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_index_and_c_options(&self) -> (u8, InstructionOptions) {
|
||||
match self {
|
||||
Argument::Constant(index) => (*index, InstructionOptions::C_IS_CONSTANT),
|
||||
Argument::Register(index) => (*index, InstructionOptions::empty()),
|
||||
Argument::Constant(index) => (*index, true),
|
||||
Argument::Register(index) => (*index, false),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -774,23 +830,3 @@ impl Display for Argument {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::mem::offset_of;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn instruction_is_4_bytes() {
|
||||
assert_eq!(size_of::<Instruction>(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instruction_layout() {
|
||||
assert_eq!(offset_of!(Instruction, metadata), 0);
|
||||
assert_eq!(offset_of!(Instruction, a), 1);
|
||||
assert_eq!(offset_of!(Instruction, b), 2);
|
||||
assert_eq!(offset_of!(Instruction, c), 3);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ pub struct Modulo {
|
||||
|
||||
impl From<&Instruction> for Modulo {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let destination = instruction.a_field();
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Modulo {
|
||||
@ -21,11 +21,11 @@ impl From<&Instruction> for Modulo {
|
||||
|
||||
impl From<Modulo> for Instruction {
|
||||
fn from(modulo: Modulo) -> Self {
|
||||
let operation = Operation::Modulo;
|
||||
let a = modulo.destination;
|
||||
let (b, b_options) = modulo.left.as_index_and_b_options();
|
||||
let (c, c_options) = modulo.right.as_index_and_c_options();
|
||||
let metadata = Operation::Modulo as u8 | b_options.bits() | c_options.bits();
|
||||
let (b, b_is_constant) = modulo.left.as_index_and_constant_flag();
|
||||
let (c, c_is_constant) = modulo.right.as_index_and_constant_flag();
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false)
|
||||
}
|
||||
}
|
||||
|
@ -8,19 +8,18 @@ pub struct Move {
|
||||
impl From<&Instruction> for Move {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
Move {
|
||||
from: instruction.b,
|
||||
to: instruction.c,
|
||||
from: instruction.b_field(),
|
||||
to: instruction.c_field(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Move> for Instruction {
|
||||
fn from(r#move: Move) -> Self {
|
||||
let metadata = Operation::Move as u8;
|
||||
let a = 0;
|
||||
let operation = Operation::Move;
|
||||
let b = r#move.from;
|
||||
let c = r#move.to;
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, 0, b, c, false, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ pub struct Multiply {
|
||||
|
||||
impl From<&Instruction> for Multiply {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let destination = instruction.a_field();
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Multiply {
|
||||
@ -21,11 +21,11 @@ impl From<&Instruction> for Multiply {
|
||||
|
||||
impl From<Multiply> for Instruction {
|
||||
fn from(multiply: Multiply) -> Self {
|
||||
let operation = Operation::Multiply;
|
||||
let a = multiply.destination;
|
||||
let (b, b_options) = multiply.left.as_index_and_b_options();
|
||||
let (c, c_options) = multiply.right.as_index_and_c_options();
|
||||
let metadata = Operation::Multiply as u8 | b_options.bits() | c_options.bits();
|
||||
let (b, b_options) = multiply.left.as_index_and_constant_flag();
|
||||
let (c, c_options) = multiply.right.as_index_and_constant_flag();
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, c, b_options, c_options, false)
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ pub struct Negate {
|
||||
|
||||
impl From<&Instruction> for Negate {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let destination = instruction.a_field();
|
||||
let argument = instruction.b_as_argument();
|
||||
|
||||
Negate {
|
||||
@ -19,11 +19,11 @@ impl From<&Instruction> for Negate {
|
||||
|
||||
impl From<Negate> for Instruction {
|
||||
fn from(negate: Negate) -> Self {
|
||||
let operation = Operation::Negate;
|
||||
let a = negate.destination;
|
||||
let (b, b_options) = negate.argument.as_index_and_b_options();
|
||||
let (b, b_is_constant) = negate.argument.as_index_and_constant_flag();
|
||||
let c = 0;
|
||||
let metadata = Operation::Negate as u8 | b_options.bits();
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, c, b_is_constant, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ pub struct Not {
|
||||
|
||||
impl From<&Instruction> for Not {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let destination = instruction.a_field();
|
||||
let argument = instruction.b_as_argument();
|
||||
|
||||
Not {
|
||||
@ -19,11 +19,10 @@ impl From<&Instruction> for Not {
|
||||
|
||||
impl From<Not> for Instruction {
|
||||
fn from(not: Not) -> Self {
|
||||
let operation = Operation::Not;
|
||||
let a = not.destination;
|
||||
let (b, b_options) = not.argument.as_index_and_b_options();
|
||||
let c = 0;
|
||||
let metadata = Operation::Not as u8 | b_options.bits();
|
||||
let (b, b_is_constant) = not.argument.as_index_and_constant_flag();
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, 0, b_is_constant, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
//! Byte that uses its bits as boolean flags to represent information about an instruction's
|
||||
//! arguments. Additionally, one bit is used as the instruction's `D` field.
|
||||
//!
|
||||
//! See the [instruction documentation](crate::instruction) for more information.
|
||||
use bitflags::bitflags;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
bitflags! {
|
||||
/// Byte that uses its bits as boolean flags to represent an instruction's options and D field.
|
||||
///
|
||||
/// See the [instruction documentation](crate::instruction) for more information.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct InstructionOptions: u8 {
|
||||
const B_IS_CONSTANT = 0b0010_0000;
|
||||
|
||||
const C_IS_CONSTANT = 0b0100_0000;
|
||||
|
||||
const D_IS_TRUE = 0b1000_0000;
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionOptions {
|
||||
pub fn b_is_constant(self) -> bool {
|
||||
self.contains(Self::B_IS_CONSTANT)
|
||||
}
|
||||
|
||||
pub fn c_is_constant(self) -> bool {
|
||||
self.contains(Self::C_IS_CONSTANT)
|
||||
}
|
||||
|
||||
pub fn d(self) -> bool {
|
||||
self.contains(Self::D_IS_TRUE)
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ pub struct Return {
|
||||
|
||||
impl From<&Instruction> for Return {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let should_return_value = instruction.b != 0;
|
||||
let should_return_value = instruction.b_field() != 0;
|
||||
|
||||
Return {
|
||||
should_return_value,
|
||||
@ -16,11 +16,9 @@ impl From<&Instruction> for Return {
|
||||
|
||||
impl From<Return> for Instruction {
|
||||
fn from(r#return: Return) -> Self {
|
||||
let metadata = Operation::Return as u8;
|
||||
let a = 0;
|
||||
let operation = Operation::Return;
|
||||
let b = r#return.should_return_value as u8;
|
||||
let c = 0;
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, 0, b, 0, false, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ pub struct SetLocal {
|
||||
|
||||
impl From<&Instruction> for SetLocal {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let register_index = instruction.b;
|
||||
let local_index = instruction.c;
|
||||
let register_index = instruction.b_field();
|
||||
let local_index = instruction.c_field();
|
||||
|
||||
SetLocal {
|
||||
register_index,
|
||||
@ -19,11 +19,10 @@ impl From<&Instruction> for SetLocal {
|
||||
|
||||
impl From<SetLocal> for Instruction {
|
||||
fn from(set_local: SetLocal) -> Self {
|
||||
let metadata = Operation::SetLocal as u8;
|
||||
let a = 0;
|
||||
let operation = Operation::SetLocal;
|
||||
let b = set_local.register_index;
|
||||
let c = set_local.local_index;
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, 0, b, c, false, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ pub struct Subtract {
|
||||
|
||||
impl From<&Instruction> for Subtract {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let destination = instruction.a_field();
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Subtract {
|
||||
@ -21,11 +21,11 @@ impl From<&Instruction> for Subtract {
|
||||
|
||||
impl From<Subtract> for Instruction {
|
||||
fn from(subtract: Subtract) -> Self {
|
||||
let operation = Operation::Subtract;
|
||||
let a = subtract.destination;
|
||||
let (b, b_options) = subtract.left.as_index_and_b_options();
|
||||
let (c, c_options) = subtract.right.as_index_and_c_options();
|
||||
let metadata = Operation::Subtract as u8 | b_options.bits() | c_options.bits();
|
||||
let (b, b_is_constant) = subtract.left.as_index_and_constant_flag();
|
||||
let (c, c_is_constant) = subtract.right.as_index_and_constant_flag();
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false)
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ pub struct Test {
|
||||
impl From<&Instruction> for Test {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let argument = instruction.b_as_argument();
|
||||
let test_value = instruction.c != 0;
|
||||
let test_value = instruction.c_field() != 0;
|
||||
|
||||
Test {
|
||||
argument,
|
||||
@ -19,11 +19,10 @@ impl From<&Instruction> for Test {
|
||||
|
||||
impl From<Test> for Instruction {
|
||||
fn from(test: Test) -> Self {
|
||||
let a = 0;
|
||||
let (b, options) = test.argument.as_index_and_b_options();
|
||||
let operation = Operation::Test;
|
||||
let (b, b_is_constant) = test.argument.as_index_and_constant_flag();
|
||||
let c = test.test_value as u8;
|
||||
let metadata = Operation::Test as u8 | options.bits();
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, 0, b, c, b_is_constant, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ pub struct TestSet {
|
||||
|
||||
impl From<&Instruction> for TestSet {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let destination = instruction.a_field();
|
||||
let argument = instruction.b_as_argument();
|
||||
let test_value = instruction.c != 0;
|
||||
let test_value = instruction.c_field() != 0;
|
||||
|
||||
TestSet {
|
||||
destination,
|
||||
@ -22,11 +22,11 @@ impl From<&Instruction> for TestSet {
|
||||
|
||||
impl From<TestSet> for Instruction {
|
||||
fn from(test_set: TestSet) -> Self {
|
||||
let operation = Operation::Test;
|
||||
let a = test_set.destination;
|
||||
let (b, b_options) = test_set.argument.as_index_and_b_options();
|
||||
let (b, b_is_constant) = test_set.argument.as_index_and_constant_flag();
|
||||
let c = test_set.test_value as u8;
|
||||
let metadata = Operation::TestSet as u8 | b_options.bits();
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
Instruction::new(operation, a, b, c, b_is_constant, false, false)
|
||||
}
|
||||
}
|
||||
|
@ -619,7 +619,7 @@ pub struct LexRule<'src> {
|
||||
lexer: LexerFn<'src>,
|
||||
}
|
||||
|
||||
impl<'src> From<&char> for LexRule<'src> {
|
||||
impl From<&char> for LexRule<'_> {
|
||||
fn from(char: &char) -> Self {
|
||||
match char {
|
||||
'0'..='9' => LexRule {
|
||||
|
@ -42,7 +42,7 @@ pub mod vm;
|
||||
pub use crate::chunk::{Chunk, Disassembler, Local, Scope};
|
||||
pub use crate::compiler::{compile, CompileError, Compiler};
|
||||
pub use crate::dust_error::{AnnotatedError, DustError};
|
||||
pub use crate::instruction::{Argument, Instruction, Operation};
|
||||
pub use crate::instruction::{Argument, Instruction, InstructionData, Operation};
|
||||
pub use crate::lexer::{lex, LexError, Lexer};
|
||||
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
||||
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
|
||||
|
@ -93,7 +93,7 @@ define_tokens! {
|
||||
StarEqual,
|
||||
}
|
||||
|
||||
impl<'src> Token<'src> {
|
||||
impl Token<'_> {
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
@ -382,7 +382,7 @@ impl<'src> Token<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> Display for Token<'src> {
|
||||
impl Display for Token<'_> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Token::ArrowThin => write!(f, "->"),
|
||||
|
@ -4,12 +4,11 @@ use std::{
|
||||
io,
|
||||
};
|
||||
|
||||
use slab::Slab;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::{
|
||||
compile, instruction::*, AbstractValue, AnnotatedError, Argument, Chunk, ConcreteValue,
|
||||
DustError, Instruction, NativeFunctionError, Operation, Span, Value, ValueError, ValueRef,
|
||||
compile, instruction::*, AbstractValue, AnnotatedError, Chunk, ConcreteValue, DustError,
|
||||
Instruction, NativeFunctionError, Operation, Span, Value, ValueError, ValueRef,
|
||||
};
|
||||
|
||||
pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
|
||||
@ -24,7 +23,7 @@ pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
|
||||
/// See the [module-level documentation](index.html) for more information.
|
||||
#[derive(Debug)]
|
||||
pub struct Vm<'a> {
|
||||
stack: Slab<Register>,
|
||||
stack: Vec<Register>,
|
||||
|
||||
chunk: &'a Chunk,
|
||||
parent: Option<&'a Vm<'a>>,
|
||||
@ -36,11 +35,7 @@ pub struct Vm<'a> {
|
||||
|
||||
impl<'a> Vm<'a> {
|
||||
pub fn new(source: &'a str, chunk: &'a Chunk, parent: Option<&'a Vm<'a>>) -> Self {
|
||||
let mut stack = Slab::with_capacity(chunk.stack_size());
|
||||
|
||||
for _ in 0..chunk.stack_size() {
|
||||
stack.insert(Register::Empty);
|
||||
}
|
||||
let stack = vec![Register::Empty; chunk.stack_size()];
|
||||
|
||||
Self {
|
||||
chunk,
|
||||
@ -70,6 +65,15 @@ impl<'a> Vm<'a> {
|
||||
pub fn run(&mut self) -> Result<Option<ConcreteValue>, VmError> {
|
||||
loop {
|
||||
let instruction = self.read();
|
||||
let InstructionData {
|
||||
operation,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
b_is_constant,
|
||||
c_is_constant,
|
||||
d,
|
||||
} = instruction.decode();
|
||||
|
||||
log::info!(
|
||||
"{} | {} | {} | {}",
|
||||
@ -79,7 +83,7 @@ impl<'a> Vm<'a> {
|
||||
instruction.disassembly_info()
|
||||
);
|
||||
|
||||
match instruction.operation() {
|
||||
match operation {
|
||||
Operation::Move => {
|
||||
let Move { from, to } = Move::from(&instruction);
|
||||
let from_register_has_value = self
|
||||
@ -121,16 +125,11 @@ impl<'a> Vm<'a> {
|
||||
}
|
||||
}
|
||||
Operation::LoadConstant => {
|
||||
let LoadConstant {
|
||||
destination,
|
||||
constant_index,
|
||||
jump_next,
|
||||
} = LoadConstant::from(&instruction);
|
||||
let register = Register::Pointer(Pointer::Constant(constant_index));
|
||||
let register = Register::Pointer(Pointer::Constant(b));
|
||||
|
||||
self.set_register(destination, register)?;
|
||||
self.set_register(a, register)?;
|
||||
|
||||
if jump_next {
|
||||
if c != 0 {
|
||||
self.jump(1, true);
|
||||
}
|
||||
}
|
||||
@ -187,13 +186,16 @@ impl<'a> Vm<'a> {
|
||||
self.set_register(local_register_index, register)?;
|
||||
}
|
||||
Operation::Add => {
|
||||
let Add {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = Add::from(&instruction);
|
||||
let left = self.get_argument(left)?;
|
||||
let right = self.get_argument(right)?;
|
||||
let left = if b_is_constant {
|
||||
self.get_constant(b).to_value_ref()
|
||||
} else {
|
||||
self.open_register(b)?
|
||||
};
|
||||
let right = if c_is_constant {
|
||||
self.get_constant(c).to_value_ref()
|
||||
} else {
|
||||
self.open_register(c)?
|
||||
};
|
||||
let sum_result = left.add(right);
|
||||
let sum = match sum_result {
|
||||
Ok(sum) => sum,
|
||||
@ -205,16 +207,11 @@ impl<'a> Vm<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
self.set_register(destination, Register::Value(sum))?;
|
||||
self.set_register(a, Register::Value(sum))?;
|
||||
}
|
||||
Operation::Subtract => {
|
||||
let Subtract {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = Subtract::from(&instruction);
|
||||
let left = self.get_argument(left)?;
|
||||
let right = self.get_argument(right)?;
|
||||
let left = self.get_argument(b, b_is_constant)?;
|
||||
let right = self.get_argument(c, c_is_constant)?;
|
||||
let subtraction_result = left.subtract(right);
|
||||
let difference = match subtraction_result {
|
||||
Ok(difference) => difference,
|
||||
@ -226,16 +223,11 @@ impl<'a> Vm<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
self.set_register(destination, Register::Value(difference))?;
|
||||
self.set_register(a, Register::Value(difference))?;
|
||||
}
|
||||
Operation::Multiply => {
|
||||
let Multiply {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = Multiply::from(&instruction);
|
||||
let left = self.get_argument(left)?;
|
||||
let right = self.get_argument(right)?;
|
||||
let left = self.get_argument(b, b_is_constant)?;
|
||||
let right = self.get_argument(c, c_is_constant)?;
|
||||
let multiplication_result = left.multiply(right);
|
||||
let product = match multiplication_result {
|
||||
Ok(product) => product,
|
||||
@ -247,7 +239,7 @@ impl<'a> Vm<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
self.set_register(destination, Register::Value(product))?;
|
||||
self.set_register(a, Register::Value(product))?;
|
||||
}
|
||||
Operation::Divide => {
|
||||
let Divide {
|
||||
@ -255,8 +247,8 @@ impl<'a> Vm<'a> {
|
||||
left,
|
||||
right,
|
||||
} = Divide::from(&instruction);
|
||||
let left = self.get_argument(left)?;
|
||||
let right = self.get_argument(right)?;
|
||||
let left = self.get_argument(b, b_is_constant)?;
|
||||
let right = self.get_argument(c, c_is_constant)?;
|
||||
let division_result = left.divide(right);
|
||||
let quotient = match division_result {
|
||||
Ok(quotient) => quotient,
|
||||
@ -276,8 +268,8 @@ impl<'a> Vm<'a> {
|
||||
left,
|
||||
right,
|
||||
} = Modulo::from(&instruction);
|
||||
let left = self.get_argument(left)?;
|
||||
let right = self.get_argument(right)?;
|
||||
let left = self.get_argument(b, b_is_constant)?;
|
||||
let right = self.get_argument(c, c_is_constant)?;
|
||||
let modulo_result = left.modulo(right);
|
||||
let remainder = match modulo_result {
|
||||
Ok(remainder) => remainder,
|
||||
@ -292,11 +284,11 @@ impl<'a> Vm<'a> {
|
||||
self.set_register(destination, Register::Value(remainder))?;
|
||||
}
|
||||
Operation::Test => {
|
||||
let Test {
|
||||
argument,
|
||||
test_value,
|
||||
} = Test::from(&instruction);
|
||||
let value = self.get_argument(argument)?;
|
||||
let value = if b_is_constant {
|
||||
self.get_constant(b).to_value_ref()
|
||||
} else {
|
||||
self.open_register(b)?
|
||||
};
|
||||
let boolean = if let ValueRef::Concrete(ConcreteValue::Boolean(boolean)) = value
|
||||
{
|
||||
*boolean
|
||||
@ -307,17 +299,12 @@ impl<'a> Vm<'a> {
|
||||
});
|
||||
};
|
||||
|
||||
if boolean == test_value {
|
||||
if boolean == (c != 0) {
|
||||
self.jump(1, true);
|
||||
}
|
||||
}
|
||||
Operation::TestSet => {
|
||||
let TestSet {
|
||||
destination,
|
||||
argument,
|
||||
test_value,
|
||||
} = TestSet::from(&instruction);
|
||||
let value = self.get_argument(argument)?;
|
||||
let value = self.get_argument(b, b_is_constant)?;
|
||||
let boolean = if let ValueRef::Concrete(ConcreteValue::Boolean(boolean)) = value
|
||||
{
|
||||
*boolean
|
||||
@ -327,28 +314,24 @@ impl<'a> Vm<'a> {
|
||||
position: self.current_position(),
|
||||
});
|
||||
};
|
||||
let test_value = c != 0;
|
||||
|
||||
if boolean == test_value {
|
||||
self.jump(1, true);
|
||||
} else {
|
||||
let pointer = match argument {
|
||||
Argument::Constant(constant_index) => Pointer::Constant(constant_index),
|
||||
Argument::Register(register_index) => Pointer::Stack(register_index),
|
||||
let pointer = if b_is_constant {
|
||||
Pointer::Constant(b)
|
||||
} else {
|
||||
Pointer::Stack(b)
|
||||
};
|
||||
let register = Register::Pointer(pointer);
|
||||
|
||||
self.set_register(destination, register)?;
|
||||
self.set_register(a, register)?;
|
||||
}
|
||||
}
|
||||
Operation::Equal => {
|
||||
let Equal {
|
||||
destination,
|
||||
value,
|
||||
left,
|
||||
right,
|
||||
} = Equal::from(&instruction);
|
||||
let left = self.get_argument(left)?;
|
||||
let right = self.get_argument(right)?;
|
||||
let left = self.get_argument(b, b_is_constant)?;
|
||||
let right = self.get_argument(c, c_is_constant)?;
|
||||
let equal_result = left.equal(right).map_err(|error| VmError::Value {
|
||||
error,
|
||||
position: self.current_position(),
|
||||
@ -362,21 +345,23 @@ impl<'a> Vm<'a> {
|
||||
position: self.current_position(),
|
||||
});
|
||||
};
|
||||
let comparison = is_equal == value;
|
||||
let comparison = is_equal == d;
|
||||
let register =
|
||||
Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
|
||||
|
||||
self.set_register(destination, register)?;
|
||||
self.set_register(a, register)?;
|
||||
}
|
||||
Operation::Less => {
|
||||
let Less {
|
||||
destination,
|
||||
value,
|
||||
left,
|
||||
right,
|
||||
} = Less::from(&instruction);
|
||||
let left = self.get_argument(left)?;
|
||||
let right = self.get_argument(right)?;
|
||||
let left = if b_is_constant {
|
||||
self.get_constant(b).to_value_ref()
|
||||
} else {
|
||||
self.open_register(b)?
|
||||
};
|
||||
let right = if c_is_constant {
|
||||
self.get_constant(c).to_value_ref()
|
||||
} else {
|
||||
self.open_register(c)?
|
||||
};
|
||||
let less_result = left.less_than(right);
|
||||
let less_than_value = match less_result {
|
||||
Ok(value) => value,
|
||||
@ -396,21 +381,23 @@ impl<'a> Vm<'a> {
|
||||
});
|
||||
}
|
||||
};
|
||||
let comparison = is_less_than == value;
|
||||
let comparison = is_less_than == d;
|
||||
let register =
|
||||
Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
|
||||
|
||||
self.set_register(destination, register)?;
|
||||
self.set_register(a, register)?;
|
||||
}
|
||||
Operation::LessEqual => {
|
||||
let LessEqual {
|
||||
destination,
|
||||
value,
|
||||
left,
|
||||
right,
|
||||
} = LessEqual::from(&instruction);
|
||||
let left = self.get_argument(left)?;
|
||||
let right = self.get_argument(right)?;
|
||||
let left = if b_is_constant {
|
||||
self.get_constant(b).to_value_ref()
|
||||
} else {
|
||||
self.open_register(b)?
|
||||
};
|
||||
let right = if c_is_constant {
|
||||
self.get_constant(c).to_value_ref()
|
||||
} else {
|
||||
self.open_register(c)?
|
||||
};
|
||||
let less_or_equal_result = left.less_than_or_equal(right);
|
||||
let less_or_equal_value = match less_or_equal_result {
|
||||
Ok(value) => value,
|
||||
@ -430,55 +417,37 @@ impl<'a> Vm<'a> {
|
||||
});
|
||||
}
|
||||
};
|
||||
let comparison = is_less_than_or_equal == value;
|
||||
let comparison = is_less_than_or_equal == d;
|
||||
let register =
|
||||
Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
|
||||
|
||||
self.set_register(destination, register)?;
|
||||
self.set_register(a, register)?;
|
||||
}
|
||||
Operation::Negate => {
|
||||
let Negate {
|
||||
destination,
|
||||
argument,
|
||||
} = Negate::from(&instruction);
|
||||
let value = self.get_argument(argument)?;
|
||||
let value = self.get_argument(b, b_is_constant)?;
|
||||
let negated = value.negate().map_err(|error| VmError::Value {
|
||||
error,
|
||||
position: self.current_position(),
|
||||
})?;
|
||||
let register = Register::Value(negated);
|
||||
|
||||
self.set_register(destination, register)?;
|
||||
self.set_register(a, register)?;
|
||||
}
|
||||
Operation::Not => {
|
||||
let Not {
|
||||
destination,
|
||||
argument,
|
||||
} = Not::from(&instruction);
|
||||
let value = self.get_argument(argument)?;
|
||||
let value = self.get_argument(b, b_is_constant)?;
|
||||
let not = value.not().map_err(|error| VmError::Value {
|
||||
error,
|
||||
position: self.current_position(),
|
||||
})?;
|
||||
let register = Register::Value(not);
|
||||
|
||||
self.set_register(destination, register)?;
|
||||
self.set_register(a, register)?;
|
||||
}
|
||||
Operation::Jump => {
|
||||
let Jump {
|
||||
offset,
|
||||
is_positive,
|
||||
} = Jump::from(&instruction);
|
||||
|
||||
self.jump(offset as usize, is_positive);
|
||||
self.jump(b as usize, c != 0);
|
||||
}
|
||||
Operation::Call => {
|
||||
let Call {
|
||||
destination,
|
||||
function,
|
||||
argument_count,
|
||||
} = Call::from(&instruction);
|
||||
let function = self.get_argument(function)?;
|
||||
let function = self.get_argument(b, b_is_constant)?;
|
||||
let chunk = if let ValueRef::Concrete(ConcreteValue::Function(chunk)) = function
|
||||
{
|
||||
chunk
|
||||
@ -491,15 +460,25 @@ impl<'a> Vm<'a> {
|
||||
});
|
||||
};
|
||||
let mut function_vm = Vm::new(self.source, chunk, Some(self));
|
||||
let first_argument_index = destination - argument_count;
|
||||
let first_argument_index = a - c;
|
||||
let mut argument_index = 0;
|
||||
|
||||
for argument_register_index in first_argument_index..a {
|
||||
let target_register_is_empty = matches!(
|
||||
self.stack[argument_register_index as usize],
|
||||
Register::Empty
|
||||
);
|
||||
|
||||
if target_register_is_empty {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (argument_index, argument_register_index) in
|
||||
(first_argument_index..destination).enumerate()
|
||||
{
|
||||
function_vm.set_register(
|
||||
argument_index as u8,
|
||||
Register::Pointer(Pointer::ParentStack(argument_register_index)),
|
||||
)?;
|
||||
|
||||
argument_index += 1;
|
||||
}
|
||||
|
||||
let return_value = function_vm.run()?;
|
||||
@ -507,7 +486,7 @@ impl<'a> Vm<'a> {
|
||||
if let Some(concrete_value) = return_value {
|
||||
let register = Register::Value(concrete_value.to_value());
|
||||
|
||||
self.set_register(destination, register)?;
|
||||
self.set_register(a, register)?;
|
||||
}
|
||||
}
|
||||
Operation::CallNative => {
|
||||
@ -645,17 +624,15 @@ impl<'a> Vm<'a> {
|
||||
|
||||
fn open_register(&self, register_index: u8) -> Result<ValueRef, VmError> {
|
||||
let register_index = register_index as usize;
|
||||
let stack = self.stack.as_slice();
|
||||
|
||||
if register_index < self.stack.len() {
|
||||
let register = &self.stack[register_index];
|
||||
if register_index < stack.len() {
|
||||
let register = &stack[register_index];
|
||||
|
||||
return match register {
|
||||
Register::Value(value) => Ok(value.to_ref()),
|
||||
Register::Pointer(pointer) => self.follow_pointer(*pointer),
|
||||
Register::Empty => Err(VmError::EmptyRegister {
|
||||
index: register_index,
|
||||
position: self.current_position(),
|
||||
}),
|
||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
||||
};
|
||||
}
|
||||
|
||||
@ -700,23 +677,21 @@ impl<'a> Vm<'a> {
|
||||
}
|
||||
|
||||
/// DRY helper to get a value from an Argument
|
||||
fn get_argument(&self, argument: Argument) -> Result<ValueRef, VmError> {
|
||||
let value_ref = match argument {
|
||||
Argument::Constant(constant_index) => {
|
||||
ValueRef::Concrete(self.get_constant(constant_index))
|
||||
fn get_argument(&self, index: u8, is_constant: bool) -> Result<ValueRef, VmError> {
|
||||
if is_constant {
|
||||
Ok(self.get_constant(index).to_value_ref())
|
||||
} else {
|
||||
Ok(self.open_register(index)?)
|
||||
}
|
||||
Argument::Register(register_index) => self.open_register(register_index)?,
|
||||
};
|
||||
|
||||
Ok(value_ref)
|
||||
}
|
||||
|
||||
fn set_register(&mut self, to_register: u8, register: Register) -> Result<(), VmError> {
|
||||
self.last_assigned_register = Some(to_register);
|
||||
let to_register = to_register as usize;
|
||||
let stack = self.stack.as_mut_slice();
|
||||
|
||||
if to_register < self.stack.len() {
|
||||
self.stack[to_register] = register;
|
||||
if to_register < stack.len() {
|
||||
stack[to_register] = register;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
@ -899,7 +874,27 @@ impl AnnotatedError for VmError {
|
||||
}
|
||||
|
||||
fn detail_snippets(&self) -> SmallVec<[(String, Span); 2]> {
|
||||
todo!()
|
||||
match self {
|
||||
VmError::StackOverflow { position } => todo!(),
|
||||
VmError::StackUnderflow { position } => todo!(),
|
||||
VmError::EmptyRegister { index, position } => todo!(),
|
||||
VmError::ExpectedConcreteValue { found, position } => todo!(),
|
||||
VmError::ExpectedValue { found, position } => todo!(),
|
||||
VmError::RegisterIndexOutOfBounds { index, position } => todo!(),
|
||||
VmError::UndefinedLocal {
|
||||
local_index,
|
||||
position,
|
||||
} => todo!(),
|
||||
VmError::ExpectedBoolean { found, position } => todo!(),
|
||||
VmError::ExpectedFunction { found, position } => todo!(),
|
||||
VmError::ExpectedParent { position } => todo!(),
|
||||
VmError::ValueDisplay { error, position } => todo!(),
|
||||
VmError::ConstantIndexOutOfBounds { index, position } => todo!(),
|
||||
VmError::InstructionIndexOutOfBounds { index, position } => todo!(),
|
||||
VmError::LocalIndexOutOfBounds { index, position } => todo!(),
|
||||
VmError::NativeFunction(native_function_error) => todo!(),
|
||||
VmError::Value { error, position } => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn help_snippets(&self) -> SmallVec<[(String, Span); 2]> {
|
||||
|
Loading…
Reference in New Issue
Block a user