1
0

Fix compiler errors; Continue value overhaul

This commit is contained in:
Jeff 2025-02-01 12:17:05 -05:00
parent d3ec85f9ad
commit 151cfe7095
28 changed files with 887 additions and 1860 deletions

View File

@ -43,7 +43,7 @@ use std::io::{self, Write};
use colored::{ColoredString, Colorize};
use crate::{Chunk, Local};
use crate::{Chunk, Local, Type};
const INSTRUCTION_COLUMNS: [(&str, usize); 4] =
[("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 INDENTATION: &str = "";
@ -314,6 +314,7 @@ impl<'a, W: Write> Disassembler<'a, W> {
Local {
identifier_index,
register_index,
r#type,
scope,
is_mutable,
},
@ -321,7 +322,7 @@ impl<'a, W: Write> Disassembler<'a, W> {
{
let identifier_display = self
.chunk
.constants
.constant_strings
.get(*identifier_index as usize)
.map(|value| value.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(CONSTANT_BORDERS[1])?;
for (index, value) in self.chunk.constants.iter().enumerate() {
let type_display = value.r#type().to_string();
for (index, value) in self.chunk.constant_characters.iter().enumerate() {
let index_display = format!("C_CHAR_{index}");
let type_display = Type::Character.to_string();
let value_display = {
let mut value_string = value.to_string();
@ -363,7 +365,46 @@ impl<'a, W: Write> Disassembler<'a, W> {
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)?;
}
@ -415,7 +456,10 @@ impl<'a, W: Write> Disassembler<'a, W> {
let info_line = format!(
"{} instructions, {} constants, {} locals, returns {}",
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.r#type.return_type
);
@ -431,7 +475,11 @@ impl<'a, W: Write> Disassembler<'a, W> {
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()?;
}

View File

@ -2,7 +2,7 @@
use serde::{Deserialize, Serialize};
use crate::Scope;
use crate::{Scope, Type};
/// Scoped variable.
#[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.
pub register_index: u16,
/// The variable's type.
pub r#type: Type,
/// Whether the local is mutable.
pub is_mutable: bool,
@ -22,10 +25,17 @@ pub struct Local {
impl Local {
/// 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 {
identifier_index,
register_index,
r#type,
is_mutable,
scope,
}

View File

@ -49,6 +49,8 @@ pub struct Chunk {
pub(crate) prototypes: Vec<Arc<Chunk>>,
pub(crate) argument_lists: Vec<Vec<u16>>,
pub(crate) register_count: usize,
pub(crate) prototype_index: u16,
}

File diff suppressed because it is too large Load Diff

View File

@ -28,8 +28,8 @@ use crate::{Compiler, Instruction, Operation};
/// The instructions must be in the following order:
/// - `TEST` or any of the `EQUAL`, `LESS` or `LESS_EQUAL` instructions
/// - `JUMP`
/// - `LOAD_BOOLEAN` or `LOAD_CONSTANT`
/// - `LOAD_BOOLEAN` or `LOAD_CONSTANT`
/// - `LOAD_ENCODED`
/// - `LOAD_ENCODED`
///
/// This optimization was taken from `A No-Frills Introduction to Lua 5.1 VM Instructions` by
/// Kein-Hong Man.
@ -39,8 +39,8 @@ pub fn control_flow_register_consolidation(compiler: &mut Compiler) {
Some([
Operation::TEST | Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL,
Operation::JUMP,
Operation::LOAD_ENCODED | Operation::LOAD_CONSTANT,
Operation::LOAD_ENCODED | Operation::LOAD_CONSTANT,
Operation::LOAD_ENCODED,
Operation::LOAD_ENCODED,
])
) {
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, _, _) = &mut compiler.instructions.get_mut(first_loader_index).unwrap();
let first_loader_destination = first_loader.a_field();
*first_loader =
Instruction::load_boolean(first_loader.a_field(), first_loader.b_field() != 0, true);
*first_loader = Instruction::load_encoded(
first_loader.a_field(),
first_loader.b_field(),
first_loader.b_type(),
true,
);
let second_loader_index = compiler.instructions.len() - 1;
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,
second_loader.b_field() != 0,
second_loader.b_field(),
second_loader.b_type(),
false,
);
}

View File

@ -7,18 +7,19 @@ use super::InstructionBuilder;
pub struct CallNative {
pub destination: u16,
pub function: NativeFunction,
pub argument_count: u16,
pub argument_list_index: u16,
}
impl From<Instruction> for CallNative {
fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field();
let function = NativeFunction::from(instruction.b_field());
let argument_list_index = instruction.c_field();
CallNative {
destination,
function,
argument_count: instruction.c_field(),
argument_list_index,
}
}
}
@ -28,7 +29,7 @@ impl From<CallNative> for Instruction {
let operation = Operation::CALL_NATIVE;
let a_field = call_native.destination;
let b_field = call_native.function as u16;
let c_field = call_native.argument_count;
let c_field = call_native.argument_list_index;
InstructionBuilder {
operation,
@ -46,21 +47,9 @@ impl Display for CallNative {
let CallNative {
destination,
function,
argument_count,
..
} = self;
let arguments_start = destination.saturating_sub(*argument_count);
let arguments_end = arguments_start + argument_count;
if function.returns_value() {
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})"),
}
write!(f, "R{destination} = {function}(..)")
}
}

View File

@ -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}")
}
}

View File

@ -99,7 +99,6 @@ mod call_native;
mod close;
mod divide;
mod equal;
mod get_local;
mod jump;
mod less;
mod less_equal;
@ -115,7 +114,6 @@ mod not;
mod operation;
mod point;
mod r#return;
mod set_local;
mod subtract;
mod test;
mod test_set;
@ -127,7 +125,6 @@ pub use call_native::CallNative;
pub use close::Close;
pub use divide::Divide;
pub use equal::Equal;
pub use get_local::GetLocal;
pub use jump::Jump;
pub use less::Less;
pub use less_equal::LessEqual;
@ -143,7 +140,6 @@ pub use not::Not;
pub use operation::Operation;
pub use point::Point;
pub use r#return::Return;
pub use set_local::SetLocal;
pub use subtract::Subtract;
pub use test::Test;
pub use test_set::TestSet;
@ -154,6 +150,7 @@ pub use type_code::TypeCode;
use crate::NativeFunction;
#[derive(Clone, Copy, Debug)]
pub struct InstructionBuilder {
pub operation: Operation,
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 {
fn default() -> Self {
InstructionBuilder {
@ -259,18 +272,28 @@ impl Instruction {
self.0 = (bits as u64) << 63;
}
pub fn point(from: u16, to: u16) -> Instruction {
Instruction::from(Point { from, to })
pub fn point(destination: u16, to: u16, r#type: TypeCode) -> Instruction {
Instruction::from(Point {
destination,
to,
r#type,
})
}
pub fn close(from: u16, to: u16) -> Instruction {
Instruction::from(Close { from, to })
}
pub fn load_boolean(destination: u16, value: bool, jump_next: bool) -> Instruction {
Instruction::from(LoadBoolean {
pub fn load_encoded(
destination: u16,
value: u16,
r#type: TypeCode,
jump_next: bool,
) -> Instruction {
Instruction::from(LoadEncoded {
destination,
value,
r#type,
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(
destination: u16,
left: Operand,
@ -502,12 +511,12 @@ impl Instruction {
pub fn call_native(
destination: u16,
function: NativeFunction,
argument_count: u16,
argument_list_index: u16,
) -> Instruction {
Instruction::from(CallNative {
destination,
function,
argument_count,
argument_list_index,
})
}
@ -532,7 +541,6 @@ impl Instruction {
Operation::LOAD_ENCODED
| Operation::LOAD_LIST
| Operation::LOAD_SELF
| Operation::GET_LOCAL
| Operation::ADD
| Operation::SUBTRACT
| Operation::MULTIPLY
@ -588,7 +596,6 @@ impl Instruction {
| Operation::LOAD_FUNCTION
| Operation::LOAD_LIST
| Operation::LOAD_SELF
| Operation::GET_LOCAL
| Operation::ADD
| Operation::SUBTRACT
| Operation::MULTIPLY
@ -603,7 +610,6 @@ impl Instruction {
function.returns_value()
}
Operation::CLOSE
| Operation::SET_LOCAL
| Operation::EQUAL
| Operation::LESS
| Operation::LESS_EQUAL
@ -621,13 +627,11 @@ impl Instruction {
match operation {
Operation::POINT => Point::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_FUNCTION => LoadFunction::from(*self).to_string(),
Operation::LOAD_LIST => LoadList::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::SUBTRACT => Subtract::from(*self).to_string(),
Operation::MULTIPLY => Multiply::from(*self).to_string(),

View File

@ -20,37 +20,33 @@ impl Operation {
pub const LOAD_LIST: Operation = Operation(5);
pub const LOAD_SELF: Operation = Operation(6);
// Locals
pub const GET_LOCAL: Operation = Operation(7);
pub const SET_LOCAL: Operation = Operation(8);
// Arithmetic
pub const ADD: Operation = Operation(9);
pub const SUBTRACT: Operation = Operation(10);
pub const MULTIPLY: Operation = Operation(11);
pub const DIVIDE: Operation = Operation(12);
pub const MODULO: Operation = Operation(13);
pub const ADD: Operation = Operation(7);
pub const SUBTRACT: Operation = Operation(8);
pub const MULTIPLY: Operation = Operation(9);
pub const DIVIDE: Operation = Operation(10);
pub const MODULO: Operation = Operation(11);
// Comparison
pub const EQUAL: Operation = Operation(14);
pub const LESS: Operation = Operation(15);
pub const LESS_EQUAL: Operation = Operation(16);
pub const EQUAL: Operation = Operation(12);
pub const LESS: Operation = Operation(13);
pub const LESS_EQUAL: Operation = Operation(14);
// Unary operations
pub const NEGATE: Operation = Operation(17);
pub const NOT: Operation = Operation(18);
pub const NEGATE: Operation = Operation(15);
pub const NOT: Operation = Operation(16);
// Logical operations
pub const TEST: Operation = Operation(19);
pub const TEST_SET: Operation = Operation(20);
pub const TEST: Operation = Operation(17);
pub const TEST_SET: Operation = Operation(18);
// Function calls
pub const CALL: Operation = Operation(21);
pub const CALL_NATIVE: Operation = Operation(22);
pub const CALL: Operation = Operation(19);
pub const CALL_NATIVE: Operation = Operation(20);
// Control flow
pub const JUMP: Operation = Operation(23);
pub const RETURN: Operation = Operation(24);
pub const JUMP: Operation = Operation(21);
pub const RETURN: Operation = Operation(22);
}
impl Operation {
@ -63,8 +59,6 @@ impl Operation {
Self::LOAD_FUNCTION => "LOAD_FUNCTION",
Self::LOAD_LIST => "LOAD_LIST",
Self::LOAD_SELF => "LOAD_SELF",
Self::GET_LOCAL => "GET_LOCAL",
Self::SET_LOCAL => "SET_LOCAL",
Self::ADD => "ADD",
Self::SUBTRACT => "SUBTRACT",
Self::MULTIPLY => "MULTIPLY",

View File

@ -2,18 +2,20 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation};
use super::InstructionBuilder;
use super::{InstructionBuilder, TypeCode};
pub struct Point {
pub from: u16,
pub destination: u16,
pub to: u16,
pub r#type: TypeCode,
}
impl From<Instruction> for Point {
fn from(instruction: Instruction) -> Self {
Point {
from: instruction.b_field(),
to: instruction.c_field(),
destination: instruction.a_field(),
to: instruction.b_field(),
r#type: instruction.b_type(),
}
}
}
@ -21,13 +23,15 @@ impl From<Instruction> for Point {
impl From<Point> for Instruction {
fn from(r#move: Point) -> Self {
let operation = Operation::POINT;
let b_field = r#move.from;
let c_field = r#move.to;
let a_field = r#move.destination;
let b_field = r#move.to;
let b_type = r#move.r#type;
InstructionBuilder {
operation,
a_field,
b_field,
c_field,
b_type,
..Default::default()
}
.build()
@ -36,8 +40,20 @@ impl From<Point> for Instruction {
impl Display for Point {
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(),
}
}
}

View File

@ -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}")
}
}

View File

@ -4,12 +4,13 @@ use std::fmt::Display;
pub struct TypeCode(pub u8);
impl TypeCode {
pub const BOOLEAN: TypeCode = TypeCode(0);
pub const BYTE: TypeCode = TypeCode(1);
pub const CHARACTER: TypeCode = TypeCode(2);
pub const FLOAT: TypeCode = TypeCode(3);
pub const INTEGER: TypeCode = TypeCode(4);
pub const STRING: TypeCode = TypeCode(5);
pub const NONE: TypeCode = TypeCode(0);
pub const BOOLEAN: TypeCode = TypeCode(1);
pub const BYTE: TypeCode = TypeCode(2);
pub const CHARACTER: TypeCode = TypeCode(3);
pub const FLOAT: TypeCode = TypeCode(4);
pub const INTEGER: TypeCode = TypeCode(5);
pub const STRING: TypeCode = TypeCode(6);
pub fn panic_from_unknown_code(self) -> ! {
panic!("Unknown type code: {}", self.0);

View File

@ -1,22 +1,5 @@
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 {
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)
}
pub fn panic(instruction: InstructionBuilder, thread: &mut Thread) {}

View File

@ -2,57 +2,13 @@ use std::io::{Write, stdin, stdout};
use std::ops::Range;
use crate::{
ConcreteValue, Value,
vm::{Register, ThreadData, get_next_action},
Value,
instruction::InstructionBuilder,
vm::{Register, Thread},
};
pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range<u16>) -> bool {
let mut buffer = String::new();
pub fn read_line(instruction: InstructionBuilder, thread: &mut Thread) {}
if stdin().read_line(&mut buffer).is_ok() {
let length = buffer.len();
pub fn write(instruction: InstructionBuilder, thread: &mut Thread) {}
buffer.truncate(length.saturating_sub(1));
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
}
pub fn write_line(instruction: InstructionBuilder, thread: &mut Thread) {}

View File

@ -17,7 +17,9 @@ use std::{
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 {
($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => {
@ -34,13 +36,12 @@ macro_rules! define_native_function {
impl NativeFunction {
pub fn call(
&self,
data: &mut ThreadData,
destination: u16,
argument_range: Range<u16>,
) -> bool {
instruction: InstructionBuilder,
thread: &mut Thread,
) {
match self {
$(
NativeFunction::$name => $function(data, destination, argument_range),
NativeFunction::$name => $function(instruction, thread),
)*
}
}

View File

@ -2,39 +2,6 @@ use std::ops::Range;
use rand::Rng;
use crate::{
Value,
vm::{Register, ThreadData, get_next_action},
};
use crate::{instruction::InstructionBuilder, vm::Thread};
pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
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
}
pub fn random_int(instruction: InstructionBuilder, thread: &mut Thread) {}

View File

@ -1,18 +1,9 @@
use std::ops::Range;
use crate::{
ConcreteValue, Value,
vm::{Register, ThreadData, get_next_action},
Value,
instruction::InstructionBuilder,
vm::{Register, Thread},
};
pub fn to_string(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
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
}
pub fn to_string(instruction: InstructionBuilder, thread: &mut Thread) {}

View File

@ -5,60 +5,6 @@ use std::{
use tracing::{Level, info, span};
use crate::{
DustString,
vm::{Thread, ThreadData, get_next_action},
};
use crate::{DustString, instruction::InstructionBuilder, vm::Thread};
fn start_thread(data: &mut ThreadData, argument_range: Range<u16>) -> JoinHandle<()> {
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
}
pub fn spawn(instruction: InstructionBuilder, thread: &mut Thread) {}

View File

@ -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, "]")
}
}

View File

@ -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}"),
}
}
}

View File

@ -1,44 +1,54 @@
use tracing::trace;
use crate::{
AbstractList, ConcreteValue, Instruction, Operand, Type, Value,
Instruction, Operand, Type, Value,
instruction::{
Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadConstant,
LoadEncoded, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
Return, SetLocal, Subtract, Test, TestSet, TypeCode,
Add, Call, CallNative, Close, Divide, Equal, InstructionBuilder, Jump, Less, LessEqual,
LoadConstant, LoadEncoded, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not,
Point, Return, Subtract, Test, TestSet, TypeCode,
},
vm::CallFrame,
};
use super::{Pointer, Register, thread::ThreadData};
use super::{Register, Thread, call_frame::Pointer};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Action {
pub logic: RunnerLogic,
pub instruction: Instruction,
pub struct ActionSequence {
pub actions: Vec<Action>,
}
impl From<Instruction> for Action {
fn from(instruction: Instruction) -> Self {
let operation = instruction.operation();
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
impl ActionSequence {
pub fn new<'a, T: IntoIterator<Item = &'a Instruction>>(instructions: T) -> Self {
let actions = instructions.into_iter().map(Action::from).collect();
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 }
}
}
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,
close,
load_boolean,
load_encoded,
load_constant,
load_function,
load_list,
load_self,
get_local,
set_local,
add,
subtract,
multiply,
@ -57,793 +67,81 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
r#return,
];
pub(crate) fn get_next_action(data: &mut ThreadData) -> Action {
let current_call = data.call_stack.last_mut_unchecked();
let instruction = current_call.chunk.instructions[current_call.ip];
let operation = instruction.operation();
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
pub fn point(instruction: InstructionBuilder, thread: &mut Thread) {
let destination = instruction.a_field as usize;
let to = instruction.b_field as usize;
let r#type = instruction.b_type;
current_call.ip += 1;
Action { logic, instruction }
match r#type {
TypeCode::BOOLEAN => {
thread.set_boolean_register(destination, Register::Pointer(Pointer::Register(to)));
}
TypeCode::BYTE => {
thread.set_byte_register(destination, Register::Pointer(Pointer::Register(to)));
}
TypeCode::CHARACTER => {
thread.set_character_register(destination, Register::Pointer(Pointer::Register(to)));
}
TypeCode::FLOAT => {
thread.set_float_register(destination, Register::Pointer(Pointer::Register(to)));
}
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(),
}
}
pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool {
let Point { from, to } = instruction.into();
let from_register = data.get_register_unchecked(from);
let from_register_is_empty = matches!(from_register, Register::Empty);
pub fn close(instruction: InstructionBuilder, thread: &mut Thread) {}
if !from_register_is_empty {
let register = Register::Pointer(Pointer::Register(to));
pub fn load_encoded(instruction: InstructionBuilder, thread: &mut Thread) {}
data.set_register(from, register);
}
pub fn load_constant(instruction: InstructionBuilder, thread: &mut Thread) {}
data.next_action = get_next_action(data);
pub fn load_list(instruction: InstructionBuilder, thread: &mut Thread) {}
false
}
pub fn load_function(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn close(instruction: Instruction, data: &mut ThreadData) -> bool {
let Close { from, to } = instruction.into();
pub fn load_self(instruction: InstructionBuilder, thread: &mut Thread) {}
for register_index in from..to {
data.set_register(register_index, Register::Empty);
}
pub fn get_local(instruction: InstructionBuilder, thread: &mut Thread) {}
data.next_action = get_next_action(data);
pub fn set_local(instruction: InstructionBuilder, thread: &mut Thread) {}
false
}
pub fn add(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool {
let LoadEncoded {
destination,
value,
jump_next,
} = instruction.into();
let boolean = Value::Concrete(ConcreteValue::Boolean(value));
let register = Register::Value(boolean);
pub fn subtract(instruction: InstructionBuilder, thread: &mut Thread) {}
data.set_register(destination, register);
pub fn multiply(instruction: InstructionBuilder, thread: &mut Thread) {}
if jump_next {
let current_call = data.call_stack.last_mut_unchecked();
pub fn divide(instruction: InstructionBuilder, thread: &mut Thread) {}
current_call.ip += 1;
}
pub fn modulo(instruction: InstructionBuilder, thread: &mut Thread) {}
data.next_action = get_next_action(data);
pub fn test(instruction: InstructionBuilder, thread: &mut Thread) {}
false
}
pub fn test_set(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool {
let LoadConstant {
destination,
constant_index,
jump_next,
} = instruction.into();
let register = Register::Pointer(Pointer::Constant(constant_index));
pub fn equal(instruction: InstructionBuilder, thread: &mut Thread) {}
trace!("Load constant {constant_index} into R{destination}");
pub fn less(instruction: InstructionBuilder, thread: &mut Thread) {}
data.set_register(destination, register);
pub fn less_equal(instruction: InstructionBuilder, thread: &mut Thread) {}
if jump_next {
let current_call = data.call_stack.last_mut_unchecked();
pub fn negate(instruction: InstructionBuilder, thread: &mut Thread) {}
current_call.ip += 1;
}
pub fn not(instruction: InstructionBuilder, thread: &mut Thread) {}
data.next_action = get_next_action(data);
pub fn jump(instruction: InstructionBuilder, thread: &mut Thread) {}
false
}
pub fn call(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
let LoadList {
destination,
start_register,
jump_next,
} = instruction.into();
let mut item_pointers = Vec::with_capacity((destination - start_register) as usize);
let mut item_type = Type::Any;
pub fn call_native(instruction: InstructionBuilder, thread: &mut Thread) {}
for register_index in start_register..destination {
match data.get_register_unchecked(register_index) {
Register::Empty => continue,
Register::Value(value) => {
if item_type == Type::Any {
item_type = value.r#type();
}
}
Register::Pointer(pointer) => {
if item_type == Type::Any {
item_type = data.follow_pointer_unchecked(*pointer).r#type();
}
}
}
let pointer = Pointer::Register(register_index);
item_pointers.push(pointer);
}
let list_value = Value::AbstractList(AbstractList {
item_type,
item_pointers,
});
let register = Register::Value(list_value);
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn load_function(instruction: 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 = &current_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 = &current_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
}
pub fn r#return(instruction: InstructionBuilder, thread: &mut Thread) {}
#[cfg(test)]
mod tests {
@ -852,15 +150,14 @@ mod tests {
use super::*;
const ALL_OPERATIONS: [(Operation, RunnerLogic); 24] = [
const ALL_OPERATIONS: [(Operation, ActionLogic); 23] = [
(Operation::POINT, point),
(Operation::CLOSE, close),
(Operation::LOAD_ENCODED, load_boolean),
(Operation::LOAD_ENCODED, load_encoded),
(Operation::LOAD_CONSTANT, load_constant),
(Operation::LOAD_LIST, load_list),
(Operation::LOAD_FUNCTION, load_function),
(Operation::LOAD_SELF, load_self),
(Operation::GET_LOCAL, get_local),
(Operation::SET_LOCAL, set_local),
(Operation::ADD, add),
(Operation::SUBTRACT, subtract),
(Operation::MULTIPLY, multiply),

View File

@ -5,7 +5,7 @@ use std::{
use smallvec::{SmallVec, smallvec};
use crate::{Chunk, DustString};
use crate::{Chunk, DustString, instruction::TypeCode};
#[derive(Debug)]
pub struct CallFrame {
@ -69,5 +69,11 @@ impl RegisterTable {
pub enum Register<T> {
Empty,
Value(T),
Pointer(*const T),
Pointer(Pointer),
}
#[derive(Debug, Clone, Copy)]
pub enum Pointer {
Register(usize),
Constant(usize),
}

View File

@ -10,7 +10,6 @@ use std::{
};
pub use action::Action;
pub(crate) use action::get_next_action;
pub use call_frame::{CallFrame, Register, RegisterTable};
pub use thread::Thread;
@ -50,7 +49,7 @@ impl Vm {
Builder::new()
.name(thread_name)
.spawn(move || {
let mut main_thread = Thread::new(main_chunk);
let main_thread = Thread::new(main_chunk);
let value_option = main_thread.run();
let _ = tx.send(value_option);
})

View File

@ -1,16 +1,18 @@
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 {
chunk: Arc<Chunk>,
call_stack: Vec<CallFrame>,
next_action: Action,
return_value: Option<Value>,
return_value: Option<Option<Value>>,
spawned_threads: Vec<JoinHandle<()>>,
}
@ -22,18 +24,18 @@ impl Thread {
call_stack.push(main_call);
let first_action = Action::from(*chunk.instructions.first().unwrap());
Thread {
chunk,
call_stack,
next_action: first_action,
return_value: None,
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!(
"Starting thread with {}",
self.chunk
@ -42,17 +44,20 @@ impl Thread {
.unwrap_or_else(|| DustString::from("anonymous"))
);
let actions = ActionSequence::new(&self.chunk.instructions);
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 {
self.spawned_threads.into_iter().for_each(|join_handle| {
let _ = join_handle.join();
});
return self.return_value.take();
if let Some(return_value) = self.return_value {
return return_value;
}
}
}
@ -105,7 +110,7 @@ impl Thread {
match register {
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"),
}
}
@ -147,7 +152,7 @@ impl Thread {
match register {
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"),
}
}
@ -189,7 +194,7 @@ impl Thread {
match register {
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"),
}
}
@ -231,11 +236,30 @@ impl Thread {
match register {
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"),
}
}
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 {
let register = if cfg!(debug_assertions) {
self.current_frame()
@ -254,7 +278,7 @@ impl Thread {
match register {
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"),
}
}
@ -296,13 +320,7 @@ impl Thread {
match register {
Register::Value(string) => string,
Register::Pointer(pointer) => {
if cfg!(debug_assertions) {
unsafe { pointer.as_ref().unwrap() }
} else {
unsafe { pointer.as_ref().unwrap_unchecked() }
}
}
Register::Pointer(pointer) => self.follow_pointer_to_string(*pointer),
Register::Empty => panic!("Attempted to get a string from an empty register"),
}
}
@ -325,4 +343,108 @@ impl Thread {
*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)
}
}
}
}
}
}

View File

@ -15,10 +15,10 @@ fn true_and_true() {
return_type: Type::Boolean,
},
smallvec![
Instruction::load_boolean(0, true, false),
Instruction::load_encoded(0, true, false),
Instruction::test(0, true),
Instruction::jump(1, true),
Instruction::load_boolean(1, true, false),
Instruction::load_encoded(1, true, false),
Instruction::r#return(true),
],
smallvec![
@ -51,10 +51,10 @@ fn false_and_false() {
return_type: Type::Boolean,
},
smallvec![
Instruction::load_boolean(0, false, false),
Instruction::load_encoded(0, false, false),
Instruction::test(0, true),
Instruction::jump(1, true),
Instruction::load_boolean(1, false, false),
Instruction::load_encoded(1, false, false),
Instruction::r#return(true),
],
smallvec![
@ -87,10 +87,10 @@ fn false_and_true() {
return_type: Type::Boolean,
},
smallvec![
Instruction::load_boolean(0, false, false),
Instruction::load_encoded(0, false, false),
Instruction::test(0, true),
Instruction::jump(1, true),
Instruction::load_boolean(1, true, false),
Instruction::load_encoded(1, true, false),
Instruction::r#return(true),
],
smallvec![
@ -123,10 +123,10 @@ fn true_and_false() {
return_type: Type::Boolean,
},
smallvec![
Instruction::load_boolean(0, true, false),
Instruction::load_encoded(0, true, false),
Instruction::test(0, true),
Instruction::jump(1, true),
Instruction::load_boolean(1, false, false),
Instruction::load_encoded(1, false, false),
Instruction::r#return(true),
],
smallvec![

View File

@ -15,13 +15,13 @@ fn true_and_true_and_true() {
return_type: Type::Boolean,
},
smallvec![
Instruction::load_boolean(0, true, false),
Instruction::load_encoded(0, true, false),
Instruction::test(0, true),
Instruction::jump(1, true),
Instruction::load_boolean(1, true, false),
Instruction::load_encoded(1, true, false),
Instruction::test(1, true),
Instruction::jump(1, true),
Instruction::load_boolean(2, true, false),
Instruction::load_encoded(2, true, false),
Instruction::r#return(true),
],
smallvec![

View File

@ -15,10 +15,10 @@ fn true_or_false() {
return_type: Type::Boolean,
},
smallvec![
Instruction::load_boolean(0, true, false),
Instruction::load_encoded(0, true, false),
Instruction::test(0, false),
Instruction::jump(1, true),
Instruction::load_boolean(1, false, false),
Instruction::load_encoded(1, false, false),
Instruction::r#return(true),
],
smallvec![

View File

@ -39,7 +39,7 @@ fn not() {
return_type: Type::Boolean,
},
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::r#return(true), Span(5, 5)),
],