Fix compiler errors; Continue value overhaul
This commit is contained in:
parent
d3ec85f9ad
commit
151cfe7095
@ -43,7 +43,7 @@ use std::io::{self, Write};
|
|||||||
|
|
||||||
use colored::{ColoredString, Colorize};
|
use colored::{ColoredString, Colorize};
|
||||||
|
|
||||||
use crate::{Chunk, Local};
|
use crate::{Chunk, Local, Type};
|
||||||
|
|
||||||
const INSTRUCTION_COLUMNS: [(&str, usize); 4] =
|
const INSTRUCTION_COLUMNS: [(&str, usize); 4] =
|
||||||
[("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 41)];
|
[("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 41)];
|
||||||
@ -66,11 +66,11 @@ const LOCAL_BORDERS: [&str; 3] = [
|
|||||||
"╰─────┴────────────────┴──────────┴───────┴───────╯",
|
"╰─────┴────────────────┴──────────┴───────┴───────╯",
|
||||||
];
|
];
|
||||||
|
|
||||||
const CONSTANT_COLUMNS: [(&str, usize); 3] = [("i", 5), ("TYPE", 26), ("VALUE", 26)];
|
const CONSTANT_COLUMNS: [(&str, usize); 3] = [("i", 11), ("TYPE", 26), ("VALUE", 26)];
|
||||||
const CONSTANT_BORDERS: [&str; 3] = [
|
const CONSTANT_BORDERS: [&str; 3] = [
|
||||||
"╭─────┬──────────────────────────┬──────────────────────────╮",
|
"╭───────────┬──────────────────────────┬──────────────────────────╮",
|
||||||
"├─────┼──────────────────────────┼──────────────────────────┤",
|
"├───────────┼──────────────────────────┼──────────────────────────┤",
|
||||||
"╰─────┴──────────────────────────┴──────────────────────────╯",
|
"╰───────────┴──────────────────────────┴──────────────────────────╯",
|
||||||
];
|
];
|
||||||
|
|
||||||
const INDENTATION: &str = "│ ";
|
const INDENTATION: &str = "│ ";
|
||||||
@ -314,6 +314,7 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
Local {
|
Local {
|
||||||
identifier_index,
|
identifier_index,
|
||||||
register_index,
|
register_index,
|
||||||
|
r#type,
|
||||||
scope,
|
scope,
|
||||||
is_mutable,
|
is_mutable,
|
||||||
},
|
},
|
||||||
@ -321,7 +322,7 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
{
|
{
|
||||||
let identifier_display = self
|
let identifier_display = self
|
||||||
.chunk
|
.chunk
|
||||||
.constants
|
.constant_strings
|
||||||
.get(*identifier_index as usize)
|
.get(*identifier_index as usize)
|
||||||
.map(|value| value.to_string())
|
.map(|value| value.to_string())
|
||||||
.unwrap_or_else(|| "unknown".to_string());
|
.unwrap_or_else(|| "unknown".to_string());
|
||||||
@ -352,8 +353,9 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
self.write_center_border(&column_name_line)?;
|
self.write_center_border(&column_name_line)?;
|
||||||
self.write_center_border(CONSTANT_BORDERS[1])?;
|
self.write_center_border(CONSTANT_BORDERS[1])?;
|
||||||
|
|
||||||
for (index, value) in self.chunk.constants.iter().enumerate() {
|
for (index, value) in self.chunk.constant_characters.iter().enumerate() {
|
||||||
let type_display = value.r#type().to_string();
|
let index_display = format!("C_CHAR_{index}");
|
||||||
|
let type_display = Type::Character.to_string();
|
||||||
let value_display = {
|
let value_display = {
|
||||||
let mut value_string = value.to_string();
|
let mut value_string = value.to_string();
|
||||||
|
|
||||||
@ -363,7 +365,46 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
|
|
||||||
value_string
|
value_string
|
||||||
};
|
};
|
||||||
let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│");
|
let constant_display =
|
||||||
|
format!("│{index_display:^11}│{type_display:^26}│{value_display:^26}│");
|
||||||
|
|
||||||
|
self.write_center_border(&constant_display)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index, value) in self.chunk.constant_floats.iter().enumerate() {
|
||||||
|
let index_display = format!("C_FLOAT_{index}");
|
||||||
|
let type_display = Type::Float.to_string();
|
||||||
|
let value_display = value.to_string();
|
||||||
|
let constant_display =
|
||||||
|
format!("│{index_display:^11}│{type_display:^26}│{value_display:^26}│");
|
||||||
|
|
||||||
|
self.write_center_border(&constant_display)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index, value) in self.chunk.constant_integers.iter().enumerate() {
|
||||||
|
let index_display = format!("C_INT_{index}");
|
||||||
|
let type_display = Type::Integer.to_string();
|
||||||
|
let value_display = value.to_string();
|
||||||
|
let constant_display =
|
||||||
|
format!("│{index_display:^11}│{type_display:^26}│{value_display:^26}│");
|
||||||
|
|
||||||
|
self.write_center_border(&constant_display)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index, value) in self.chunk.constant_strings.iter().enumerate() {
|
||||||
|
let index_display = format!("C_STR_{index}");
|
||||||
|
let type_display = Type::String.to_string();
|
||||||
|
let value_display = {
|
||||||
|
let mut value_string = value.to_string();
|
||||||
|
|
||||||
|
if value_string.len() > 26 {
|
||||||
|
value_string = format!("{value_string:.23}...");
|
||||||
|
}
|
||||||
|
|
||||||
|
value_string
|
||||||
|
};
|
||||||
|
let constant_display =
|
||||||
|
format!("│{index_display:^11}│{type_display:^26}│{value_display:^26}│");
|
||||||
|
|
||||||
self.write_center_border(&constant_display)?;
|
self.write_center_border(&constant_display)?;
|
||||||
}
|
}
|
||||||
@ -415,7 +456,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.instructions.len(),
|
self.chunk.instructions.len(),
|
||||||
self.chunk.constants.len(),
|
self.chunk.constant_characters.len()
|
||||||
|
+ self.chunk.constant_floats.len()
|
||||||
|
+ self.chunk.constant_integers.len()
|
||||||
|
+ self.chunk.constant_strings.len(),
|
||||||
self.chunk.locals.len(),
|
self.chunk.locals.len(),
|
||||||
self.chunk.r#type.return_type
|
self.chunk.r#type.return_type
|
||||||
);
|
);
|
||||||
@ -431,7 +475,11 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
self.write_local_section()?;
|
self.write_local_section()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.chunk.constants.is_empty() {
|
if !(self.chunk.constant_characters.is_empty()
|
||||||
|
&& self.chunk.constant_floats.is_empty()
|
||||||
|
&& self.chunk.constant_integers.is_empty()
|
||||||
|
&& self.chunk.constant_strings.is_empty())
|
||||||
|
{
|
||||||
self.write_constant_section()?;
|
self.write_constant_section()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::Scope;
|
use crate::{Scope, Type};
|
||||||
|
|
||||||
/// Scoped variable.
|
/// Scoped variable.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
@ -13,6 +13,9 @@ pub struct Local {
|
|||||||
/// Index of the register where the variable's value is stored.
|
/// Index of the register where the variable's value is stored.
|
||||||
pub register_index: u16,
|
pub register_index: u16,
|
||||||
|
|
||||||
|
/// The variable's type.
|
||||||
|
pub r#type: Type,
|
||||||
|
|
||||||
/// Whether the local is mutable.
|
/// Whether the local is mutable.
|
||||||
pub is_mutable: bool,
|
pub is_mutable: bool,
|
||||||
|
|
||||||
@ -22,10 +25,17 @@ pub struct Local {
|
|||||||
|
|
||||||
impl Local {
|
impl Local {
|
||||||
/// Creates a new Local instance.
|
/// Creates a new Local instance.
|
||||||
pub fn new(identifier_index: u16, register_index: u16, is_mutable: bool, scope: Scope) -> Self {
|
pub fn new(
|
||||||
|
identifier_index: u16,
|
||||||
|
register_index: u16,
|
||||||
|
r#type: Type,
|
||||||
|
is_mutable: bool,
|
||||||
|
scope: Scope,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
identifier_index,
|
identifier_index,
|
||||||
register_index,
|
register_index,
|
||||||
|
r#type,
|
||||||
is_mutable,
|
is_mutable,
|
||||||
scope,
|
scope,
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,8 @@ pub struct Chunk {
|
|||||||
|
|
||||||
pub(crate) prototypes: Vec<Arc<Chunk>>,
|
pub(crate) prototypes: Vec<Arc<Chunk>>,
|
||||||
|
|
||||||
|
pub(crate) argument_lists: Vec<Vec<u16>>,
|
||||||
|
|
||||||
pub(crate) register_count: usize,
|
pub(crate) register_count: usize,
|
||||||
pub(crate) prototype_index: u16,
|
pub(crate) prototype_index: u16,
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -28,8 +28,8 @@ use crate::{Compiler, Instruction, Operation};
|
|||||||
/// The instructions must be in the following order:
|
/// The instructions must be in the following order:
|
||||||
/// - `TEST` or any of the `EQUAL`, `LESS` or `LESS_EQUAL` instructions
|
/// - `TEST` or any of the `EQUAL`, `LESS` or `LESS_EQUAL` instructions
|
||||||
/// - `JUMP`
|
/// - `JUMP`
|
||||||
/// - `LOAD_BOOLEAN` or `LOAD_CONSTANT`
|
/// - `LOAD_ENCODED`
|
||||||
/// - `LOAD_BOOLEAN` or `LOAD_CONSTANT`
|
/// - `LOAD_ENCODED`
|
||||||
///
|
///
|
||||||
/// This optimization was taken from `A No-Frills Introduction to Lua 5.1 VM Instructions` by
|
/// This optimization was taken from `A No-Frills Introduction to Lua 5.1 VM Instructions` by
|
||||||
/// Kein-Hong Man.
|
/// Kein-Hong Man.
|
||||||
@ -39,8 +39,8 @@ pub fn control_flow_register_consolidation(compiler: &mut Compiler) {
|
|||||||
Some([
|
Some([
|
||||||
Operation::TEST | Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL,
|
Operation::TEST | Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL,
|
||||||
Operation::JUMP,
|
Operation::JUMP,
|
||||||
Operation::LOAD_ENCODED | Operation::LOAD_CONSTANT,
|
Operation::LOAD_ENCODED,
|
||||||
Operation::LOAD_ENCODED | Operation::LOAD_CONSTANT,
|
Operation::LOAD_ENCODED,
|
||||||
])
|
])
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@ -51,14 +51,19 @@ pub fn control_flow_register_consolidation(compiler: &mut Compiler) {
|
|||||||
let first_loader_index = compiler.instructions.len() - 2;
|
let first_loader_index = compiler.instructions.len() - 2;
|
||||||
let (first_loader, _, _) = &mut compiler.instructions.get_mut(first_loader_index).unwrap();
|
let (first_loader, _, _) = &mut compiler.instructions.get_mut(first_loader_index).unwrap();
|
||||||
let first_loader_destination = first_loader.a_field();
|
let first_loader_destination = first_loader.a_field();
|
||||||
*first_loader =
|
*first_loader = Instruction::load_encoded(
|
||||||
Instruction::load_boolean(first_loader.a_field(), first_loader.b_field() != 0, true);
|
first_loader.a_field(),
|
||||||
|
first_loader.b_field(),
|
||||||
|
first_loader.b_type(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
let second_loader_index = compiler.instructions.len() - 1;
|
let second_loader_index = compiler.instructions.len() - 1;
|
||||||
let (second_loader, _, _) = &mut compiler.instructions.get_mut(second_loader_index).unwrap();
|
let (second_loader, _, _) = &mut compiler.instructions.get_mut(second_loader_index).unwrap();
|
||||||
*second_loader = Instruction::load_boolean(
|
*second_loader = Instruction::load_encoded(
|
||||||
first_loader_destination,
|
first_loader_destination,
|
||||||
second_loader.b_field() != 0,
|
second_loader.b_field(),
|
||||||
|
second_loader.b_type(),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,18 +7,19 @@ use super::InstructionBuilder;
|
|||||||
pub struct CallNative {
|
pub struct CallNative {
|
||||||
pub destination: u16,
|
pub destination: u16,
|
||||||
pub function: NativeFunction,
|
pub function: NativeFunction,
|
||||||
pub argument_count: u16,
|
pub argument_list_index: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for CallNative {
|
impl From<Instruction> for CallNative {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let destination = instruction.a_field();
|
let destination = instruction.a_field();
|
||||||
let function = NativeFunction::from(instruction.b_field());
|
let function = NativeFunction::from(instruction.b_field());
|
||||||
|
let argument_list_index = instruction.c_field();
|
||||||
|
|
||||||
CallNative {
|
CallNative {
|
||||||
destination,
|
destination,
|
||||||
function,
|
function,
|
||||||
argument_count: instruction.c_field(),
|
argument_list_index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -28,7 +29,7 @@ impl From<CallNative> for Instruction {
|
|||||||
let operation = Operation::CALL_NATIVE;
|
let operation = Operation::CALL_NATIVE;
|
||||||
let a_field = call_native.destination;
|
let a_field = call_native.destination;
|
||||||
let b_field = call_native.function as u16;
|
let b_field = call_native.function as u16;
|
||||||
let c_field = call_native.argument_count;
|
let c_field = call_native.argument_list_index;
|
||||||
|
|
||||||
InstructionBuilder {
|
InstructionBuilder {
|
||||||
operation,
|
operation,
|
||||||
@ -46,21 +47,9 @@ impl Display for CallNative {
|
|||||||
let CallNative {
|
let CallNative {
|
||||||
destination,
|
destination,
|
||||||
function,
|
function,
|
||||||
argument_count,
|
..
|
||||||
} = self;
|
} = self;
|
||||||
let arguments_start = destination.saturating_sub(*argument_count);
|
|
||||||
let arguments_end = arguments_start + argument_count;
|
|
||||||
|
|
||||||
if function.returns_value() {
|
write!(f, "R{destination} = {function}(..)")
|
||||||
write!(f, "R{destination} = ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
match argument_count {
|
|
||||||
0 => {
|
|
||||||
write!(f, "{function}()")
|
|
||||||
}
|
|
||||||
1 => write!(f, "{function}(R{arguments_start})"),
|
|
||||||
_ => write!(f, "{function}(R{arguments_start}..R{arguments_end})"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
@ -154,6 +150,7 @@ pub use type_code::TypeCode;
|
|||||||
|
|
||||||
use crate::NativeFunction;
|
use crate::NativeFunction;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct InstructionBuilder {
|
pub struct InstructionBuilder {
|
||||||
pub operation: Operation,
|
pub operation: Operation,
|
||||||
pub a_field: u16,
|
pub a_field: u16,
|
||||||
@ -182,6 +179,22 @@ impl InstructionBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&Instruction> for InstructionBuilder {
|
||||||
|
fn from(instruction: &Instruction) -> Self {
|
||||||
|
InstructionBuilder {
|
||||||
|
operation: instruction.operation(),
|
||||||
|
a_field: instruction.a_field(),
|
||||||
|
b_field: instruction.b_field(),
|
||||||
|
c_field: instruction.c_field(),
|
||||||
|
d_field: instruction.d_field(),
|
||||||
|
b_is_constant: instruction.b_is_constant(),
|
||||||
|
c_is_constant: instruction.c_is_constant(),
|
||||||
|
b_type: instruction.b_type(),
|
||||||
|
c_type: instruction.c_type(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for InstructionBuilder {
|
impl Default for InstructionBuilder {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
InstructionBuilder {
|
InstructionBuilder {
|
||||||
@ -259,18 +272,28 @@ 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: u16, r#type: TypeCode) -> Instruction {
|
||||||
Instruction::from(Point { from, to })
|
Instruction::from(Point {
|
||||||
|
destination,
|
||||||
|
to,
|
||||||
|
r#type,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(from: u16, to: u16) -> Instruction {
|
pub fn close(from: u16, to: u16) -> Instruction {
|
||||||
Instruction::from(Close { from, to })
|
Instruction::from(Close { from, to })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_boolean(destination: u16, value: bool, jump_next: bool) -> Instruction {
|
pub fn load_encoded(
|
||||||
Instruction::from(LoadBoolean {
|
destination: u16,
|
||||||
|
value: u16,
|
||||||
|
r#type: TypeCode,
|
||||||
|
jump_next: bool,
|
||||||
|
) -> Instruction {
|
||||||
|
Instruction::from(LoadEncoded {
|
||||||
destination,
|
destination,
|
||||||
value,
|
value,
|
||||||
|
r#type,
|
||||||
jump_next,
|
jump_next,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -306,20 +329,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,
|
||||||
@ -502,12 +511,12 @@ impl Instruction {
|
|||||||
pub fn call_native(
|
pub fn call_native(
|
||||||
destination: u16,
|
destination: u16,
|
||||||
function: NativeFunction,
|
function: NativeFunction,
|
||||||
argument_count: u16,
|
argument_list_index: u16,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
Instruction::from(CallNative {
|
Instruction::from(CallNative {
|
||||||
destination,
|
destination,
|
||||||
function,
|
function,
|
||||||
argument_count,
|
argument_list_index,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -532,7 +541,6 @@ impl Instruction {
|
|||||||
Operation::LOAD_ENCODED
|
Operation::LOAD_ENCODED
|
||||||
| 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
|
||||||
@ -588,7 +596,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
|
||||||
@ -603,7 +610,6 @@ impl Instruction {
|
|||||||
function.returns_value()
|
function.returns_value()
|
||||||
}
|
}
|
||||||
Operation::CLOSE
|
Operation::CLOSE
|
||||||
| Operation::SET_LOCAL
|
|
||||||
| Operation::EQUAL
|
| Operation::EQUAL
|
||||||
| Operation::LESS
|
| Operation::LESS
|
||||||
| Operation::LESS_EQUAL
|
| Operation::LESS_EQUAL
|
||||||
@ -621,13 +627,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::CLOSE => Close::from(*self).to_string(),
|
||||||
Operation::LOAD_ENCODED => LoadBoolean::from(*self).to_string(),
|
Operation::LOAD_ENCODED => LoadEncoded::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(),
|
||||||
|
@ -20,37 +20,33 @@ 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 {
|
||||||
@ -63,8 +59,6 @@ impl Operation {
|
|||||||
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,20 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
use super::InstructionBuilder;
|
use super::{InstructionBuilder, TypeCode};
|
||||||
|
|
||||||
pub struct Point {
|
pub struct Point {
|
||||||
pub from: u16,
|
pub destination: u16,
|
||||||
pub to: u16,
|
pub to: u16,
|
||||||
|
pub r#type: TypeCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
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_field(),
|
||||||
|
r#type: instruction.b_type(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -21,13 +23,15 @@ 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 = r#move.to;
|
||||||
|
let b_type = r#move.r#type;
|
||||||
|
|
||||||
InstructionBuilder {
|
InstructionBuilder {
|
||||||
operation,
|
operation,
|
||||||
|
a_field,
|
||||||
b_field,
|
b_field,
|
||||||
c_field,
|
b_type,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
@ -36,8 +40,20 @@ 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,
|
||||||
|
r#type,
|
||||||
|
} = self;
|
||||||
|
|
||||||
write!(f, "{from} -> {to}")
|
match *r#type {
|
||||||
|
TypeCode::BOOLEAN => write!(f, "R_BOOL_{destination} = R_BOOL_{to}"),
|
||||||
|
TypeCode::BYTE => write!(f, "R_BYTE_{destination} = R_BYTE_{to}"),
|
||||||
|
TypeCode::CHARACTER => write!(f, "R_CHAR_{destination} = R_CHAR_{to}"),
|
||||||
|
TypeCode::FLOAT => write!(f, "R_FLOAT_{destination} = R_FLOAT_{to}"),
|
||||||
|
TypeCode::INTEGER => write!(f, "R_INT_{destination} = R_INT_{to}"),
|
||||||
|
TypeCode::STRING => write!(f, "R_STR_{destination} = R_STR_{to}"),
|
||||||
|
unsupported => unsupported.panic_from_unsupported_code(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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}")
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,12 +4,13 @@ use std::fmt::Display;
|
|||||||
pub struct TypeCode(pub u8);
|
pub struct TypeCode(pub u8);
|
||||||
|
|
||||||
impl TypeCode {
|
impl TypeCode {
|
||||||
pub const BOOLEAN: TypeCode = TypeCode(0);
|
pub const NONE: TypeCode = TypeCode(0);
|
||||||
pub const BYTE: TypeCode = TypeCode(1);
|
pub const BOOLEAN: TypeCode = TypeCode(1);
|
||||||
pub const CHARACTER: TypeCode = TypeCode(2);
|
pub const BYTE: TypeCode = TypeCode(2);
|
||||||
pub const FLOAT: TypeCode = TypeCode(3);
|
pub const CHARACTER: TypeCode = TypeCode(3);
|
||||||
pub const INTEGER: TypeCode = TypeCode(4);
|
pub const FLOAT: TypeCode = TypeCode(4);
|
||||||
pub const STRING: TypeCode = TypeCode(5);
|
pub const INTEGER: TypeCode = TypeCode(5);
|
||||||
|
pub const STRING: TypeCode = TypeCode(6);
|
||||||
|
|
||||||
pub fn panic_from_unknown_code(self) -> ! {
|
pub fn panic_from_unknown_code(self) -> ! {
|
||||||
panic!("Unknown type code: {}", self.0);
|
panic!("Unknown type code: {}", self.0);
|
||||||
|
@ -1,22 +1,5 @@
|
|||||||
use std::{ops::Range, panic};
|
use std::{ops::Range, panic};
|
||||||
|
|
||||||
use crate::vm::ThreadData;
|
use crate::{instruction::InstructionBuilder, vm::Thread};
|
||||||
|
|
||||||
pub fn panic(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
pub fn panic(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let position = data.current_position();
|
|
||||||
let mut message = format!("Dust panic at {position}!");
|
|
||||||
|
|
||||||
for register_index in argument_range {
|
|
||||||
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
|
||||||
let value = match value_option {
|
|
||||||
Some(value) => value,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
let string = value.display(data);
|
|
||||||
|
|
||||||
message.push_str(&string);
|
|
||||||
message.push('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
panic!("{}", message)
|
|
||||||
}
|
|
||||||
|
@ -2,57 +2,13 @@ use std::io::{Write, stdin, stdout};
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ConcreteValue, Value,
|
Value,
|
||||||
vm::{Register, ThreadData, get_next_action},
|
instruction::InstructionBuilder,
|
||||||
|
vm::{Register, Thread},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range<u16>) -> bool {
|
pub fn read_line(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let mut buffer = String::new();
|
|
||||||
|
|
||||||
if stdin().read_line(&mut buffer).is_ok() {
|
pub fn write(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let length = buffer.len();
|
|
||||||
|
|
||||||
buffer.truncate(length.saturating_sub(1));
|
pub fn write_line(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
|
|
||||||
let register = Register::Value(Value::Concrete(ConcreteValue::string(buffer)));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
|
||||||
let mut stdout = stdout();
|
|
||||||
|
|
||||||
for register_index in argument_range {
|
|
||||||
if let Some(value) = data.open_register_allow_empty_unchecked(register_index) {
|
|
||||||
let string = value.display(data);
|
|
||||||
let _ = stdout.write(string.as_bytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = stdout.flush();
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_line(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
|
||||||
let mut stdout = stdout().lock();
|
|
||||||
|
|
||||||
for register_index in argument_range {
|
|
||||||
if let Some(value) = data.open_register_allow_empty_unchecked(register_index) {
|
|
||||||
let string = value.display(data);
|
|
||||||
let _ = stdout.write(string.as_bytes());
|
|
||||||
let _ = stdout.write(b"\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = stdout.flush();
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
@ -17,7 +17,9 @@ use std::{
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{AnnotatedError, FunctionType, Span, Type, vm::ThreadData};
|
use crate::{
|
||||||
|
AnnotatedError, FunctionType, Span, Type, instruction::InstructionBuilder, 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 +36,12 @@ macro_rules! define_native_function {
|
|||||||
impl NativeFunction {
|
impl NativeFunction {
|
||||||
pub fn call(
|
pub fn call(
|
||||||
&self,
|
&self,
|
||||||
data: &mut ThreadData,
|
instruction: InstructionBuilder,
|
||||||
destination: u16,
|
thread: &mut Thread,
|
||||||
argument_range: Range<u16>,
|
) {
|
||||||
) -> bool {
|
|
||||||
match self {
|
match self {
|
||||||
$(
|
$(
|
||||||
NativeFunction::$name => $function(data, destination, argument_range),
|
NativeFunction::$name => $function(instruction, thread),
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,39 +2,6 @@ use std::ops::Range;
|
|||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
use crate::{
|
use crate::{instruction::InstructionBuilder, vm::Thread};
|
||||||
Value,
|
|
||||||
vm::{Register, ThreadData, get_next_action},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
|
pub fn random_int(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let mut argument_range_iter = argument_range.into_iter();
|
|
||||||
let (min, max) = {
|
|
||||||
let mut min = None;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let register_index = argument_range_iter
|
|
||||||
.next()
|
|
||||||
.unwrap_or_else(|| panic!("No argument was passed to \"random_int\""));
|
|
||||||
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
|
||||||
|
|
||||||
if let Some(argument) = value_option {
|
|
||||||
if let Some(integer) = argument.as_integer() {
|
|
||||||
if min.is_none() {
|
|
||||||
min = Some(integer);
|
|
||||||
} else {
|
|
||||||
break (min, integer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let random_integer = rand::thread_rng().gen_range(min.unwrap()..max);
|
|
||||||
|
|
||||||
data.set_register(destination, Register::Value(Value::integer(random_integer)));
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
@ -1,18 +1,9 @@
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ConcreteValue, Value,
|
Value,
|
||||||
vm::{Register, ThreadData, get_next_action},
|
instruction::InstructionBuilder,
|
||||||
|
vm::{Register, Thread},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn to_string(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
|
pub fn to_string(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let argument_value = data.open_register_unchecked(argument_range.start);
|
|
||||||
let argument_string = argument_value.display(data);
|
|
||||||
let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
@ -5,60 +5,6 @@ use std::{
|
|||||||
|
|
||||||
use tracing::{Level, info, span};
|
use tracing::{Level, info, span};
|
||||||
|
|
||||||
use crate::{
|
use crate::{DustString, instruction::InstructionBuilder, vm::Thread};
|
||||||
DustString,
|
|
||||||
vm::{Thread, ThreadData, get_next_action},
|
|
||||||
};
|
|
||||||
|
|
||||||
fn start_thread(data: &mut ThreadData, argument_range: Range<u16>) -> JoinHandle<()> {
|
pub fn spawn(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let mut argument_range_iter = argument_range.into_iter();
|
|
||||||
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 {
|
|
||||||
let _ = start_thread(data, argument_range);
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use crate::{vm::ThreadData, Pointer, Type};
|
|
||||||
|
|
||||||
use super::DustString;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
|
||||||
pub struct AbstractList {
|
|
||||||
pub item_type: Type,
|
|
||||||
pub item_pointers: Vec<Pointer>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AbstractList {
|
|
||||||
pub fn display(&self, data: &ThreadData) -> DustString {
|
|
||||||
let mut display = DustString::new();
|
|
||||||
|
|
||||||
display.push('[');
|
|
||||||
|
|
||||||
for (i, item) in self.item_pointers.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
display.push_str(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
let item_display = data.follow_pointer_unchecked(*item).display(data);
|
|
||||||
|
|
||||||
display.push_str(&item_display);
|
|
||||||
}
|
|
||||||
|
|
||||||
display.push(']');
|
|
||||||
|
|
||||||
display
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for AbstractList {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "[")?;
|
|
||||||
|
|
||||||
for pointer in &self.item_pointers {
|
|
||||||
write!(f, "{}", pointer)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "]")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,341 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use smartstring::{LazyCompact, SmartString};
|
|
||||||
use tracing::trace;
|
|
||||||
|
|
||||||
use crate::{Type, Value, ValueError};
|
|
||||||
|
|
||||||
use super::RangeValue;
|
|
||||||
|
|
||||||
pub type DustString = SmartString<LazyCompact>;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
|
||||||
pub enum ConcreteValue {
|
|
||||||
Boolean(bool),
|
|
||||||
Byte(u8),
|
|
||||||
Character(char),
|
|
||||||
Float(f64),
|
|
||||||
Integer(i64),
|
|
||||||
List(Vec<ConcreteValue>),
|
|
||||||
Range(RangeValue),
|
|
||||||
String(DustString),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConcreteValue {
|
|
||||||
pub fn to_value(self) -> Value {
|
|
||||||
Value::Concrete(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn list<T: Into<Vec<ConcreteValue>>>(into_list: T) -> Self {
|
|
||||||
ConcreteValue::List(into_list.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn string<T: Into<SmartString<LazyCompact>>>(to_string: T) -> Self {
|
|
||||||
ConcreteValue::String(to_string.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_string(&self) -> Option<&DustString> {
|
|
||||||
if let ConcreteValue::String(string) = self {
|
|
||||||
Some(string)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn display(&self) -> DustString {
|
|
||||||
DustString::from(self.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn r#type(&self) -> Type {
|
|
||||||
match self {
|
|
||||||
ConcreteValue::Boolean(_) => Type::Boolean,
|
|
||||||
ConcreteValue::Byte(_) => Type::Byte,
|
|
||||||
ConcreteValue::Character(_) => Type::Character,
|
|
||||||
ConcreteValue::Float(_) => Type::Float,
|
|
||||||
ConcreteValue::Integer(_) => Type::Integer,
|
|
||||||
ConcreteValue::List(list) => {
|
|
||||||
let item_type = list.first().map_or(Type::Any, |item| item.r#type());
|
|
||||||
|
|
||||||
Type::List(Box::new(item_type))
|
|
||||||
}
|
|
||||||
ConcreteValue::Range(range) => range.r#type(),
|
|
||||||
ConcreteValue::String(_) => Type::String,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(&self, other: &Self) -> ConcreteValue {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
match (self, other) {
|
|
||||||
(Byte(left), Byte(right)) => {
|
|
||||||
let sum = left.saturating_add(*right);
|
|
||||||
|
|
||||||
Byte(sum)
|
|
||||||
}
|
|
||||||
(Character(left), Character(right)) => {
|
|
||||||
let mut concatenated = DustString::new();
|
|
||||||
|
|
||||||
concatenated.push(*left);
|
|
||||||
concatenated.push(*right);
|
|
||||||
|
|
||||||
String(concatenated)
|
|
||||||
}
|
|
||||||
(Character(left), String(right)) => {
|
|
||||||
let mut concatenated = DustString::new();
|
|
||||||
|
|
||||||
concatenated.push(*left);
|
|
||||||
concatenated.push_str(right);
|
|
||||||
|
|
||||||
String(concatenated)
|
|
||||||
}
|
|
||||||
(Float(left), Float(right)) => {
|
|
||||||
let sum = left + right;
|
|
||||||
|
|
||||||
Float(sum)
|
|
||||||
}
|
|
||||||
(Integer(left), Integer(right)) => {
|
|
||||||
let sum = left.saturating_add(*right);
|
|
||||||
|
|
||||||
Integer(sum)
|
|
||||||
}
|
|
||||||
(String(left), Character(right)) => {
|
|
||||||
let concatenated = format!("{}{}", left, right);
|
|
||||||
|
|
||||||
String(DustString::from(concatenated))
|
|
||||||
}
|
|
||||||
(String(left), String(right)) => {
|
|
||||||
let concatenated = format!("{}{}", left, right);
|
|
||||||
|
|
||||||
String(DustString::from(concatenated))
|
|
||||||
}
|
|
||||||
_ => panic!(
|
|
||||||
"{}",
|
|
||||||
ValueError::CannotAdd(
|
|
||||||
Value::Concrete(self.clone()),
|
|
||||||
Value::Concrete(other.clone())
|
|
||||||
)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subtract(&self, other: &Self) -> ConcreteValue {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
match (self, other) {
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_sub(*right)),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::Float(left - right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_sub(*right)),
|
|
||||||
_ => panic!(
|
|
||||||
"{}",
|
|
||||||
ValueError::CannotSubtract(
|
|
||||||
Value::Concrete(self.clone()),
|
|
||||||
Value::Concrete(other.clone())
|
|
||||||
)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn multiply(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let product = match (self, other) {
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_mul(*right)),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::Float(left * right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_mul(*right)),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotMultiply(
|
|
||||||
self.clone().to_value(),
|
|
||||||
other.clone().to_value(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(product)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn divide(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let quotient = match (self, other) {
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_div(*right)),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::Float(left / right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_div(*right)),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotMultiply(
|
|
||||||
self.clone().to_value(),
|
|
||||||
other.clone().to_value(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(quotient)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn modulo(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let product = match (self, other) {
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.wrapping_rem(*right)),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::Float(left % right),
|
|
||||||
(Integer(left), Integer(right)) => {
|
|
||||||
ConcreteValue::Integer(left.wrapping_rem_euclid(*right))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotMultiply(
|
|
||||||
self.clone().to_value(),
|
|
||||||
other.clone().to_value(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(product)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn negate(&self) -> ConcreteValue {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Boolean(value) => ConcreteValue::Boolean(!value),
|
|
||||||
Byte(value) => ConcreteValue::Byte(value.wrapping_neg()),
|
|
||||||
Float(value) => ConcreteValue::Float(-value),
|
|
||||||
Integer(value) => ConcreteValue::Integer(value.wrapping_neg()),
|
|
||||||
_ => panic!("{}", ValueError::CannotNegate(self.clone().to_value())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn not(&self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let not = match self {
|
|
||||||
Boolean(value) => ConcreteValue::Boolean(!value),
|
|
||||||
_ => return Err(ValueError::CannotNot(self.clone().to_value())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(not)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn equals(&self, other: &ConcreteValue) -> bool {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
match (self, other) {
|
|
||||||
(Boolean(left), Boolean(right)) => left == right,
|
|
||||||
(Byte(left), Byte(right)) => left == right,
|
|
||||||
(Character(left), Character(right)) => left == right,
|
|
||||||
(Float(left), Float(right)) => left == right,
|
|
||||||
(Integer(left), Integer(right)) => left == right,
|
|
||||||
(List(left), List(right)) => left == right,
|
|
||||||
(Range(left), Range(right)) => left == right,
|
|
||||||
(String(left), String(right)) => left == right,
|
|
||||||
_ => {
|
|
||||||
panic!(
|
|
||||||
"{}",
|
|
||||||
ValueError::CannotCompare(
|
|
||||||
Value::Concrete(self.clone()),
|
|
||||||
Value::Concrete(other.clone())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less_than(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let less_than = match (self, other) {
|
|
||||||
(Boolean(left), Boolean(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
(Character(left), Character(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
(List(left), List(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
(Range(left), Range(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
(String(left), String(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotCompare(
|
|
||||||
self.clone().to_value(),
|
|
||||||
other.clone().to_value(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(less_than)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less_than_or_equals(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let less_than_or_equal = match (self, other) {
|
|
||||||
(Boolean(left), Boolean(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
(Character(left), Character(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
(List(left), List(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
(Range(left), Range(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
(String(left), String(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotCompare(
|
|
||||||
self.clone().to_value(),
|
|
||||||
other.clone().to_value(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(less_than_or_equal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for ConcreteValue {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
trace!("Cloning concrete value {}", self);
|
|
||||||
|
|
||||||
match self {
|
|
||||||
ConcreteValue::Boolean(boolean) => ConcreteValue::Boolean(*boolean),
|
|
||||||
ConcreteValue::Byte(byte) => ConcreteValue::Byte(*byte),
|
|
||||||
ConcreteValue::Character(character) => ConcreteValue::Character(*character),
|
|
||||||
ConcreteValue::Float(float) => ConcreteValue::Float(*float),
|
|
||||||
ConcreteValue::Integer(integer) => ConcreteValue::Integer(*integer),
|
|
||||||
ConcreteValue::List(list) => ConcreteValue::List(list.clone()),
|
|
||||||
ConcreteValue::Range(range) => ConcreteValue::Range(*range),
|
|
||||||
ConcreteValue::String(string) => ConcreteValue::String(string.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ConcreteValue {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
ConcreteValue::Boolean(boolean) => write!(f, "{boolean}"),
|
|
||||||
ConcreteValue::Byte(byte) => write!(f, "0x{byte:02x}"),
|
|
||||||
ConcreteValue::Character(character) => write!(f, "{character}"),
|
|
||||||
ConcreteValue::Float(float) => {
|
|
||||||
write!(f, "{float}")?;
|
|
||||||
|
|
||||||
if float.fract() == 0.0 {
|
|
||||||
write!(f, ".0")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
ConcreteValue::Integer(integer) => write!(f, "{integer}"),
|
|
||||||
ConcreteValue::List(list) => {
|
|
||||||
write!(f, "[")?;
|
|
||||||
|
|
||||||
for (index, item) in list.iter().enumerate() {
|
|
||||||
if index > 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "{item}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "]")
|
|
||||||
}
|
|
||||||
ConcreteValue::Range(range_value) => {
|
|
||||||
write!(f, "{range_value}")
|
|
||||||
}
|
|
||||||
ConcreteValue::String(string) => write!(f, "{string}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +1,54 @@
|
|||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AbstractList, ConcreteValue, Instruction, Operand, Type, Value,
|
Instruction, Operand, Type, Value,
|
||||||
instruction::{
|
instruction::{
|
||||||
Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadConstant,
|
Add, Call, CallNative, Close, Divide, Equal, InstructionBuilder, Jump, Less, LessEqual,
|
||||||
LoadEncoded, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
|
LoadConstant, LoadEncoded, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not,
|
||||||
Return, SetLocal, Subtract, Test, TestSet, TypeCode,
|
Point, Return, Subtract, Test, TestSet, TypeCode,
|
||||||
},
|
},
|
||||||
vm::CallFrame,
|
vm::CallFrame,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Pointer, Register, thread::ThreadData};
|
use super::{Register, Thread, call_frame::Pointer};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
pub struct ActionSequence {
|
||||||
pub struct Action {
|
pub actions: Vec<Action>,
|
||||||
pub logic: RunnerLogic,
|
|
||||||
pub instruction: Instruction,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Action {
|
impl ActionSequence {
|
||||||
fn from(instruction: Instruction) -> Self {
|
pub fn new<'a, T: IntoIterator<Item = &'a Instruction>>(instructions: T) -> Self {
|
||||||
let operation = instruction.operation();
|
let actions = instructions.into_iter().map(Action::from).collect();
|
||||||
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
|
||||||
|
Self { actions }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Action {
|
||||||
|
pub logic: ActionLogic,
|
||||||
|
pub instruction: InstructionBuilder,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Instruction> for Action {
|
||||||
|
fn from(instruction: &Instruction) -> Self {
|
||||||
|
let instruction = InstructionBuilder::from(instruction);
|
||||||
|
let logic = RUNNER_LOGIC_TABLE[instruction.operation.0 as usize];
|
||||||
|
|
||||||
Action { logic, instruction }
|
Action { logic, instruction }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type RunnerLogic = fn(Instruction, &mut Thread) -> bool;
|
pub type ActionLogic = fn(InstructionBuilder, &mut Thread);
|
||||||
|
|
||||||
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
|
pub const RUNNER_LOGIC_TABLE: [ActionLogic; 23] = [
|
||||||
point,
|
point,
|
||||||
close,
|
close,
|
||||||
load_boolean,
|
load_encoded,
|
||||||
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,
|
||||||
@ -57,793 +67,81 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
|
|||||||
r#return,
|
r#return,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub(crate) fn get_next_action(data: &mut ThreadData) -> Action {
|
pub fn point(instruction: InstructionBuilder, thread: &mut Thread) {
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
let destination = instruction.a_field as usize;
|
||||||
let instruction = current_call.chunk.instructions[current_call.ip];
|
let to = instruction.b_field as usize;
|
||||||
let operation = instruction.operation();
|
let r#type = instruction.b_type;
|
||||||
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
match r#type {
|
||||||
|
TypeCode::BOOLEAN => {
|
||||||
Action { logic, instruction }
|
thread.set_boolean_register(destination, Register::Pointer(Pointer::Register(to)));
|
||||||
}
|
}
|
||||||
|
TypeCode::BYTE => {
|
||||||
pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool {
|
thread.set_byte_register(destination, Register::Pointer(Pointer::Register(to)));
|
||||||
let Point { from, to } = instruction.into();
|
}
|
||||||
let from_register = data.get_register_unchecked(from);
|
TypeCode::CHARACTER => {
|
||||||
let from_register_is_empty = matches!(from_register, Register::Empty);
|
thread.set_character_register(destination, Register::Pointer(Pointer::Register(to)));
|
||||||
|
}
|
||||||
if !from_register_is_empty {
|
TypeCode::FLOAT => {
|
||||||
let register = Register::Pointer(Pointer::Register(to));
|
thread.set_float_register(destination, Register::Pointer(Pointer::Register(to)));
|
||||||
|
}
|
||||||
data.set_register(from, register);
|
TypeCode::INTEGER => {
|
||||||
|
thread.set_integer_register(destination, Register::Pointer(Pointer::Register(to)));
|
||||||
|
}
|
||||||
|
TypeCode::STRING => {
|
||||||
|
thread.set_string_register(destination, Register::Pointer(Pointer::Register(to)));
|
||||||
|
}
|
||||||
|
unsupported => unsupported.panic_from_unsupported_code(),
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn close(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let Close { from, to } = instruction.into();
|
|
||||||
|
|
||||||
for register_index in from..to {
|
pub fn load_encoded(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
data.set_register(register_index, Register::Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
pub fn load_constant(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
|
|
||||||
false
|
pub fn load_list(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn load_function(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let LoadEncoded {
|
|
||||||
destination,
|
|
||||||
value,
|
|
||||||
jump_next,
|
|
||||||
} = instruction.into();
|
|
||||||
let boolean = Value::Concrete(ConcreteValue::Boolean(value));
|
|
||||||
let register = Register::Value(boolean);
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
pub fn load_self(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
|
|
||||||
if jump_next {
|
pub fn get_local(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
pub fn set_local(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
pub fn add(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
|
|
||||||
false
|
pub fn subtract(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn multiply(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
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}");
|
pub fn divide(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
|
|
||||||
data.set_register(destination, register);
|
pub fn modulo(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
|
|
||||||
if jump_next {
|
pub fn test(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
pub fn test_set(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
pub fn equal(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
|
|
||||||
false
|
pub fn less(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
|
pub fn less_equal(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
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 {
|
pub fn negate(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
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);
|
pub fn not(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
|
|
||||||
item_pointers.push(pointer);
|
pub fn jump(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
}
|
|
||||||
|
|
||||||
let list_value = Value::AbstractList(AbstractList {
|
pub fn call(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
item_type,
|
|
||||||
item_pointers,
|
|
||||||
});
|
|
||||||
let register = Register::Value(list_value);
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
pub fn call_native(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
pub fn r#return(instruction: InstructionBuilder, thread: &mut Thread) {}
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> 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: Instruction, data: &mut ThreadData) -> 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: Instruction, data: &mut ThreadData) -> 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: Instruction, data: &mut ThreadData) -> 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: Instruction, data: &mut ThreadData) -> 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: Instruction, data: &mut ThreadData) -> 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: Instruction, data: &mut ThreadData) -> 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: Instruction, data: &mut ThreadData) -> 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: Instruction, data: &mut ThreadData) -> 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: Instruction, data: &mut ThreadData) -> 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 {
|
|
||||||
panic!("VM Error: Expected boolean value for TEST operation",);
|
|
||||||
};
|
|
||||||
|
|
||||||
if boolean == test_value {
|
|
||||||
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: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
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 {
|
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
(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
|
|
||||||
}
|
|
||||||
(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
|
|
||||||
}
|
|
||||||
(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
|
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot compare values"),
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_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 less(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
(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 == comparator {
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
(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: Instruction, data: &mut ThreadData) -> 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: Instruction, data: &mut ThreadData) -> 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: Instruction, data: &mut ThreadData) -> 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: Instruction, data: &mut ThreadData) -> 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: Instruction, data: &mut ThreadData) -> 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: Instruction, data: &mut ThreadData) -> 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 {
|
||||||
@ -852,15 +150,14 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const ALL_OPERATIONS: [(Operation, RunnerLogic); 24] = [
|
const ALL_OPERATIONS: [(Operation, ActionLogic); 23] = [
|
||||||
(Operation::POINT, point),
|
(Operation::POINT, point),
|
||||||
(Operation::CLOSE, close),
|
(Operation::CLOSE, close),
|
||||||
(Operation::LOAD_ENCODED, load_boolean),
|
(Operation::LOAD_ENCODED, load_encoded),
|
||||||
(Operation::LOAD_CONSTANT, load_constant),
|
(Operation::LOAD_CONSTANT, load_constant),
|
||||||
(Operation::LOAD_LIST, load_list),
|
(Operation::LOAD_LIST, load_list),
|
||||||
|
(Operation::LOAD_FUNCTION, load_function),
|
||||||
(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),
|
||||||
|
@ -5,7 +5,7 @@ use std::{
|
|||||||
|
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
|
|
||||||
use crate::{Chunk, DustString};
|
use crate::{Chunk, DustString, instruction::TypeCode};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CallFrame {
|
pub struct CallFrame {
|
||||||
@ -69,5 +69,11 @@ impl RegisterTable {
|
|||||||
pub enum Register<T> {
|
pub enum Register<T> {
|
||||||
Empty,
|
Empty,
|
||||||
Value(T),
|
Value(T),
|
||||||
Pointer(*const T),
|
Pointer(Pointer),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum Pointer {
|
||||||
|
Register(usize),
|
||||||
|
Constant(usize),
|
||||||
}
|
}
|
||||||
|
@ -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, Register, RegisterTable};
|
pub use call_frame::{CallFrame, Register, RegisterTable};
|
||||||
pub use thread::Thread;
|
pub use thread::Thread;
|
||||||
|
|
||||||
@ -50,7 +49,7 @@ impl Vm {
|
|||||||
Builder::new()
|
Builder::new()
|
||||||
.name(thread_name)
|
.name(thread_name)
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
let mut main_thread = Thread::new(main_chunk);
|
let main_thread = Thread::new(main_chunk);
|
||||||
let value_option = main_thread.run();
|
let value_option = main_thread.run();
|
||||||
let _ = tx.send(value_option);
|
let _ = tx.send(value_option);
|
||||||
})
|
})
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
use std::{sync::Arc, thread::JoinHandle};
|
use std::{sync::Arc, thread::JoinHandle};
|
||||||
|
|
||||||
use tracing::{info, trace};
|
use tracing::{info, span};
|
||||||
|
|
||||||
use crate::{Chunk, DustString, Span, Value, vm::CallFrame};
|
use crate::{
|
||||||
|
Chunk, DustString, Span, Value,
|
||||||
|
vm::{CallFrame, action::ActionSequence},
|
||||||
|
};
|
||||||
|
|
||||||
use super::{Action, Register};
|
use super::{Register, call_frame::Pointer};
|
||||||
|
|
||||||
pub struct Thread {
|
pub struct Thread {
|
||||||
chunk: Arc<Chunk>,
|
chunk: Arc<Chunk>,
|
||||||
call_stack: Vec<CallFrame>,
|
call_stack: Vec<CallFrame>,
|
||||||
next_action: Action,
|
return_value: Option<Option<Value>>,
|
||||||
return_value: Option<Value>,
|
|
||||||
spawned_threads: Vec<JoinHandle<()>>,
|
spawned_threads: Vec<JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,18 +24,18 @@ impl Thread {
|
|||||||
|
|
||||||
call_stack.push(main_call);
|
call_stack.push(main_call);
|
||||||
|
|
||||||
let first_action = Action::from(*chunk.instructions.first().unwrap());
|
|
||||||
|
|
||||||
Thread {
|
Thread {
|
||||||
chunk,
|
chunk,
|
||||||
call_stack,
|
call_stack,
|
||||||
next_action: first_action,
|
|
||||||
return_value: None,
|
return_value: None,
|
||||||
spawned_threads: Vec::new(),
|
spawned_threads: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) -> Option<Value> {
|
pub fn run(mut self) -> Option<Value> {
|
||||||
|
let span = span!(tracing::Level::INFO, "Thread");
|
||||||
|
let _ = span.enter();
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Starting thread with {}",
|
"Starting thread with {}",
|
||||||
self.chunk
|
self.chunk
|
||||||
@ -42,17 +44,20 @@ impl Thread {
|
|||||||
.unwrap_or_else(|| DustString::from("anonymous"))
|
.unwrap_or_else(|| DustString::from("anonymous"))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let actions = ActionSequence::new(&self.chunk.instructions);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
trace!("Instruction: {}", self.next_action.instruction);
|
let ip = self.current_frame().ip;
|
||||||
|
let next_action = if cfg!(debug_assertions) {
|
||||||
|
actions.actions.get(ip).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { actions.actions.get_unchecked(ip) }
|
||||||
|
};
|
||||||
|
|
||||||
let should_end = (self.next_action.logic)(self.next_action.instruction, self);
|
(next_action.logic)(next_action.instruction, &mut self);
|
||||||
|
|
||||||
if should_end {
|
if let Some(return_value) = self.return_value {
|
||||||
self.spawned_threads.into_iter().for_each(|join_handle| {
|
return return_value;
|
||||||
let _ = join_handle.join();
|
|
||||||
});
|
|
||||||
|
|
||||||
return self.return_value.take();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,7 +110,7 @@ impl Thread {
|
|||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(boolean) => *boolean,
|
Register::Value(boolean) => *boolean,
|
||||||
Register::Pointer(pointer) => unsafe { **pointer },
|
Register::Pointer(pointer) => self.follow_pointer_to_boolean(*pointer),
|
||||||
Register::Empty => panic!("Attempted to get a boolean from an empty register"),
|
Register::Empty => panic!("Attempted to get a boolean from an empty register"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,7 +152,7 @@ impl Thread {
|
|||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(byte) => *byte,
|
Register::Value(byte) => *byte,
|
||||||
Register::Pointer(pointer) => unsafe { **pointer },
|
Register::Pointer(pointer) => self.follow_pointer_to_byte(*pointer),
|
||||||
Register::Empty => panic!("Attempted to get a byte from an empty register"),
|
Register::Empty => panic!("Attempted to get a byte from an empty register"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,7 +194,7 @@ impl Thread {
|
|||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(character) => *character,
|
Register::Value(character) => *character,
|
||||||
Register::Pointer(pointer) => unsafe { **pointer },
|
Register::Pointer(pointer) => self.follow_pointer_to_character(*pointer),
|
||||||
Register::Empty => panic!("Attempted to get a character from an empty register"),
|
Register::Empty => panic!("Attempted to get a character from an empty register"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,11 +236,30 @@ impl Thread {
|
|||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(float) => *float,
|
Register::Value(float) => *float,
|
||||||
Register::Pointer(pointer) => unsafe { **pointer },
|
Register::Pointer(pointer) => self.follow_pointer_to_float(*pointer),
|
||||||
Register::Empty => panic!("Attempted to get a float from an empty register"),
|
Register::Empty => panic!("Attempted to get a float from an empty register"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_float_register(&mut self, index: usize, new_register: Register<f64>) {
|
||||||
|
let old_register = if cfg!(debug_assertions) {
|
||||||
|
self.current_frame_mut()
|
||||||
|
.registers
|
||||||
|
.floats
|
||||||
|
.get_mut(index as usize)
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
self.current_frame_mut()
|
||||||
|
.registers
|
||||||
|
.floats
|
||||||
|
.get_unchecked_mut(index as usize)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
*old_register = new_register;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_integer_register(&self, index: usize) -> i64 {
|
pub fn get_integer_register(&self, index: usize) -> i64 {
|
||||||
let register = if cfg!(debug_assertions) {
|
let register = if cfg!(debug_assertions) {
|
||||||
self.current_frame()
|
self.current_frame()
|
||||||
@ -254,7 +278,7 @@ impl Thread {
|
|||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(integer) => *integer,
|
Register::Value(integer) => *integer,
|
||||||
Register::Pointer(pointer) => unsafe { **pointer },
|
Register::Pointer(pointer) => self.follow_pointer_to_integer(*pointer),
|
||||||
Register::Empty => panic!("Attempted to get an integer from an empty register"),
|
Register::Empty => panic!("Attempted to get an integer from an empty register"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -296,13 +320,7 @@ impl Thread {
|
|||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(string) => string,
|
Register::Value(string) => string,
|
||||||
Register::Pointer(pointer) => {
|
Register::Pointer(pointer) => self.follow_pointer_to_string(*pointer),
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
unsafe { pointer.as_ref().unwrap() }
|
|
||||||
} else {
|
|
||||||
unsafe { pointer.as_ref().unwrap_unchecked() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Register::Empty => panic!("Attempted to get a string from an empty register"),
|
Register::Empty => panic!("Attempted to get a string from an empty register"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -325,4 +343,108 @@ impl Thread {
|
|||||||
|
|
||||||
*old_register = new_register;
|
*old_register = new_register;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn follow_pointer_to_boolean(&self, pointer: Pointer) -> bool {
|
||||||
|
match pointer {
|
||||||
|
Pointer::Register(register_index) => self.get_boolean_register(register_index),
|
||||||
|
Pointer::Constant(_) => {
|
||||||
|
panic!("Attempted to access boolean from a constant pointer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn follow_pointer_to_byte(&self, pointer: Pointer) -> u8 {
|
||||||
|
match pointer {
|
||||||
|
Pointer::Register(register_index) => self.get_byte_register(register_index),
|
||||||
|
Pointer::Constant(_) => {
|
||||||
|
panic!("Attempted to access byte from a constant pointer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn follow_pointer_to_character(&self, pointer: Pointer) -> char {
|
||||||
|
match pointer {
|
||||||
|
Pointer::Register(register_index) => self.get_character_register(register_index),
|
||||||
|
Pointer::Constant(constant_index) => {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
*self
|
||||||
|
.chunk
|
||||||
|
.constant_characters
|
||||||
|
.get(constant_index as usize)
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
*self
|
||||||
|
.chunk
|
||||||
|
.constant_characters
|
||||||
|
.get_unchecked(constant_index as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn follow_pointer_to_float(&self, pointer: Pointer) -> f64 {
|
||||||
|
match pointer {
|
||||||
|
Pointer::Register(register_index) => self.get_float_register(register_index),
|
||||||
|
Pointer::Constant(constant_index) => {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
*self
|
||||||
|
.chunk
|
||||||
|
.constant_floats
|
||||||
|
.get(constant_index as usize)
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
*self
|
||||||
|
.chunk
|
||||||
|
.constant_floats
|
||||||
|
.get_unchecked(constant_index as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn follow_pointer_to_integer(&self, pointer: Pointer) -> i64 {
|
||||||
|
match pointer {
|
||||||
|
Pointer::Register(register_index) => self.get_integer_register(register_index),
|
||||||
|
Pointer::Constant(constant_index) => {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
*self
|
||||||
|
.chunk
|
||||||
|
.constant_integers
|
||||||
|
.get(constant_index as usize)
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
*self
|
||||||
|
.chunk
|
||||||
|
.constant_integers
|
||||||
|
.get_unchecked(constant_index as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn follow_pointer_to_string(&self, pointer: Pointer) -> &DustString {
|
||||||
|
match pointer {
|
||||||
|
Pointer::Register(register_index) => self.get_string_register(register_index),
|
||||||
|
Pointer::Constant(constant_index) => {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.chunk
|
||||||
|
.constant_strings
|
||||||
|
.get(constant_index as usize)
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
self.chunk
|
||||||
|
.constant_strings
|
||||||
|
.get_unchecked(constant_index as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,10 @@ fn true_and_true() {
|
|||||||
return_type: Type::Boolean,
|
return_type: Type::Boolean,
|
||||||
},
|
},
|
||||||
smallvec![
|
smallvec![
|
||||||
Instruction::load_boolean(0, true, false),
|
Instruction::load_encoded(0, true, false),
|
||||||
Instruction::test(0, true),
|
Instruction::test(0, true),
|
||||||
Instruction::jump(1, true),
|
Instruction::jump(1, true),
|
||||||
Instruction::load_boolean(1, true, false),
|
Instruction::load_encoded(1, true, false),
|
||||||
Instruction::r#return(true),
|
Instruction::r#return(true),
|
||||||
],
|
],
|
||||||
smallvec![
|
smallvec![
|
||||||
@ -51,10 +51,10 @@ fn false_and_false() {
|
|||||||
return_type: Type::Boolean,
|
return_type: Type::Boolean,
|
||||||
},
|
},
|
||||||
smallvec![
|
smallvec![
|
||||||
Instruction::load_boolean(0, false, false),
|
Instruction::load_encoded(0, false, false),
|
||||||
Instruction::test(0, true),
|
Instruction::test(0, true),
|
||||||
Instruction::jump(1, true),
|
Instruction::jump(1, true),
|
||||||
Instruction::load_boolean(1, false, false),
|
Instruction::load_encoded(1, false, false),
|
||||||
Instruction::r#return(true),
|
Instruction::r#return(true),
|
||||||
],
|
],
|
||||||
smallvec![
|
smallvec![
|
||||||
@ -87,10 +87,10 @@ fn false_and_true() {
|
|||||||
return_type: Type::Boolean,
|
return_type: Type::Boolean,
|
||||||
},
|
},
|
||||||
smallvec![
|
smallvec![
|
||||||
Instruction::load_boolean(0, false, false),
|
Instruction::load_encoded(0, false, false),
|
||||||
Instruction::test(0, true),
|
Instruction::test(0, true),
|
||||||
Instruction::jump(1, true),
|
Instruction::jump(1, true),
|
||||||
Instruction::load_boolean(1, true, false),
|
Instruction::load_encoded(1, true, false),
|
||||||
Instruction::r#return(true),
|
Instruction::r#return(true),
|
||||||
],
|
],
|
||||||
smallvec![
|
smallvec![
|
||||||
@ -123,10 +123,10 @@ fn true_and_false() {
|
|||||||
return_type: Type::Boolean,
|
return_type: Type::Boolean,
|
||||||
},
|
},
|
||||||
smallvec![
|
smallvec![
|
||||||
Instruction::load_boolean(0, true, false),
|
Instruction::load_encoded(0, true, false),
|
||||||
Instruction::test(0, true),
|
Instruction::test(0, true),
|
||||||
Instruction::jump(1, true),
|
Instruction::jump(1, true),
|
||||||
Instruction::load_boolean(1, false, false),
|
Instruction::load_encoded(1, false, false),
|
||||||
Instruction::r#return(true),
|
Instruction::r#return(true),
|
||||||
],
|
],
|
||||||
smallvec![
|
smallvec![
|
||||||
|
@ -15,13 +15,13 @@ fn true_and_true_and_true() {
|
|||||||
return_type: Type::Boolean,
|
return_type: Type::Boolean,
|
||||||
},
|
},
|
||||||
smallvec![
|
smallvec![
|
||||||
Instruction::load_boolean(0, true, false),
|
Instruction::load_encoded(0, true, false),
|
||||||
Instruction::test(0, true),
|
Instruction::test(0, true),
|
||||||
Instruction::jump(1, true),
|
Instruction::jump(1, true),
|
||||||
Instruction::load_boolean(1, true, false),
|
Instruction::load_encoded(1, true, false),
|
||||||
Instruction::test(1, true),
|
Instruction::test(1, true),
|
||||||
Instruction::jump(1, true),
|
Instruction::jump(1, true),
|
||||||
Instruction::load_boolean(2, true, false),
|
Instruction::load_encoded(2, true, false),
|
||||||
Instruction::r#return(true),
|
Instruction::r#return(true),
|
||||||
],
|
],
|
||||||
smallvec![
|
smallvec![
|
||||||
|
@ -15,10 +15,10 @@ fn true_or_false() {
|
|||||||
return_type: Type::Boolean,
|
return_type: Type::Boolean,
|
||||||
},
|
},
|
||||||
smallvec![
|
smallvec![
|
||||||
Instruction::load_boolean(0, true, false),
|
Instruction::load_encoded(0, true, false),
|
||||||
Instruction::test(0, false),
|
Instruction::test(0, false),
|
||||||
Instruction::jump(1, true),
|
Instruction::jump(1, true),
|
||||||
Instruction::load_boolean(1, false, false),
|
Instruction::load_encoded(1, false, false),
|
||||||
Instruction::r#return(true),
|
Instruction::r#return(true),
|
||||||
],
|
],
|
||||||
smallvec![
|
smallvec![
|
||||||
|
@ -39,7 +39,7 @@ fn not() {
|
|||||||
return_type: Type::Boolean,
|
return_type: Type::Boolean,
|
||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_boolean(0, true, false), Span(1, 5)),
|
(Instruction::load_encoded(0, true, false), Span(1, 5)),
|
||||||
(Instruction::not(1, Operand::Register(0)), Span(0, 1)),
|
(Instruction::not(1, Operand::Register(0)), Span(0, 1)),
|
||||||
(Instruction::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user