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