Consolidate local operations to point operations
This commit is contained in:
parent
c1fe54ccd5
commit
371a061b1c
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -349,6 +349,7 @@ dependencies = [
|
|||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"smallvec",
|
||||||
"smartstring",
|
"smartstring",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
@ -805,6 +806,9 @@ name = "smallvec"
|
|||||||
version = "1.13.2"
|
version = "1.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smartstring"
|
name = "smartstring"
|
||||||
|
@ -22,6 +22,7 @@ smartstring = { version = "1.0.1", features = [
|
|||||||
], default-features = false }
|
], default-features = false }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
crossbeam-channel = "0.5.14"
|
crossbeam-channel = "0.5.14"
|
||||||
|
smallvec = { version = "1.13.2", features = ["serde", "const_generics"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.3.4", features = ["html_reports"] }
|
criterion = { version = "0.3.4", features = ["html_reports"] }
|
||||||
|
@ -35,7 +35,7 @@ use optimize::control_flow_register_consolidation;
|
|||||||
use crate::{
|
use crate::{
|
||||||
Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local,
|
Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local,
|
||||||
NativeFunction, Operand, Operation, Scope, Span, Token, TokenKind, Type, Value,
|
NativeFunction, Operand, Operation, Scope, Span, Token, TokenKind, Type, Value,
|
||||||
instruction::{CallNative, Close, GetLocal, Jump, LoadList, Return, SetLocal, TypeCode},
|
instruction::{CallNative, Close, Jump, LoadList, Point, Return, TypeCode},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Compiles the input and returns a chunk.
|
/// Compiles the input and returns a chunk.
|
||||||
@ -697,12 +697,7 @@ impl<'src> Compiler<'src> {
|
|||||||
) -> Result<(Operand, bool), CompileError> {
|
) -> Result<(Operand, bool), CompileError> {
|
||||||
let (argument, push_back) = match instruction.operation() {
|
let (argument, push_back) = match instruction.operation() {
|
||||||
Operation::LOAD_CONSTANT => (Operand::Constant(instruction.b_field()), false),
|
Operation::LOAD_CONSTANT => (Operand::Constant(instruction.b_field()), false),
|
||||||
Operation::GET_LOCAL => {
|
Operation::POINT => (Operand::Register(instruction.a_field()), false),
|
||||||
let local_index = instruction.b_field();
|
|
||||||
let (local, _) = self.get_local(local_index)?;
|
|
||||||
|
|
||||||
(Operand::Register(local.register_index), false)
|
|
||||||
}
|
|
||||||
Operation::LOAD_BOOLEAN
|
Operation::LOAD_BOOLEAN
|
||||||
| Operation::LOAD_LIST
|
| Operation::LOAD_LIST
|
||||||
| Operation::LOAD_SELF
|
| Operation::LOAD_SELF
|
||||||
@ -749,11 +744,11 @@ impl<'src> Compiler<'src> {
|
|||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
})?;
|
})?;
|
||||||
let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?;
|
let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?;
|
||||||
let left_is_mutable_local = if let Operation::GET_LOCAL = left_instruction.operation() {
|
let left_is_mutable_local = if left_instruction.operation() == Operation::POINT {
|
||||||
let GetLocal { local_index, .. } = GetLocal::from(left_instruction);
|
let Point { to, .. } = Point::from(left_instruction);
|
||||||
|
|
||||||
self.locals
|
self.locals
|
||||||
.get(local_index as usize)
|
.get(to.index() as usize)
|
||||||
.map(|(local, _)| local.is_mutable)
|
.map(|(local, _)| local.is_mutable)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
} else {
|
} else {
|
||||||
@ -1008,8 +1003,9 @@ impl<'src> Compiler<'src> {
|
|||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
})?;
|
})?;
|
||||||
let operand_register = if last_instruction.operation() == Operation::GET_LOCAL {
|
let operand_register = if last_instruction.operation() == Operation::POINT {
|
||||||
let (local, _) = self.get_local(last_instruction.b_field())?;
|
let Point { to, .. } = Point::from(last_instruction);
|
||||||
|
let (local, _) = self.get_local(to.index())?;
|
||||||
|
|
||||||
local.register_index
|
local.register_index
|
||||||
} else if last_instruction.yields_value() {
|
} else if last_instruction.yields_value() {
|
||||||
@ -1138,24 +1134,18 @@ impl<'src> Compiler<'src> {
|
|||||||
math_instruction.set_a_field(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 point = Instruction::point(local_register_index, Operand::Register(register));
|
||||||
register_index: register,
|
|
||||||
local_index,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.emit_instruction(set_local, Type::None, start_position);
|
self.emit_instruction(point, r#type, start_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let destination = self.next_register();
|
let destination = self.next_register();
|
||||||
let get_local = Instruction::from(GetLocal {
|
let point = Instruction::point(destination, Operand::Register(local_register_index));
|
||||||
destination,
|
|
||||||
local_index,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.emit_instruction(get_local, r#type, self.previous_position);
|
self.emit_instruction(point, r#type, self.previous_position);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use crate::{Instruction, Operation};
|
|
||||||
|
|
||||||
use super::InstructionBuilder;
|
|
||||||
|
|
||||||
pub struct GetLocal {
|
|
||||||
pub destination: u16,
|
|
||||||
pub local_index: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Instruction> for GetLocal {
|
|
||||||
fn from(instruction: Instruction) -> Self {
|
|
||||||
let destination = instruction.a_field();
|
|
||||||
let local_index = instruction.b_field();
|
|
||||||
|
|
||||||
GetLocal {
|
|
||||||
destination,
|
|
||||||
local_index,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<GetLocal> for Instruction {
|
|
||||||
fn from(get_local: GetLocal) -> Self {
|
|
||||||
let operation = Operation::GET_LOCAL;
|
|
||||||
let a_field = get_local.destination;
|
|
||||||
let b_field = get_local.local_index;
|
|
||||||
|
|
||||||
InstructionBuilder {
|
|
||||||
operation,
|
|
||||||
a_field,
|
|
||||||
b_field,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for GetLocal {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
let GetLocal {
|
|
||||||
destination,
|
|
||||||
local_index,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
write!(f, "R{destination} = L{local_index}")
|
|
||||||
}
|
|
||||||
}
|
|
@ -99,7 +99,6 @@ mod call_native;
|
|||||||
mod close;
|
mod close;
|
||||||
mod divide;
|
mod divide;
|
||||||
mod equal;
|
mod equal;
|
||||||
mod get_local;
|
|
||||||
mod jump;
|
mod jump;
|
||||||
mod less;
|
mod less;
|
||||||
mod less_equal;
|
mod less_equal;
|
||||||
@ -115,7 +114,6 @@ mod not;
|
|||||||
mod operation;
|
mod operation;
|
||||||
mod point;
|
mod point;
|
||||||
mod r#return;
|
mod r#return;
|
||||||
mod set_local;
|
|
||||||
mod subtract;
|
mod subtract;
|
||||||
mod test;
|
mod test;
|
||||||
mod test_set;
|
mod test_set;
|
||||||
@ -127,7 +125,6 @@ pub use call_native::CallNative;
|
|||||||
pub use close::Close;
|
pub use close::Close;
|
||||||
pub use divide::Divide;
|
pub use divide::Divide;
|
||||||
pub use equal::Equal;
|
pub use equal::Equal;
|
||||||
pub use get_local::GetLocal;
|
|
||||||
pub use jump::Jump;
|
pub use jump::Jump;
|
||||||
pub use less::Less;
|
pub use less::Less;
|
||||||
pub use less_equal::LessEqual;
|
pub use less_equal::LessEqual;
|
||||||
@ -143,7 +140,6 @@ pub use not::Not;
|
|||||||
pub use operation::Operation;
|
pub use operation::Operation;
|
||||||
pub use point::Point;
|
pub use point::Point;
|
||||||
pub use r#return::Return;
|
pub use r#return::Return;
|
||||||
pub use set_local::SetLocal;
|
|
||||||
pub use subtract::Subtract;
|
pub use subtract::Subtract;
|
||||||
pub use test::Test;
|
pub use test::Test;
|
||||||
pub use test_set::TestSet;
|
pub use test_set::TestSet;
|
||||||
@ -276,8 +272,8 @@ impl Instruction {
|
|||||||
self.0 = (bits as u64) << 63;
|
self.0 = (bits as u64) << 63;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn point(from: u16, to: u16) -> Instruction {
|
pub fn point(destination: u16, to: Operand) -> Instruction {
|
||||||
Instruction::from(Point { from, to })
|
Instruction::from(Point { destination, to })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(from: u16, to: u16) -> Instruction {
|
pub fn close(from: u16, to: u16) -> Instruction {
|
||||||
@ -323,20 +319,6 @@ impl Instruction {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local(destination: u16, local_index: u16) -> Instruction {
|
|
||||||
Instruction::from(GetLocal {
|
|
||||||
destination,
|
|
||||||
local_index,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_local(register: u16, local_index: u16) -> Instruction {
|
|
||||||
Instruction::from(SetLocal {
|
|
||||||
local_index,
|
|
||||||
register_index: register,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(
|
pub fn add(
|
||||||
destination: u16,
|
destination: u16,
|
||||||
left: Operand,
|
left: Operand,
|
||||||
@ -546,10 +528,10 @@ impl Instruction {
|
|||||||
pub fn as_argument(&self) -> Option<Operand> {
|
pub fn as_argument(&self) -> Option<Operand> {
|
||||||
match self.operation() {
|
match self.operation() {
|
||||||
Operation::LOAD_CONSTANT => Some(Operand::Constant(self.b_field())),
|
Operation::LOAD_CONSTANT => Some(Operand::Constant(self.b_field())),
|
||||||
Operation::LOAD_BOOLEAN
|
Operation::POINT
|
||||||
|
| Operation::LOAD_BOOLEAN
|
||||||
| Operation::LOAD_LIST
|
| Operation::LOAD_LIST
|
||||||
| Operation::LOAD_SELF
|
| Operation::LOAD_SELF
|
||||||
| Operation::GET_LOCAL
|
|
||||||
| Operation::ADD
|
| Operation::ADD
|
||||||
| Operation::SUBTRACT
|
| Operation::SUBTRACT
|
||||||
| Operation::MULTIPLY
|
| Operation::MULTIPLY
|
||||||
@ -574,7 +556,7 @@ impl Instruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn b_as_argument(&self) -> Operand {
|
pub fn b_as_operand(&self) -> Operand {
|
||||||
if self.b_is_constant() {
|
if self.b_is_constant() {
|
||||||
Operand::Constant(self.b_field())
|
Operand::Constant(self.b_field())
|
||||||
} else {
|
} else {
|
||||||
@ -605,7 +587,6 @@ impl Instruction {
|
|||||||
| Operation::LOAD_FUNCTION
|
| Operation::LOAD_FUNCTION
|
||||||
| Operation::LOAD_LIST
|
| Operation::LOAD_LIST
|
||||||
| Operation::LOAD_SELF
|
| Operation::LOAD_SELF
|
||||||
| Operation::GET_LOCAL
|
|
||||||
| Operation::ADD
|
| Operation::ADD
|
||||||
| Operation::SUBTRACT
|
| Operation::SUBTRACT
|
||||||
| Operation::MULTIPLY
|
| Operation::MULTIPLY
|
||||||
@ -619,9 +600,7 @@ impl Instruction {
|
|||||||
|
|
||||||
function.returns_value()
|
function.returns_value()
|
||||||
}
|
}
|
||||||
Operation::CLOSE
|
Operation::EQUAL
|
||||||
| Operation::SET_LOCAL
|
|
||||||
| Operation::EQUAL
|
|
||||||
| Operation::LESS
|
| Operation::LESS
|
||||||
| Operation::LESS_EQUAL
|
| Operation::LESS_EQUAL
|
||||||
| Operation::TEST
|
| Operation::TEST
|
||||||
@ -637,14 +616,11 @@ impl Instruction {
|
|||||||
|
|
||||||
match operation {
|
match operation {
|
||||||
Operation::POINT => Point::from(*self).to_string(),
|
Operation::POINT => Point::from(*self).to_string(),
|
||||||
Operation::CLOSE => Close::from(*self).to_string(),
|
|
||||||
Operation::LOAD_BOOLEAN => LoadBoolean::from(*self).to_string(),
|
Operation::LOAD_BOOLEAN => LoadBoolean::from(*self).to_string(),
|
||||||
Operation::LOAD_CONSTANT => LoadConstant::from(*self).to_string(),
|
Operation::LOAD_CONSTANT => LoadConstant::from(*self).to_string(),
|
||||||
Operation::LOAD_FUNCTION => LoadFunction::from(*self).to_string(),
|
Operation::LOAD_FUNCTION => LoadFunction::from(*self).to_string(),
|
||||||
Operation::LOAD_LIST => LoadList::from(*self).to_string(),
|
Operation::LOAD_LIST => LoadList::from(*self).to_string(),
|
||||||
Operation::LOAD_SELF => LoadSelf::from(*self).to_string(),
|
Operation::LOAD_SELF => LoadSelf::from(*self).to_string(),
|
||||||
Operation::GET_LOCAL => GetLocal::from(*self).to_string(),
|
|
||||||
Operation::SET_LOCAL => SetLocal::from(*self).to_string(),
|
|
||||||
Operation::ADD => Add::from(*self).to_string(),
|
Operation::ADD => Add::from(*self).to_string(),
|
||||||
Operation::SUBTRACT => Subtract::from(*self).to_string(),
|
Operation::SUBTRACT => Subtract::from(*self).to_string(),
|
||||||
Operation::MULTIPLY => Multiply::from(*self).to_string(),
|
Operation::MULTIPLY => Multiply::from(*self).to_string(),
|
||||||
|
@ -11,7 +11,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_field();
|
let destination = instruction.a_field();
|
||||||
let argument = instruction.b_as_argument();
|
let argument = instruction.b_as_operand();
|
||||||
let argument_type = instruction.b_type();
|
let argument_type = instruction.b_type();
|
||||||
|
|
||||||
Negate {
|
Negate {
|
||||||
|
@ -12,7 +12,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_field();
|
let destination = instruction.a_field();
|
||||||
let argument = instruction.b_as_argument();
|
let argument = instruction.b_as_operand();
|
||||||
|
|
||||||
Not {
|
Not {
|
||||||
destination,
|
destination,
|
||||||
|
@ -20,51 +20,44 @@ impl Operation {
|
|||||||
pub const LOAD_LIST: Operation = Operation(5);
|
pub const LOAD_LIST: Operation = Operation(5);
|
||||||
pub const LOAD_SELF: Operation = Operation(6);
|
pub const LOAD_SELF: Operation = Operation(6);
|
||||||
|
|
||||||
// Locals
|
|
||||||
pub const GET_LOCAL: Operation = Operation(7);
|
|
||||||
pub const SET_LOCAL: Operation = Operation(8);
|
|
||||||
|
|
||||||
// Arithmetic
|
// Arithmetic
|
||||||
pub const ADD: Operation = Operation(9);
|
pub const ADD: Operation = Operation(7);
|
||||||
pub const SUBTRACT: Operation = Operation(10);
|
pub const SUBTRACT: Operation = Operation(8);
|
||||||
pub const MULTIPLY: Operation = Operation(11);
|
pub const MULTIPLY: Operation = Operation(9);
|
||||||
pub const DIVIDE: Operation = Operation(12);
|
pub const DIVIDE: Operation = Operation(10);
|
||||||
pub const MODULO: Operation = Operation(13);
|
pub const MODULO: Operation = Operation(11);
|
||||||
|
|
||||||
// Comparison
|
// Comparison
|
||||||
pub const EQUAL: Operation = Operation(14);
|
pub const EQUAL: Operation = Operation(12);
|
||||||
pub const LESS: Operation = Operation(15);
|
pub const LESS: Operation = Operation(13);
|
||||||
pub const LESS_EQUAL: Operation = Operation(16);
|
pub const LESS_EQUAL: Operation = Operation(14);
|
||||||
|
|
||||||
// Unary operations
|
// Unary operations
|
||||||
pub const NEGATE: Operation = Operation(17);
|
pub const NEGATE: Operation = Operation(15);
|
||||||
pub const NOT: Operation = Operation(18);
|
pub const NOT: Operation = Operation(16);
|
||||||
|
|
||||||
// Logical operations
|
// Logical operations
|
||||||
pub const TEST: Operation = Operation(19);
|
pub const TEST: Operation = Operation(17);
|
||||||
pub const TEST_SET: Operation = Operation(20);
|
pub const TEST_SET: Operation = Operation(18);
|
||||||
|
|
||||||
// Function calls
|
// Function calls
|
||||||
pub const CALL: Operation = Operation(21);
|
pub const CALL: Operation = Operation(19);
|
||||||
pub const CALL_NATIVE: Operation = Operation(22);
|
pub const CALL_NATIVE: Operation = Operation(20);
|
||||||
|
|
||||||
// Control flow
|
// Control flow
|
||||||
pub const JUMP: Operation = Operation(23);
|
pub const JUMP: Operation = Operation(21);
|
||||||
pub const RETURN: Operation = Operation(24);
|
pub const RETURN: Operation = Operation(22);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Operation {
|
impl Operation {
|
||||||
pub fn name(&self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
match *self {
|
match *self {
|
||||||
Self::POINT => "POINT",
|
Self::POINT => "POINT",
|
||||||
Self::CLOSE => "CLOSE",
|
|
||||||
Self::LOAD_BOOLEAN => "LOAD_BOOLEAN",
|
Self::LOAD_BOOLEAN => "LOAD_BOOLEAN",
|
||||||
Self::LOAD_CONSTANT => "LOAD_CONSTANT",
|
Self::LOAD_CONSTANT => "LOAD_CONSTANT",
|
||||||
Self::LOAD_FUNCTION => "LOAD_FUNCTION",
|
Self::LOAD_FUNCTION => "LOAD_FUNCTION",
|
||||||
Self::LOAD_LIST => "LOAD_LIST",
|
Self::LOAD_LIST => "LOAD_LIST",
|
||||||
Self::LOAD_SELF => "LOAD_SELF",
|
Self::LOAD_SELF => "LOAD_SELF",
|
||||||
Self::GET_LOCAL => "GET_LOCAL",
|
|
||||||
Self::SET_LOCAL => "SET_LOCAL",
|
|
||||||
Self::ADD => "ADD",
|
Self::ADD => "ADD",
|
||||||
Self::SUBTRACT => "SUBTRACT",
|
Self::SUBTRACT => "SUBTRACT",
|
||||||
Self::MULTIPLY => "MULTIPLY",
|
Self::MULTIPLY => "MULTIPLY",
|
||||||
|
@ -2,18 +2,18 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
use super::InstructionBuilder;
|
use super::{InstructionBuilder, Operand};
|
||||||
|
|
||||||
pub struct Point {
|
pub struct Point {
|
||||||
pub from: u16,
|
pub destination: u16,
|
||||||
pub to: u16,
|
pub to: Operand,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Point {
|
impl From<Instruction> for Point {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
Point {
|
Point {
|
||||||
from: instruction.b_field(),
|
destination: instruction.a_field(),
|
||||||
to: instruction.c_field(),
|
to: instruction.b_as_operand(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -21,13 +21,14 @@ impl From<Instruction> for Point {
|
|||||||
impl From<Point> for Instruction {
|
impl From<Point> for Instruction {
|
||||||
fn from(r#move: Point) -> Self {
|
fn from(r#move: Point) -> Self {
|
||||||
let operation = Operation::POINT;
|
let operation = Operation::POINT;
|
||||||
let b_field = r#move.from;
|
let a_field = r#move.destination;
|
||||||
let c_field = r#move.to;
|
let (b_field, b_is_constant) = r#move.to.as_index_and_constant_flag();
|
||||||
|
|
||||||
InstructionBuilder {
|
InstructionBuilder {
|
||||||
operation,
|
operation,
|
||||||
|
a_field,
|
||||||
b_field,
|
b_field,
|
||||||
c_field,
|
b_is_constant,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
@ -36,8 +37,8 @@ impl From<Point> for Instruction {
|
|||||||
|
|
||||||
impl Display for Point {
|
impl Display for Point {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
let Point { from, to } = self;
|
let Point { destination, to } = self;
|
||||||
|
|
||||||
write!(f, "{from} -> {to}")
|
write!(f, "R{destination} -> {to}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use crate::{Instruction, Operation};
|
|
||||||
|
|
||||||
use super::InstructionBuilder;
|
|
||||||
|
|
||||||
pub struct SetLocal {
|
|
||||||
pub register_index: u16,
|
|
||||||
pub local_index: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Instruction> for SetLocal {
|
|
||||||
fn from(instruction: Instruction) -> Self {
|
|
||||||
let register_index = instruction.b_field();
|
|
||||||
let local_index = instruction.c_field();
|
|
||||||
|
|
||||||
SetLocal {
|
|
||||||
register_index,
|
|
||||||
local_index,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SetLocal> for Instruction {
|
|
||||||
fn from(set_local: SetLocal) -> Self {
|
|
||||||
let operation = Operation::SET_LOCAL;
|
|
||||||
let b_field = set_local.register_index;
|
|
||||||
let c_field = set_local.local_index;
|
|
||||||
|
|
||||||
InstructionBuilder {
|
|
||||||
operation,
|
|
||||||
b_field,
|
|
||||||
c_field,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for SetLocal {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
let SetLocal {
|
|
||||||
register_index,
|
|
||||||
local_index,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
write!(f, "L{local_index} = R{register_index}")
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,7 +13,7 @@ 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_field();
|
let destination = instruction.a_field();
|
||||||
let argument = instruction.b_as_argument();
|
let argument = instruction.b_as_operand();
|
||||||
let test_value = instruction.c_field() != 0;
|
let test_value = instruction.c_field() != 0;
|
||||||
|
|
||||||
TestSet {
|
TestSet {
|
||||||
|
@ -1,17 +1,13 @@
|
|||||||
use std::{ops::Range, panic};
|
use std::{ops::Range, panic};
|
||||||
|
|
||||||
use crate::vm::ThreadData;
|
use crate::vm::Thread;
|
||||||
|
|
||||||
pub fn panic(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
pub fn panic(data: &mut Thread, _: usize, argument_range: Range<usize>) {
|
||||||
let position = data.current_position();
|
let position = data.current_position();
|
||||||
let mut message = format!("Dust panic at {position}!");
|
let mut message = format!("Dust panic at {position}!");
|
||||||
|
|
||||||
for register_index in argument_range {
|
for register_index in argument_range {
|
||||||
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
let value = data.get_register(register_index);
|
||||||
let value = match value_option {
|
|
||||||
Some(value) => value,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
let string = value.display(data);
|
let string = value.display(data);
|
||||||
|
|
||||||
message.push_str(&string);
|
message.push_str(&string);
|
||||||
|
@ -3,10 +3,10 @@ use std::ops::Range;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ConcreteValue, Value,
|
ConcreteValue, Value,
|
||||||
vm::{Register, ThreadData, get_next_action},
|
vm::{Register, Thread},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range<u16>) -> bool {
|
pub fn read_line(data: &mut Thread, destination: usize, _argument_range: Range<usize>) {
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
|
|
||||||
if stdin().read_line(&mut buffer).is_ok() {
|
if stdin().read_line(&mut buffer).is_ok() {
|
||||||
@ -14,45 +14,32 @@ pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range
|
|||||||
|
|
||||||
buffer.truncate(length.saturating_sub(1));
|
buffer.truncate(length.saturating_sub(1));
|
||||||
|
|
||||||
let register = Register::Value(Value::Concrete(ConcreteValue::string(buffer)));
|
let new_register = Register::Value(Value::Concrete(ConcreteValue::string(buffer)));
|
||||||
|
let old_register = data.get_register_mut(destination);
|
||||||
|
|
||||||
data.set_register(destination, register);
|
*old_register = new_register;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
pub fn write(data: &mut Thread, _: usize, argument_range: Range<usize>) {
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
|
|
||||||
for register_index in argument_range {
|
for register_index in argument_range {
|
||||||
if let Some(value) = data.open_register_allow_empty_unchecked(register_index) {
|
let value = data.get_register(register_index);
|
||||||
let string = value.display(data);
|
let _ = stdout.write(value.to_string().as_bytes());
|
||||||
let _ = stdout.write(string.as_bytes());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = stdout.flush();
|
let _ = stdout.flush();
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_line(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
pub fn write_line(data: &mut Thread, _: usize, argument_range: Range<usize>) {
|
||||||
let mut stdout = stdout().lock();
|
let mut stdout = stdout().lock();
|
||||||
|
|
||||||
for register_index in argument_range {
|
for register_index in argument_range {
|
||||||
if let Some(value) = data.open_register_allow_empty_unchecked(register_index) {
|
let value = data.get_register(register_index);
|
||||||
let string = value.display(data);
|
let _ = stdout.write(value.to_string().as_bytes());
|
||||||
let _ = stdout.write(string.as_bytes());
|
}
|
||||||
|
|
||||||
let _ = stdout.write(b"\n");
|
let _ = stdout.write(b"\n");
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = stdout.flush();
|
let _ = stdout.flush();
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ use std::{
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{AnnotatedError, FunctionType, Span, Type, vm::ThreadData};
|
use crate::{AnnotatedError, FunctionType, Span, Type, vm::Thread};
|
||||||
|
|
||||||
macro_rules! define_native_function {
|
macro_rules! define_native_function {
|
||||||
($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => {
|
($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => {
|
||||||
@ -34,13 +34,13 @@ macro_rules! define_native_function {
|
|||||||
impl NativeFunction {
|
impl NativeFunction {
|
||||||
pub fn call(
|
pub fn call(
|
||||||
&self,
|
&self,
|
||||||
data: &mut ThreadData,
|
thread: &mut Thread,
|
||||||
destination: u16,
|
destination: usize,
|
||||||
argument_range: Range<u16>,
|
argument_range: Range<usize>,
|
||||||
) -> bool {
|
) {
|
||||||
match self {
|
match self {
|
||||||
$(
|
$(
|
||||||
NativeFunction::$name => $function(data, destination, argument_range),
|
NativeFunction::$name => $function(thread, destination, argument_range),
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,10 @@ use rand::Rng;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Value,
|
Value,
|
||||||
vm::{Register, ThreadData, get_next_action},
|
vm::{Register, Thread},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
|
pub fn random_int(data: &mut Thread, destination: usize, argument_range: Range<usize>) {
|
||||||
let mut argument_range_iter = argument_range.into_iter();
|
let mut argument_range_iter = argument_range.into_iter();
|
||||||
let (min, max) = {
|
let (min, max) = {
|
||||||
let mut min = None;
|
let mut min = None;
|
||||||
@ -16,9 +16,8 @@ pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range
|
|||||||
let register_index = argument_range_iter
|
let register_index = argument_range_iter
|
||||||
.next()
|
.next()
|
||||||
.unwrap_or_else(|| panic!("No argument was passed to \"random_int\""));
|
.unwrap_or_else(|| panic!("No argument was passed to \"random_int\""));
|
||||||
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
let argument = data.get_register(register_index);
|
||||||
|
|
||||||
if let Some(argument) = value_option {
|
|
||||||
if let Some(integer) = argument.as_integer() {
|
if let Some(integer) = argument.as_integer() {
|
||||||
if min.is_none() {
|
if min.is_none() {
|
||||||
min = Some(integer);
|
min = Some(integer);
|
||||||
@ -27,14 +26,11 @@ pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let random_integer = rand::thread_rng().gen_range(min.unwrap()..max);
|
let random_integer = rand::thread_rng().gen_range(min.unwrap()..max);
|
||||||
|
let new_register = Register::Value(Value::integer(random_integer));
|
||||||
|
let old_register = data.get_register_mut(destination);
|
||||||
|
|
||||||
data.set_register(destination, Register::Value(Value::integer(random_integer)));
|
*old_register = new_register;
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,14 @@ use std::ops::Range;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ConcreteValue, Value,
|
ConcreteValue, Value,
|
||||||
vm::{Register, ThreadData, get_next_action},
|
vm::{Register, Thread},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn to_string(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
|
pub fn to_string(thread: &mut Thread, destination: usize, argument_range: Range<usize>) {
|
||||||
let argument_value = data.open_register_unchecked(argument_range.start);
|
let argument_value = thread.get_register(argument_range.start);
|
||||||
let argument_string = argument_value.display(data);
|
let argument_string = argument_value.display(thread);
|
||||||
let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
|
let new_register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
|
||||||
|
let old_register = thread.get_register_mut(destination);
|
||||||
|
|
||||||
data.set_register(destination, register);
|
*old_register = new_register;
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
@ -5,60 +5,12 @@ use std::{
|
|||||||
|
|
||||||
use tracing::{Level, info, span};
|
use tracing::{Level, info, span};
|
||||||
|
|
||||||
use crate::{
|
use crate::{DustString, vm::Thread};
|
||||||
DustString,
|
|
||||||
vm::{Thread, ThreadData, get_next_action},
|
|
||||||
};
|
|
||||||
|
|
||||||
fn start_thread(data: &mut ThreadData, argument_range: Range<u16>) -> JoinHandle<()> {
|
fn start_thread(thread: &mut Thread, argument_range: Range<usize>) -> JoinHandle<()> {
|
||||||
let mut argument_range_iter = argument_range.into_iter();
|
todo!();
|
||||||
let function_argument = {
|
|
||||||
loop {
|
|
||||||
let register_index = argument_range_iter
|
|
||||||
.next()
|
|
||||||
.unwrap_or_else(|| panic!("No argument was passed to \"spawn\""));
|
|
||||||
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
|
||||||
|
|
||||||
if let Some(argument) = value_option {
|
|
||||||
break argument;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let function = function_argument.as_function().unwrap();
|
|
||||||
let prototype_index = function.prototype_index as usize;
|
|
||||||
let current_call = data.call_stack.last_unchecked();
|
|
||||||
let prototype = current_call.chunk.prototypes[prototype_index].clone();
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"Spawning thread for \"{}\"",
|
|
||||||
function
|
|
||||||
.name
|
|
||||||
.as_ref()
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_else(|| DustString::from("anonymous"))
|
|
||||||
);
|
|
||||||
|
|
||||||
let thread_name = prototype
|
|
||||||
.name
|
|
||||||
.as_ref()
|
|
||||||
.map(|name| name.to_string())
|
|
||||||
.unwrap_or_else(|| "anonymous".to_string());
|
|
||||||
let mut thread = Thread::new(prototype);
|
|
||||||
|
|
||||||
Builder::new()
|
|
||||||
.name(thread_name)
|
|
||||||
.spawn(move || {
|
|
||||||
let span = span!(Level::INFO, "Spawned thread");
|
|
||||||
let _enter = span.enter();
|
|
||||||
|
|
||||||
thread.run();
|
|
||||||
})
|
|
||||||
.expect("Critical VM Error: Failed to spawn thread")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
pub fn spawn(data: &mut Thread, _: usize, argument_range: Range<usize>) {
|
||||||
let _ = start_thread(data, argument_range);
|
let _ = start_thread(data, argument_range);
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{vm::ThreadData, Pointer, Type};
|
use crate::{Pointer, Type, vm::Thread};
|
||||||
|
|
||||||
use super::DustString;
|
use super::DustString;
|
||||||
|
|
||||||
@ -11,17 +11,17 @@ pub struct AbstractList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AbstractList {
|
impl AbstractList {
|
||||||
pub fn display(&self, data: &ThreadData) -> DustString {
|
pub fn display(&self, thread: &Thread) -> DustString {
|
||||||
let mut display = DustString::new();
|
let mut display = DustString::new();
|
||||||
|
|
||||||
display.push('[');
|
display.push('[');
|
||||||
|
|
||||||
for (i, item) in self.item_pointers.iter().enumerate() {
|
for (i, pointer) in self.item_pointers.iter().enumerate() {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
display.push_str(", ");
|
display.push_str(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
let item_display = data.follow_pointer_unchecked(*item).display(data);
|
let item_display = thread.get_pointer_value(pointer).to_string();
|
||||||
|
|
||||||
display.push_str(&item_display);
|
display.push_str(&item_display);
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::fmt::{self, Debug, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Type, vm::ThreadData};
|
use crate::{Type, vm::Thread};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
@ -219,7 +219,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display(&self, data: &ThreadData) -> DustString {
|
pub fn display(&self, data: &Thread) -> DustString {
|
||||||
match self {
|
match self {
|
||||||
Value::AbstractList(list) => list.display(data),
|
Value::AbstractList(list) => list.display(data),
|
||||||
Value::Concrete(concrete_value) => concrete_value.display(),
|
Value::Concrete(concrete_value) => concrete_value.display(),
|
||||||
|
@ -3,15 +3,16 @@ use tracing::trace;
|
|||||||
use crate::{
|
use crate::{
|
||||||
AbstractList, ConcreteValue, Instruction, Operand, Type, Value,
|
AbstractList, ConcreteValue, Instruction, Operand, Type, Value,
|
||||||
instruction::{
|
instruction::{
|
||||||
Add, Call, CallNative, Close, Divide, Equal, GetLocal, InstructionBuilder, Jump, Less,
|
Add, Call, CallNative, Divide, Equal, InstructionBuilder, Jump, Less, LessEqual,
|
||||||
LessEqual, LoadBoolean, LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply,
|
LoadBoolean, LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not,
|
||||||
Negate, Not, Point, Return, SetLocal, Subtract, Test, TestSet, TypeCode,
|
Return, Subtract, Test, TestSet, TypeCode,
|
||||||
},
|
},
|
||||||
vm::CallFrame,
|
vm::CallFrame,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Pointer, Register, thread::Thread};
|
use super::{Pointer, Register, thread::Thread};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ActionSequence {
|
pub struct ActionSequence {
|
||||||
pub actions: Vec<Action>,
|
pub actions: Vec<Action>,
|
||||||
}
|
}
|
||||||
@ -21,7 +22,7 @@ impl ActionSequence {
|
|||||||
let mut actions = Vec::with_capacity(instructions.len());
|
let mut actions = Vec::with_capacity(instructions.len());
|
||||||
|
|
||||||
for instruction in instructions {
|
for instruction in instructions {
|
||||||
let action = Action::from(*instruction);
|
let action = Action::from(instruction);
|
||||||
|
|
||||||
actions.push(action);
|
actions.push(action);
|
||||||
}
|
}
|
||||||
@ -46,18 +47,15 @@ impl From<&Instruction> for Action {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type RunnerLogic = fn(InstructionBuilder, &mut Thread) -> bool;
|
pub type RunnerLogic = fn(InstructionBuilder, &mut Thread);
|
||||||
|
|
||||||
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
|
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 22] = [
|
||||||
point,
|
point,
|
||||||
close,
|
|
||||||
load_boolean,
|
load_boolean,
|
||||||
load_constant,
|
load_constant,
|
||||||
load_function,
|
load_function,
|
||||||
load_list,
|
load_list,
|
||||||
load_self,
|
load_self,
|
||||||
get_local,
|
|
||||||
set_local,
|
|
||||||
add,
|
add,
|
||||||
subtract,
|
subtract,
|
||||||
multiply,
|
multiply,
|
||||||
@ -76,782 +74,66 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
|
|||||||
r#return,
|
r#return,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn point(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
pub fn point(instruction: InstructionBuilder, thread: &mut Thread) {
|
||||||
let Point { from, to } = instruction.into();
|
let destination = instruction.a_field as usize;
|
||||||
let from_register = data.get_register_unchecked(from);
|
let to = instruction.b_field as usize;
|
||||||
let from_register_is_empty = matches!(from_register, Register::Empty);
|
let to_is_constant = instruction.b_is_constant;
|
||||||
|
let pointer = if to_is_constant {
|
||||||
if !from_register_is_empty {
|
Pointer::Constant(to)
|
||||||
let register = Register::Pointer(Pointer::Register(to));
|
|
||||||
|
|
||||||
data.set_register(from, register);
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let Close { from, to } = instruction.into();
|
|
||||||
|
|
||||||
for register_index in from..to {
|
|
||||||
data.set_register(register_index, Register::Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_boolean(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let LoadBoolean {
|
|
||||||
destination,
|
|
||||||
value,
|
|
||||||
jump_next,
|
|
||||||
} = instruction.into();
|
|
||||||
let boolean = Value::Concrete(ConcreteValue::Boolean(value));
|
|
||||||
let register = Register::Value(boolean);
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
if jump_next {
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_constant(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let LoadConstant {
|
|
||||||
destination,
|
|
||||||
constant_index,
|
|
||||||
jump_next,
|
|
||||||
} = instruction.into();
|
|
||||||
let register = Register::Pointer(Pointer::Constant(constant_index));
|
|
||||||
|
|
||||||
trace!("Load constant {constant_index} into R{destination}");
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
if jump_next {
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_list(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let LoadList {
|
|
||||||
destination,
|
|
||||||
start_register,
|
|
||||||
jump_next,
|
|
||||||
} = instruction.into();
|
|
||||||
let mut item_pointers = Vec::with_capacity((destination - start_register) as usize);
|
|
||||||
let mut item_type = Type::Any;
|
|
||||||
|
|
||||||
for register_index in start_register..destination {
|
|
||||||
match data.get_register_unchecked(register_index) {
|
|
||||||
Register::Empty => continue,
|
|
||||||
Register::Value(value) => {
|
|
||||||
if item_type == Type::Any {
|
|
||||||
item_type = value.r#type();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Register::Pointer(pointer) => {
|
|
||||||
if item_type == Type::Any {
|
|
||||||
item_type = data.follow_pointer_unchecked(*pointer).r#type();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let pointer = Pointer::Register(register_index);
|
|
||||||
|
|
||||||
item_pointers.push(pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
let list_value = Value::AbstractList(AbstractList {
|
|
||||||
item_type,
|
|
||||||
item_pointers,
|
|
||||||
});
|
|
||||||
let register = Register::Value(list_value);
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_function(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let LoadFunction {
|
|
||||||
destination,
|
|
||||||
prototype_index,
|
|
||||||
jump_next,
|
|
||||||
} = instruction.into();
|
|
||||||
let prototype_index = prototype_index as usize;
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
let prototype = ¤t_call.chunk.prototypes[prototype_index];
|
|
||||||
let function = prototype.as_function();
|
|
||||||
let register = Register::Value(Value::Function(function));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_self(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let LoadSelf {
|
|
||||||
destination,
|
|
||||||
jump_next,
|
|
||||||
} = instruction.into();
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
let prototype = ¤t_call.chunk;
|
|
||||||
let function = prototype.as_function();
|
|
||||||
let register = Register::Value(Value::Function(function));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_local(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let GetLocal {
|
|
||||||
destination,
|
|
||||||
local_index,
|
|
||||||
} = instruction.into();
|
|
||||||
let local_register_index = data.get_local_register(local_index);
|
|
||||||
let register = Register::Pointer(Pointer::Register(local_register_index));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_local(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let SetLocal {
|
|
||||||
register_index,
|
|
||||||
local_index,
|
|
||||||
} = instruction.into();
|
|
||||||
let local_register_index = data.get_local_register(local_index);
|
|
||||||
let register = Register::Pointer(Pointer::Register(register_index));
|
|
||||||
|
|
||||||
data.set_register(local_register_index, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let Add {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let sum = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Integer(left + right)
|
|
||||||
}
|
|
||||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Float(left + right)
|
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot add values"),
|
|
||||||
};
|
|
||||||
let register = Register::Value(Value::Concrete(sum));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subtract(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let Subtract {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let difference = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Integer(left - right)
|
|
||||||
}
|
|
||||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Float(left - right)
|
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot subtract values"),
|
|
||||||
};
|
|
||||||
let register = Register::Value(Value::Concrete(difference));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn multiply(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let Multiply {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let product = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Integer(left * right)
|
|
||||||
}
|
|
||||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Float(left * right)
|
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot multiply values"),
|
|
||||||
};
|
|
||||||
let register = Register::Value(Value::Concrete(product));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn divide(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let Divide {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let quotient = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Integer(left / right)
|
|
||||||
}
|
|
||||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Float(left / right)
|
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot divide values"),
|
|
||||||
};
|
|
||||||
let register = Register::Value(Value::Concrete(quotient));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn modulo(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let Modulo {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let remainder = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Integer(left % right)
|
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot modulo values"),
|
|
||||||
};
|
|
||||||
let register = Register::Value(Value::Concrete(remainder));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let Test {
|
|
||||||
operand_register,
|
|
||||||
test_value,
|
|
||||||
} = instruction.into();
|
|
||||||
let value = data.open_register_unchecked(operand_register);
|
|
||||||
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
|
||||||
*boolean
|
|
||||||
} else {
|
} else {
|
||||||
panic!("VM Error: Expected boolean value for TEST operation",);
|
Pointer::Register(to)
|
||||||
};
|
};
|
||||||
|
let new_register = Register::Pointer(pointer);
|
||||||
|
let old_register = thread.get_register_mut(destination);
|
||||||
|
|
||||||
if boolean == test_value {
|
*old_register = new_register;
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_set(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
pub fn load_boolean(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let TestSet {
|
|
||||||
destination,
|
|
||||||
argument,
|
|
||||||
test_value,
|
|
||||||
} = instruction.into();
|
|
||||||
let value = data.get_argument_unchecked(argument);
|
|
||||||
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
|
||||||
*boolean
|
|
||||||
} else {
|
|
||||||
panic!("VM Error: Expected boolean value for TEST_SET operation",);
|
|
||||||
};
|
|
||||||
|
|
||||||
if boolean == test_value {
|
pub fn load_constant(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
} else {
|
|
||||||
let pointer = match argument {
|
|
||||||
Operand::Constant(constant_index) => Pointer::Constant(constant_index),
|
|
||||||
Operand::Register(register_index) => Pointer::Register(register_index),
|
|
||||||
};
|
|
||||||
let register = Register::Pointer(pointer);
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
pub fn load_list(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
pub fn load_function(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
|
|
||||||
false
|
pub fn load_self(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn equal(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
pub fn get_local(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let Equal {
|
|
||||||
comparator,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let is_equal = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left == right
|
pub fn set_local(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left == right
|
pub fn add(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
(TypeCode::BOOLEAN, TypeCode::BOOLEAN) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_boolean()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_boolean()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left == right
|
pub fn subtract(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
(TypeCode::STRING, TypeCode::STRING) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_string()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_string()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left == right
|
pub fn multiply(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot compare values"),
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_equal == comparator {
|
pub fn divide(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
pub fn modulo(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
pub fn test(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
|
|
||||||
false
|
pub fn test_set(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
pub fn equal(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let Less {
|
|
||||||
comparator,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let is_less = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left < right
|
pub fn less(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left < right
|
pub fn less_equal(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot compare values"),
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_less == comparator {
|
pub fn negate(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
pub fn not(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
pub fn jump(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
|
|
||||||
false
|
pub fn call(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less_equal(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
pub fn call_native(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let LessEqual {
|
|
||||||
comparator,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let is_less_or_equal = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left <= right
|
pub fn r#return(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left <= right
|
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot compare values"),
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_less_or_equal == comparator {
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn negate(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let Negate {
|
|
||||||
destination,
|
|
||||||
argument,
|
|
||||||
argument_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let argument = data.get_argument_unchecked(argument);
|
|
||||||
let negated = argument.negate();
|
|
||||||
let register = Register::Value(negated);
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn not(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let Not {
|
|
||||||
destination,
|
|
||||||
argument,
|
|
||||||
} = instruction.into();
|
|
||||||
let argument = data.get_argument_unchecked(argument);
|
|
||||||
let not = match argument {
|
|
||||||
Value::Concrete(ConcreteValue::Boolean(boolean)) => ConcreteValue::Boolean(!boolean),
|
|
||||||
_ => panic!("VM Error: Expected boolean value for NOT operation"),
|
|
||||||
};
|
|
||||||
let register = Register::Value(Value::Concrete(not));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn jump(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let Jump {
|
|
||||||
offset,
|
|
||||||
is_positive,
|
|
||||||
} = instruction.into();
|
|
||||||
let offset = offset as usize;
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
if is_positive {
|
|
||||||
current_call.ip += offset;
|
|
||||||
} else {
|
|
||||||
current_call.ip -= offset + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let Call {
|
|
||||||
destination: return_register,
|
|
||||||
function_register,
|
|
||||||
argument_count,
|
|
||||||
is_recursive,
|
|
||||||
} = instruction.into();
|
|
||||||
let current_call = data.call_stack.last_unchecked();
|
|
||||||
let first_argument_register = return_register - argument_count;
|
|
||||||
let prototype = if is_recursive {
|
|
||||||
current_call.chunk.clone()
|
|
||||||
} else {
|
|
||||||
let function = data
|
|
||||||
.open_register_unchecked(function_register)
|
|
||||||
.as_function()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
current_call.chunk.prototypes[function.prototype_index as usize].clone()
|
|
||||||
};
|
|
||||||
let mut next_call = CallFrame::new(prototype, return_register);
|
|
||||||
let mut argument_index = 0;
|
|
||||||
|
|
||||||
for register_index in first_argument_register..return_register {
|
|
||||||
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
|
||||||
let argument = if let Some(value) = value_option {
|
|
||||||
value.clone()
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
next_call.registers[argument_index] = Register::Value(argument);
|
|
||||||
argument_index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.call_stack.push(next_call);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call_native(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
let CallNative {
|
|
||||||
destination,
|
|
||||||
function,
|
|
||||||
argument_count,
|
|
||||||
} = instruction.into();
|
|
||||||
let first_argument_index = destination - argument_count;
|
|
||||||
let argument_range = first_argument_index..destination;
|
|
||||||
|
|
||||||
function.call(data, destination, argument_range)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn r#return(instruction: InstructionBuilder, data: &mut Thread) -> bool {
|
|
||||||
trace!("Returning with call stack:\n{}", data.call_stack);
|
|
||||||
|
|
||||||
let Return {
|
|
||||||
should_return_value,
|
|
||||||
return_register,
|
|
||||||
} = instruction.into();
|
|
||||||
let (destination, return_value) = if data.call_stack.len() == 1 {
|
|
||||||
if should_return_value {
|
|
||||||
data.return_value_index = Some(return_register);
|
|
||||||
};
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
let return_value = data.empty_register_or_clone_constant_unchecked(return_register);
|
|
||||||
let destination = data.call_stack.pop_unchecked().return_register;
|
|
||||||
|
|
||||||
(destination, return_value)
|
|
||||||
};
|
|
||||||
|
|
||||||
if should_return_value {
|
|
||||||
data.set_register(destination, Register::Value(return_value));
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
@ -860,15 +142,12 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const ALL_OPERATIONS: [(Operation, RunnerLogic); 24] = [
|
const ALL_OPERATIONS: [(Operation, RunnerLogic); 21] = [
|
||||||
(Operation::POINT, point),
|
(Operation::POINT, point),
|
||||||
(Operation::CLOSE, close),
|
|
||||||
(Operation::LOAD_BOOLEAN, load_boolean),
|
(Operation::LOAD_BOOLEAN, load_boolean),
|
||||||
(Operation::LOAD_CONSTANT, load_constant),
|
(Operation::LOAD_CONSTANT, load_constant),
|
||||||
(Operation::LOAD_LIST, load_list),
|
(Operation::LOAD_LIST, load_list),
|
||||||
(Operation::LOAD_SELF, load_self),
|
(Operation::LOAD_SELF, load_self),
|
||||||
(Operation::GET_LOCAL, get_local),
|
|
||||||
(Operation::SET_LOCAL, set_local),
|
|
||||||
(Operation::ADD, add),
|
(Operation::ADD, add),
|
||||||
(Operation::SUBTRACT, subtract),
|
(Operation::SUBTRACT, subtract),
|
||||||
(Operation::MULTIPLY, multiply),
|
(Operation::MULTIPLY, multiply),
|
||||||
|
@ -3,27 +3,32 @@ use std::{
|
|||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use smallvec::{SmallVec, smallvec};
|
||||||
|
|
||||||
use crate::{Chunk, DustString};
|
use crate::{Chunk, DustString};
|
||||||
|
|
||||||
use super::Register;
|
use super::{Register, action::ActionSequence};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CallFrame {
|
pub struct CallFrame {
|
||||||
pub chunk: Arc<Chunk>,
|
pub chunk: Arc<Chunk>,
|
||||||
pub ip: usize,
|
pub ip: usize,
|
||||||
pub return_register: u16,
|
pub return_register: u16,
|
||||||
pub registers: Vec<Register>,
|
pub registers: SmallVec<[Register; 64]>,
|
||||||
|
pub action_sequence: ActionSequence,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CallFrame {
|
impl CallFrame {
|
||||||
pub fn new(chunk: Arc<Chunk>, return_register: u16) -> Self {
|
pub fn new(chunk: Arc<Chunk>, return_register: u16) -> Self {
|
||||||
let register_count = chunk.register_count;
|
let registers = smallvec![Register::Empty; chunk.register_count];
|
||||||
|
let action_sequence = ActionSequence::new(&chunk.instructions);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
chunk,
|
chunk,
|
||||||
ip: 0,
|
ip: 0,
|
||||||
return_register,
|
return_register,
|
||||||
registers: vec![Register::Empty; register_count],
|
registers,
|
||||||
|
action_sequence,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub use action::Action;
|
pub use action::Action;
|
||||||
pub(crate) use action::get_next_action;
|
|
||||||
pub use call_frame::CallFrame;
|
pub use call_frame::CallFrame;
|
||||||
pub use thread::Thread;
|
pub use thread::Thread;
|
||||||
|
|
||||||
@ -73,17 +72,17 @@ impl Display for Register {
|
|||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Empty => write!(f, "empty"),
|
Self::Empty => write!(f, "empty"),
|
||||||
Self::Value(value) => write!(f, "{}", value),
|
Self::Value(value) => write!(f, "{value}"),
|
||||||
Self::Pointer(pointer) => write!(f, "{}", pointer),
|
Self::Pointer(pointer) => write!(f, "{pointer}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub enum Pointer {
|
pub enum Pointer {
|
||||||
Register(u16),
|
Register(usize),
|
||||||
Constant(u16),
|
Constant(usize),
|
||||||
Stack(usize, u16),
|
Stack(usize, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Pointer {
|
impl Display for Pointer {
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
use std::{mem::replace, sync::Arc, thread::JoinHandle};
|
use std::{sync::Arc, thread::JoinHandle};
|
||||||
|
|
||||||
use tracing::{info, trace};
|
use tracing::{info, trace};
|
||||||
|
|
||||||
use crate::{
|
use crate::{Chunk, DustString, Span, Value, vm::CallFrame};
|
||||||
Chunk, DustString, Operand, Span, Value,
|
|
||||||
vm::{CallFrame, action::ActionSequence},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{Pointer, Register};
|
use super::{Pointer, Register};
|
||||||
|
|
||||||
@ -40,9 +37,6 @@ impl Thread {
|
|||||||
.unwrap_or_else(|| DustString::from("anonymous"))
|
.unwrap_or_else(|| DustString::from("anonymous"))
|
||||||
);
|
);
|
||||||
|
|
||||||
let main_call = self.current_frame();
|
|
||||||
let action_sequence = ActionSequence::new(&main_call.chunk.instructions);
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let current_frame = self.current_frame_mut();
|
let current_frame = self.current_frame_mut();
|
||||||
let ip = {
|
let ip = {
|
||||||
@ -52,18 +46,21 @@ impl Thread {
|
|||||||
ip
|
ip
|
||||||
};
|
};
|
||||||
let current_action = if cfg!(debug_assertions) {
|
let current_action = if cfg!(debug_assertions) {
|
||||||
action_sequence.actions.get(ip).unwrap()
|
current_frame.action_sequence.actions.get(ip).unwrap()
|
||||||
} else {
|
} else {
|
||||||
unsafe { action_sequence.actions.get_unchecked(ip) }
|
unsafe { current_frame.action_sequence.actions.get_unchecked(ip) }
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!("Operation: {}", current_action.instruction.operation);
|
trace!(
|
||||||
|
"Instruction operation: {}",
|
||||||
|
current_action.instruction.operation
|
||||||
|
);
|
||||||
|
|
||||||
(current_action.logic)(current_action.instruction, &mut self);
|
(current_action.logic)(current_action.instruction, &mut self);
|
||||||
|
|
||||||
if let Some(return_index_option) = self.return_value_index {
|
if let Some(return_index_option) = self.return_value_index {
|
||||||
if let Some(return_index) = return_index_option {
|
if let Some(return_index) = return_index_option {
|
||||||
let return_value = self.open_register_unchecked(return_index as u16).clone();
|
let return_value = self.get_register(return_index).clone();
|
||||||
|
|
||||||
return Some(return_value);
|
return Some(return_value);
|
||||||
} else {
|
} else {
|
||||||
@ -95,187 +92,84 @@ impl Thread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value {
|
pub fn get_register(&self, register_index: usize) -> &Value {
|
||||||
trace!("Follow {pointer}");
|
|
||||||
|
|
||||||
match pointer {
|
|
||||||
Pointer::Register(register_index) => self.open_register_unchecked(register_index),
|
|
||||||
Pointer::Constant(constant_index) => self.get_constant_unchecked(constant_index),
|
|
||||||
Pointer::Stack(stack_index, register_index) => unsafe {
|
|
||||||
let register = self
|
|
||||||
.call_stack
|
|
||||||
.get_unchecked(stack_index)
|
|
||||||
.registers
|
|
||||||
.get_unchecked(register_index as usize);
|
|
||||||
|
|
||||||
match register {
|
|
||||||
Register::Value(value) => value,
|
|
||||||
Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer),
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_register_unchecked(&self, register_index: u16) -> &Register {
|
|
||||||
trace!("Get R{register_index}");
|
trace!("Get R{register_index}");
|
||||||
|
|
||||||
let register_index = register_index as usize;
|
|
||||||
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
&self.call_stack.last_unchecked().registers[register_index]
|
|
||||||
} else {
|
|
||||||
unsafe {
|
|
||||||
self.call_stack
|
|
||||||
.last_unchecked()
|
|
||||||
.registers
|
|
||||||
.get_unchecked(register_index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_register(&mut self, to_register: u16, register: Register) {
|
|
||||||
let to_register = to_register as usize;
|
|
||||||
|
|
||||||
self.call_stack.last_mut_unchecked().registers[to_register] = register;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open_register_unchecked(&self, register_index: u16) -> &Value {
|
|
||||||
let register_index = register_index as usize;
|
|
||||||
|
|
||||||
let register = if cfg!(debug_assertions) {
|
let register = if cfg!(debug_assertions) {
|
||||||
&self.call_stack.last_unchecked().registers[register_index]
|
self.call_stack
|
||||||
|
.last()
|
||||||
|
.unwrap()
|
||||||
|
.registers
|
||||||
|
.get(register_index)
|
||||||
|
.unwrap()
|
||||||
} else {
|
} else {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.call_stack
|
self.call_stack
|
||||||
.last_unchecked()
|
.last()
|
||||||
|
.unwrap_unchecked()
|
||||||
.registers
|
.registers
|
||||||
.get_unchecked(register_index)
|
.get_unchecked(register_index)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!("Open R{register_index} to {register}");
|
|
||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => value,
|
Register::Value(value) => value,
|
||||||
Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer),
|
Register::Pointer(pointer) => self.get_pointer_value(pointer),
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_register_allow_empty_unchecked(&self, register_index: u16) -> Option<&Value> {
|
pub fn get_register_mut(&mut self, register_index: usize) -> &mut Register {
|
||||||
trace!("Open R{register_index}");
|
trace!("Get R{register_index}");
|
||||||
|
|
||||||
let register = self.get_register_unchecked(register_index);
|
|
||||||
|
|
||||||
trace!("Open R{register_index} to {register}");
|
|
||||||
|
|
||||||
match register {
|
|
||||||
Register::Value(value) => Some(value),
|
|
||||||
Register::Pointer(pointer) => Some(self.follow_pointer_unchecked(*pointer)),
|
|
||||||
Register::Empty => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn empty_register_or_clone_constant_unchecked(&mut self, register_index: u16) -> Value {
|
|
||||||
let register_index = register_index as usize;
|
|
||||||
let old_register = replace(
|
|
||||||
&mut self.call_stack.last_mut_unchecked().registers[register_index],
|
|
||||||
Register::Empty,
|
|
||||||
);
|
|
||||||
|
|
||||||
match old_register {
|
|
||||||
Register::Value(value) => value,
|
|
||||||
Register::Pointer(pointer) => match pointer {
|
|
||||||
Pointer::Register(register_index) => {
|
|
||||||
self.empty_register_or_clone_constant_unchecked(register_index)
|
|
||||||
}
|
|
||||||
Pointer::Constant(constant_index) => {
|
|
||||||
self.get_constant_unchecked(constant_index).clone()
|
|
||||||
}
|
|
||||||
Pointer::Stack(stack_index, register_index) => {
|
|
||||||
let call = self.call_stack.get_unchecked_mut(stack_index);
|
|
||||||
|
|
||||||
let old_register = replace(
|
|
||||||
&mut call.registers[register_index as usize],
|
|
||||||
Register::Empty,
|
|
||||||
);
|
|
||||||
|
|
||||||
match old_register {
|
|
||||||
Register::Value(value) => value,
|
|
||||||
Register::Pointer(pointer) => {
|
|
||||||
self.follow_pointer_unchecked(pointer).clone()
|
|
||||||
}
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clone_register_value_or_constant_unchecked(&self, register_index: u16) -> Value {
|
|
||||||
let register = self.get_register_unchecked(register_index);
|
|
||||||
|
|
||||||
match register {
|
|
||||||
Register::Value(value) => value.clone(),
|
|
||||||
Register::Pointer(pointer) => match pointer {
|
|
||||||
Pointer::Register(register_index) => {
|
|
||||||
self.open_register_unchecked(*register_index).clone()
|
|
||||||
}
|
|
||||||
Pointer::Constant(constant_index) => {
|
|
||||||
self.get_constant_unchecked(*constant_index).clone()
|
|
||||||
}
|
|
||||||
Pointer::Stack(stack_index, register_index) => {
|
|
||||||
let call = self.call_stack.get_unchecked(*stack_index);
|
|
||||||
let register = &call.registers[*register_index as usize];
|
|
||||||
|
|
||||||
match register {
|
|
||||||
Register::Value(value) => value.clone(),
|
|
||||||
Register::Pointer(pointer) => {
|
|
||||||
self.follow_pointer_unchecked(*pointer).clone()
|
|
||||||
}
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DRY helper to get a value from an Argument
|
|
||||||
pub fn get_argument_unchecked(&self, argument: Operand) -> &Value {
|
|
||||||
match argument {
|
|
||||||
Operand::Constant(constant_index) => self.get_constant_unchecked(constant_index),
|
|
||||||
Operand::Register(register_index) => self.open_register_unchecked(register_index),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_constant_unchecked(&self, constant_index: u16) -> &Value {
|
|
||||||
let constant_index = constant_index as usize;
|
|
||||||
|
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
&self.call_stack.last().unwrap().chunk.constants[constant_index]
|
self.call_stack
|
||||||
|
.last_mut()
|
||||||
|
.unwrap()
|
||||||
|
.registers
|
||||||
|
.get_mut(register_index)
|
||||||
|
.unwrap()
|
||||||
} else {
|
} else {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.call_stack
|
self.call_stack
|
||||||
.last_unchecked()
|
.last_mut()
|
||||||
.chunk
|
.unwrap_unchecked()
|
||||||
.constants
|
.registers
|
||||||
.get_unchecked(constant_index)
|
.get_unchecked_mut(register_index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local_register(&self, local_index: u16) -> u16 {
|
pub fn get_constant(&self, constant_index: usize) -> &Value {
|
||||||
let local_index = local_index as usize;
|
if cfg!(debug_assertions) {
|
||||||
let chunk = &self.call_stack.last_unchecked().chunk;
|
self.chunk.constants.get(constant_index).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.chunk.constants.get_unchecked(constant_index) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assert!(
|
pub fn get_pointer_value(&self, pointer: &Pointer) -> &Value {
|
||||||
local_index < chunk.locals.len(),
|
match pointer {
|
||||||
"VM Error: Local index out of bounds"
|
Pointer::Register(register_index) => self.get_register(*register_index),
|
||||||
);
|
Pointer::Constant(constant_index) => self.get_constant(*constant_index),
|
||||||
|
Pointer::Stack(call_index, register_index) => {
|
||||||
|
let call_frame = if cfg!(debug_assertions) {
|
||||||
|
self.call_stack.get(*call_index).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.call_stack.get_unchecked(*call_index) }
|
||||||
|
};
|
||||||
|
let register = if cfg!(debug_assertions) {
|
||||||
|
call_frame.registers.get(*register_index).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { call_frame.registers.get_unchecked(*register_index) }
|
||||||
|
};
|
||||||
|
|
||||||
chunk.locals[local_index].register_index
|
match register {
|
||||||
|
Register::Value(value) => value,
|
||||||
|
Register::Pointer(pointer) => self.get_pointer_value(pointer),
|
||||||
|
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user