1
0

Implement function calls in the VM

This commit is contained in:
Jeff 2025-02-21 05:00:20 -05:00
parent a08a1e560a
commit d5b4278571
29 changed files with 2202 additions and 3744 deletions

View File

@ -65,6 +65,13 @@ const LOCAL_BORDERS: [&str; 3] = [
"╰─────┴────────────────┴──────────────────────────┴────────────┴───────┴───────╯",
];
const ARGUMENT_LIST_COLUMNS: [(&str, usize); 2] = [("i", 5), ("REGISTERS", 21)];
const ARGUMENT_LIST_BORDERS: [&str; 3] = [
"╭─────┬─────────────────────╮",
"├─────┼─────────────────────┤",
"╰─────┴─────────────────────╯",
];
const CONSTANT_COLUMNS: [(&str, usize); 3] = [("i", 5), ("TYPE", 26), ("VALUE", 26)];
const CONSTANT_BORDERS: [&str; 3] = [
"╭─────┬──────────────────────────┬──────────────────────────╮",
@ -326,8 +333,17 @@ impl<'a, W: Write> Disassembler<'a, W> {
.map(|value| value.to_string())
.unwrap_or_else(|| "unknown".to_string());
let type_display = r#type.to_string();
let type_caps = type_display.to_uppercase();
let register_display = format!("R_{type_caps}_{register_index}");
let register_display = match r#type {
Type::Boolean => format!("R_BOOL_{register_index}"),
Type::Byte => format!("R_BYTE_{register_index}"),
Type::Character => format!("R_CHAR_{register_index}"),
Type::Float => format!("R_FLOAT_{register_index}"),
Type::Integer => format!("R_INT_{register_index}"),
Type::String => format!("R_STR_{register_index}"),
Type::List(_) => format!("R_LIST_{register_index}"),
Type::Function(_) => format!("R_FN_{register_index}"),
_ => unreachable!(),
};
let scope = scope.to_string();
let row = format!(
"│{index:^5}│{identifier_display:^16}│{type_display:^26}│{register_display:^12}│{scope:^7}│{is_mutable:^7}│"
@ -423,7 +439,38 @@ impl<'a, W: Write> Disassembler<'a, W> {
Ok(())
}
pub fn write_prototype_section(&mut self) -> Result<(), io::Error> {
fn write_argument_list_section(&mut self) -> Result<(), io::Error> {
let mut column_name_line = String::new();
for (column_name, width) in ARGUMENT_LIST_COLUMNS {
column_name_line.push_str(&format!("{:^width$}", column_name, width = width));
}
column_name_line.push('│');
self.write_center_border_bold("Argument Lists")?;
self.write_center_border(ARGUMENT_LIST_BORDERS[0])?;
self.write_center_border(&column_name_line)?;
self.write_center_border(ARGUMENT_LIST_BORDERS[1])?;
for (index, argument_list) in self.chunk.argument_lists.iter().enumerate() {
let argument_list_display = format!(
"│{index:^5}│{:^21}│",
argument_list
.iter()
.map(|index| index.to_string())
.collect::<Vec<String>>()
.join(", ")
);
self.write_center_border(&argument_list_display)?;
}
self.write_center_border(ARGUMENT_LIST_BORDERS[2])?;
Ok(())
}
fn write_prototype_section(&mut self) -> Result<(), io::Error> {
self.write_center_border_bold("Prototypes")?;
for chunk in &self.chunk.prototypes {
@ -495,6 +542,10 @@ impl<'a, W: Write> Disassembler<'a, W> {
self.write_constant_section()?;
}
if !self.chunk.argument_lists.is_empty() {
self.write_argument_list_section()?;
}
if !self.chunk.prototypes.is_empty() {
self.write_prototype_section()?;
}

View File

@ -42,6 +42,7 @@ pub struct Chunk {
pub string_constants: Vec<DustString>,
pub locals: Vec<Local>,
pub prototypes: Vec<Arc<Chunk>>,
pub argument_lists: Vec<Vec<u16>>,
pub boolean_register_count: u16,
pub byte_register_count: u16,

View File

@ -222,12 +222,6 @@ impl AnnotatedError for CompileError {
fn detail_snippets(&self) -> Vec<(String, Span)> {
match self {
Self::CannotAddType {
argument_type,
position,
} => {
vec![(format!("Cannot add type `{}`", argument_type), *position)]
}
Self::CannotAddArguments {
left_type,
left_position,
@ -242,6 +236,12 @@ impl AnnotatedError for CompileError {
),
]
}
Self::CannotAddType {
argument_type,
position,
} => {
vec![(format!("Cannot add type `{}`", argument_type), *position)]
}
Self::ReturnTypeConflict { conflict, position } => {
vec![(
format!(

View File

@ -107,6 +107,11 @@ pub struct Compiler<'src> {
/// [`Compiler::finish`] is called.
prototypes: Vec<Arc<Chunk>>,
/// Lists of arguments for each function call. The integers represent the register of each
/// argument. Note that the type of each argument is not stored, so the caller must check the
/// function's type to determine the type of each argument.
argument_lists: Vec<Vec<u16>>,
/// The first boolean register index that the compiler should use. This is used to avoid reusing
/// the registers that are used for the function's arguments.
minimum_boolean_register: u16,
@ -179,6 +184,7 @@ impl<'src> Compiler<'src> {
string_constants: Vec::new(),
locals: Vec::new(),
prototypes: Vec::new(),
argument_lists: Vec::new(),
lexer,
minimum_byte_register: 0,
minimum_boolean_register: 0,
@ -212,20 +218,12 @@ impl<'src> Compiler<'src> {
self.current_position.to_string()
);
loop {
while !matches!(self.current_token, Token::Eof | Token::RightBrace) {
self.parse(Precedence::None)?;
if matches!(self.current_token, Token::Eof | Token::RightBrace) {
if self.get_last_operation() == Some(Operation::RETURN) {
break;
}
self.parse_implicit_return()?;
break;
}
}
self.parse_implicit_return()?;
info!("End chunk");
Ok(())
@ -237,6 +235,12 @@ impl<'src> Compiler<'src> {
/// will allow [`Compiler::function_name`] to be both the name used for recursive calls and the
/// name of the function when it is compiled. The name can later be seen in the VM's call stack.
pub fn finish(mut self) -> Chunk {
if self.instructions.is_empty() {
let r#return = Instruction::r#return(false, 0, TypeCode::NONE);
self.emit_instruction(r#return, Type::None, self.current_position);
}
let boolean_register_count = self.next_boolean_register();
let byte_register_count = self.next_byte_register();
let character_register_count = self.next_character_register();
@ -262,6 +266,7 @@ impl<'src> Compiler<'src> {
string_constants: self.string_constants,
locals: self.locals,
prototypes: self.prototypes,
argument_lists: self.argument_lists,
boolean_register_count,
byte_register_count,
character_register_count,
@ -397,7 +402,7 @@ impl<'src> Compiler<'src> {
.iter()
.rev()
.find_map(|(instruction, _, _)| {
if instruction.b_type() == TypeCode::FUNCTION && instruction.yields_value() {
if instruction.operation() == Operation::LOAD_FUNCTION {
Some(instruction.a_field() + 1)
} else {
None
@ -1253,6 +1258,8 @@ impl<'src> Compiler<'src> {
Type::Float => self.next_float_register(),
Type::Integer => self.next_integer_register(),
Type::String => self.next_string_register(),
Type::List(_) => self.next_list_register(),
Type::Function(_) => self.next_function_register(),
_ => todo!(),
};
let point = Instruction::r#move(
@ -1801,26 +1808,21 @@ impl<'src> Compiler<'src> {
let r#return = Instruction::r#return(false, 0, TypeCode::NONE);
self.emit_instruction(r#return, Type::None, self.current_position);
} else {
let (previous_expression_type, previous_register) = self
.instructions
.last()
.map(|(instruction, r#type, _)| {
if instruction.yields_value() {
(r#type.clone(), instruction.a_field())
} else {
(Type::None, 0)
}
})
.ok_or_else(|| CompileError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
} else if let Some((previous_expression_type, previous_destination_register)) =
self.instructions.last().map(|(instruction, r#type, _)| {
if r#type == &Type::None {
(Type::None, 0)
} else if instruction.yields_value() {
(r#type.clone(), instruction.a_field())
} else {
(Type::None, 0)
}
})
{
let should_return_value = previous_expression_type != Type::None;
let r#return = Instruction::r#return(
should_return_value,
previous_register,
previous_destination_register,
previous_expression_type.type_code(),
);
@ -2034,12 +2036,13 @@ impl<'src> Compiler<'src> {
}
let load_function = Instruction::load_function(destination, prototype_index, false);
let r#type = if identifier.is_some() {
Type::None
} else {
Type::Function(function_type)
};
self.emit_instruction(
load_function,
Type::Function(function_type),
Span(function_start, function_end),
);
self.emit_instruction(load_function, r#type, Span(function_start, function_end));
Ok(())
}
@ -2056,19 +2059,8 @@ impl<'src> Compiler<'src> {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
let b_field = last_instruction.b_field();
if !matches!(
last_instruction_type,
Type::Function { .. } | Type::SelfFunction
) {
return Err(CompileError::ExpectedFunction {
found: self.previous_token.to_owned(),
actual_type: last_instruction_type.clone(),
position: self.previous_position,
});
}
let function_register = last_instruction.a_field();
let function_return_type = match last_instruction_type {
Type::Function(function_type) => *function_type.return_type.clone(),
Type::SelfFunction => *self.r#type.return_type.clone(),
@ -2080,17 +2072,43 @@ impl<'src> Compiler<'src> {
});
}
};
let is_recursive = last_instruction_type == &Type::SelfFunction;
let function_register = if last_instruction.operation() == Operation::LOAD_FUNCTION {
last_instruction.a_field()
} else if last_instruction.operation() == Operation::MOVE {
self.instructions.pop();
let mut argument_count = 0;
b_field
} else {
return Err(CompileError::ExpectedFunction {
found: self.previous_token.to_owned(),
actual_type: last_instruction_type.clone(),
position: self.previous_position,
});
};
let mut argument_list = Vec::new();
while !self.allow(Token::RightParenthesis)? {
self.parse_expression()?;
self.allow(Token::Comma)?;
argument_count += 1;
let argument_index = match self.get_last_instruction_type() {
Type::Boolean => self.next_boolean_register() - 1,
Type::Byte => self.next_byte_register() - 1,
Type::Character => self.next_character_register() - 1,
Type::Float => self.next_float_register() - 1,
Type::Integer => self.next_integer_register() - 1,
Type::String => self.next_string_register() - 1,
_ => todo!(),
};
argument_list.push(argument_index);
}
let argument_list_index = self.argument_lists.len() as u16;
self.argument_lists.push(argument_list);
let end = self.current_position.1;
let destination = match function_return_type {
Type::None => 0,
@ -2102,7 +2120,12 @@ impl<'src> Compiler<'src> {
Type::String => self.next_string_register(),
_ => todo!(),
};
let call = Instruction::call(destination, function_register, argument_count, is_recursive);
let call = Instruction::call(
destination,
function_register,
argument_list_index,
function_return_type.type_code(),
);
self.emit_instruction(call, function_return_type, Span(start, end));

View File

@ -4,50 +4,36 @@ use std::fmt::{self, Display, Formatter};
use annotate_snippets::{Level, Renderer, Snippet};
use crate::{CompileError, NativeFunctionError, Span};
use crate::{CompileError, Span};
/// A top-level error that can occur during the interpretation of Dust code.
#[derive(Debug, PartialEq)]
pub enum DustError<'src> {
Compile {
error: CompileError,
source: &'src str,
},
NativeFunction {
error: NativeFunctionError,
source: &'src str,
},
pub struct DustError<'src> {
pub error: CompileError,
pub source: &'src str,
}
impl<'src> DustError<'src> {
pub fn compile(error: CompileError, source: &'src str) -> Self {
DustError::Compile { error, source }
DustError { error, source }
}
pub fn report(&self) -> String {
let (title, description, detail_snippets, help_snippets) = match self {
Self::Compile { error, .. } => (
CompileError::title(),
error.description(),
error.detail_snippets(),
error.help_snippets(),
),
Self::NativeFunction { error, .. } => (
NativeFunctionError::title(),
error.description(),
error.detail_snippets(),
error.help_snippets(),
),
};
let (title, description, detail_snippets, help_snippets) = (
CompileError::title(),
self.error.description(),
self.error.detail_snippets(),
self.error.help_snippets(),
);
let label = format!("{}: {}", title, description);
let message = Level::Error
.title(&label)
.snippets(detail_snippets.iter().map(|(details, position)| {
Snippet::source(self.source())
Snippet::source(self.source)
.annotation(Level::Info.span(position.0..position.1).label(details))
}))
.snippets(help_snippets.iter().map(|(help, position)| {
Snippet::source(self.source())
Snippet::source(self.source)
.annotation(Level::Help.span(position.0..position.1).label(help))
}));
let mut report = String::new();
@ -57,13 +43,6 @@ impl<'src> DustError<'src> {
report
}
fn source(&self) -> &str {
match self {
Self::Compile { source, .. } => source,
Self::NativeFunction { source, .. } => source,
}
}
}
impl Display for DustError<'_> {

View File

@ -2,27 +2,27 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation};
use super::InstructionFields;
use super::{InstructionFields, TypeCode};
pub struct Call {
pub destination: u16,
pub function_register: u16,
pub argument_count: u16,
pub is_recursive: bool,
pub argument_list_index: u16,
pub return_type: TypeCode,
}
impl From<Instruction> for Call {
fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field();
let function_register = instruction.b_field();
let argument_count = instruction.c_field();
let is_recursive = instruction.d_field();
let argument_list_index = instruction.c_field();
let return_type = instruction.b_type();
Call {
destination,
function_register,
argument_count,
is_recursive,
argument_list_index,
return_type,
}
}
}
@ -31,15 +31,15 @@ impl From<Call> for Instruction {
fn from(call: Call) -> Self {
let a_field = call.destination;
let b_field = call.function_register;
let c_field = call.argument_count;
let d_field = call.is_recursive;
let b_type = call.return_type;
let c_field = call.argument_list_index;
InstructionFields {
operation: Operation::CALL,
a_field,
b_field,
b_type,
c_field,
d_field,
..Default::default()
}
.build()
@ -51,23 +51,24 @@ impl Display for Call {
let Call {
destination,
function_register,
argument_count,
argument_list_index,
return_type,
..
} = self;
let arguments_start = destination.saturating_sub(*argument_count);
match argument_count {
0 => write!(f, "R{destination} = R{function_register}()"),
1 => write!(
f,
"R{destination} = R{function_register}(R{arguments_start})"
),
_ => {
write!(
f,
"R{destination} = R{function_register}(R{arguments_start}..R{destination})"
)
}
match *return_type {
TypeCode::NONE => {}
TypeCode::BOOLEAN => write!(f, "R_BOOL_{destination} = ")?,
TypeCode::BYTE => write!(f, "R_BYTE_{destination} = ")?,
TypeCode::CHARACTER => write!(f, "R_CHR_{destination} = ")?,
TypeCode::FLOAT => write!(f, "R_FLT_{destination} = ")?,
TypeCode::INTEGER => write!(f, "R_INT_{destination} = ")?,
TypeCode::STRING => write!(f, "R_STR_{destination} = ")?,
TypeCode::LIST => write!(f, "R_LIST_{destination} = ")?,
TypeCode::FUNCTION => write!(f, "R_FN_{destination} = ")?,
_ => unreachable!(),
}
write!(f, "R_FN_{function_register}({argument_list_index})")
}
}

View File

@ -43,7 +43,7 @@ impl Display for LoadFunction {
jump_next,
} = self;
write!(f, "R{destination} = P{prototype_index}")?;
write!(f, "R_FN_{destination} = P{prototype_index}")?;
if *jump_next {
write!(f, " JUMP +1")?;

View File

@ -544,14 +544,14 @@ impl Instruction {
pub fn call(
destination: u16,
function_register: u16,
argument_count: u16,
is_recursive: bool,
argument_list_register: u16,
return_type: TypeCode,
) -> Instruction {
Instruction::from(Call {
destination,
function_register,
argument_count,
is_recursive,
argument_list_index: argument_list_register,
return_type,
})
}

View File

@ -51,6 +51,8 @@ impl Display for Move {
TypeCode::FLOAT => write!(f, "R_FLOAT_{destination} -> {to}"),
TypeCode::INTEGER => write!(f, "R_INT_{destination} -> {to}"),
TypeCode::STRING => write!(f, "R_STR_{destination} -> {to}"),
TypeCode::LIST => write!(f, "R_LIST_{destination} -> {to}"),
TypeCode::FUNCTION => write!(f, "R_FN_{destination} -> {to}"),
unsupported => write!(
f,
"Unsupported type code: {unsupported} for MOVE instruction"

View File

@ -44,7 +44,7 @@ pub use crate::compiler::{compile, CompileError, Compiler};
pub use crate::dust_error::{AnnotatedError, DustError};
pub use crate::instruction::{Instruction, Operand, Operation};
pub use crate::lexer::{lex, LexError, Lexer};
pub use crate::native_function::{NativeFunction, NativeFunctionError};
pub use crate::native_function::NativeFunction;
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
pub use crate::token::{Token, TokenKind, TokenOwned};
pub use crate::value::{

View File

@ -1,22 +0,0 @@
use std::{ops::Range, panic};
use crate::vm::Thread;
pub fn panic(data: &mut Thread, _: usize, argument_range: Range<usize>) {
let current_frame = data.current_frame();
let position = data.current_position();
let mut message = format!("Dust panic at {position}!");
for register_index in argument_range {
let string = current_frame
.registers
.strings
.get(register_index)
.as_value();
message.push_str(string);
message.push('\n');
}
panic!("{}", message)
}

View File

@ -1,56 +0,0 @@
use std::io::{stdin, stdout, Write};
use std::ops::Range;
use crate::vm::Thread;
use crate::DustString;
pub fn read_line(data: &mut Thread, destination: usize, _argument_range: Range<usize>) {
let current_frame = data.current_frame_mut();
let mut buffer = String::new();
if stdin().read_line(&mut buffer).is_ok() {
let length = buffer.len();
buffer.truncate(length.saturating_sub(1));
let string = DustString::from(buffer);
current_frame
.registers
.strings
.set_to_new_register(destination, string);
}
}
pub fn write(data: &mut Thread, _: usize, argument_range: Range<usize>) {
let current_frame = data.current_frame_mut();
let mut stdout = stdout();
for register_index in argument_range {
let string = current_frame
.registers
.strings
.get(register_index)
.as_value();
let _ = stdout.write(string.as_bytes());
}
let _ = stdout.flush();
}
pub fn write_line(data: &mut Thread, _: usize, argument_range: Range<usize>) {
let current_frame = data.current_frame_mut();
let mut stdout = stdout().lock();
for register_index in argument_range {
let string = current_frame
.registers
.strings
.get(register_index)
.as_value();
let _ = stdout.write(string.as_bytes());
}
let _ = stdout.write(b"\n");
let _ = stdout.flush();
}

View File

@ -2,21 +2,15 @@
//!
//! Native functions are used to implement features that are not possible to implement in Dust
//! itself or that are more efficient to implement in Rust.
mod assert;
mod io;
mod random;
mod thread;
use std::{
fmt::{self, Display, Formatter},
io::ErrorKind as IoErrorKind,
ops::Range,
string::ParseError,
};
use serde::{Deserialize, Serialize};
use crate::{AnnotatedError, FunctionType, Span, Type, vm::Thread};
use crate::{vm::Thread, FunctionType, Type};
macro_rules! define_native_function {
($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => {
@ -86,11 +80,7 @@ macro_rules! define_native_function {
$bytes => NativeFunction::$name,
)*
_ => {
if cfg!(test) {
panic!("Invalid native function byte: {}", bytes)
} else {
NativeFunction::Panic
}
panic!("Invalid native function byte: {}", bytes);
}
}
}
@ -129,13 +119,13 @@ define_native_function! {
// ),
// (AssertEqual, 1_u8, "assert_equal", false),
// (AssertNotEqual, 2_u8, "assert_not_equal", false),
(
Panic,
3,
"panic",
FunctionType::new([], [], Type::None),
assert::panic
),
// (
// Panic,
// 3,
// "panic",
// FunctionType::new([], [], Type::None),
// assert::panic
// ),
// // Type conversion
// (Parse, 4_u8, "parse", true),
@ -199,42 +189,42 @@ define_native_function! {
// // Read
// (Read, 48_u8, "read", true),
// (ReadFile, 49_u8, "read_file", true),
(
ReadLine,
50,
"read_line",
FunctionType::new([], [], Type::String),
io::read_line
),
// (
// ReadLine,
// 50,
// "read_line",
// FunctionType::new([], [], Type::String),
// io::read_line
// ),
// (ReadTo, 51_u8, "read_to", false),
// (ReadUntil, 52_u8, "read_until", true),
// // Write
// (AppendFile, 53_u8, "append_file", false),
// (PrependFile, 54_u8, "prepend_file", false),
(
Write,
55,
"write",
FunctionType::new([], [Type::String], Type::None),
io::write
),
// (
// Write,
// 55,
// "write",
// FunctionType::new([], [Type::String], Type::None),
// io::write
// ),
// (WriteFile, 56_u8, "write_file", false),
(
WriteLine,
57,
"write_line",
FunctionType::new([], [Type::String], Type::None),
io::write_line
),
// (
// WriteLine,
// 57,
// "write_line",
// FunctionType::new([], [Type::String], Type::None),
// io::write_line
// ),
// // Random
(
RandomInteger,
58,
"random_int",
FunctionType::new([], [Type::Integer, Type::Integer], Type::Integer),
random::random_int
),
// (
// RandomInteger,
// 58,
// "random_int",
// FunctionType::new([], [Type::Integer, Type::Integer], Type::Integer),
// random::random_int
// ),
// Thread
(
@ -242,70 +232,8 @@ define_native_function! {
60,
"spawn",
FunctionType::new([], [ Type::function([], [], Type::None)], Type::None),
thread::spawn
spawn
)
}
#[derive(Debug, Clone, PartialEq)]
pub enum NativeFunctionError {
ExpectedArgumentCount {
expected: usize,
found: usize,
position: Span,
},
Panic {
message: String,
position: Span,
},
Parse {
error: ParseError,
position: Span,
},
Io {
error: IoErrorKind,
position: Span,
},
}
impl AnnotatedError for NativeFunctionError {
fn title() -> &'static str {
"Native Function Error"
}
fn description(&self) -> &'static str {
match self {
NativeFunctionError::ExpectedArgumentCount { .. } => {
"Expected a different number of arguments"
}
NativeFunctionError::Panic { .. } => "Explicit panic",
NativeFunctionError::Parse { .. } => "Failed to parse value",
NativeFunctionError::Io { .. } => "I/O error",
}
}
fn detail_snippets(&self) -> Vec<(String, Span)> {
match self {
NativeFunctionError::ExpectedArgumentCount {
expected,
found,
position,
} => vec![(
format!("Expected {expected} arguments, found {found}"),
*position,
)],
NativeFunctionError::Panic { message, position } => {
vec![(format!("Dust panic!\n{message}"), *position)]
}
NativeFunctionError::Parse { error, position } => {
vec![(format!("{error}"), *position)]
}
NativeFunctionError::Io { error, position } => {
vec![(format!("{error}"), *position)]
}
}
}
fn help_snippets(&self) -> Vec<(String, Span)> {
Vec::with_capacity(0)
}
}
fn spawn(_: &mut Thread, _: usize, _: Range<usize>) {}

View File

@ -1,37 +0,0 @@
use std::ops::Range;
use rand::Rng;
use crate::vm::Thread;
pub fn random_int(data: &mut Thread, destination: usize, argument_range: Range<usize>) {
let current_frame = data.current_frame_mut();
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 integer = current_frame
.registers
.integers
.get(register_index)
.copy_value();
if let Some(min) = min {
break (min, integer);
} else {
min = Some(integer);
}
}
};
let random_integer = rand::thread_rng().gen_range(min..max);
current_frame
.registers
.integers
.set_to_new_register(destination, random_integer);
}

View File

@ -1,11 +0,0 @@
use std::{ops::Range, thread::JoinHandle};
use crate::vm::Thread;
fn start_thread(_thread: &mut Thread, _argument_range: Range<usize>) -> JoinHandle<()> {
todo!();
}
pub fn spawn(data: &mut Thread, _: usize, argument_range: Range<usize>) {
let _ = start_thread(data, argument_range);
}

View File

@ -313,7 +313,7 @@ impl Default for FunctionType {
impl Display for FunctionType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "fn ")?;
write!(f, "fn")?;
if !self.type_parameters.is_empty() {
write!(f, "<")?;

View File

@ -1,4 +1,7 @@
use std::fmt::{self, Display, Formatter};
use std::{
fmt::{self, Display, Formatter},
sync::Arc,
};
use crate::{
instruction::TypeCode,
@ -15,7 +18,6 @@ pub struct AbstractList {
impl AbstractList {
pub fn display(&self, thread: &Thread) -> DustString {
let current_frame = thread.current_frame();
let mut display = DustString::new();
display.push('[');
@ -25,15 +27,110 @@ impl AbstractList {
display.push_str(", ");
}
let item_display = match self.item_type {
TypeCode::BOOLEAN => current_frame.get_boolean_from_pointer(pointer).to_string(),
TypeCode::BYTE => current_frame.get_byte_from_pointer(pointer).to_string(),
TypeCode::CHARACTER => current_frame
.get_character_from_pointer(pointer)
.to_string(),
TypeCode::FLOAT => current_frame.get_float_from_pointer(pointer).to_string(),
TypeCode::INTEGER => current_frame.get_integer_from_pointer(pointer).to_string(),
TypeCode::STRING => current_frame.get_string_from_pointer(pointer).to_string(),
let item_display = match (pointer, self.item_type) {
(Pointer::Register(register_index), TypeCode::BOOLEAN) => {
let boolean = thread
.current_registers()
.booleans
.get(register_index as usize)
.as_value();
format!("{}", boolean)
}
(Pointer::Register(register_index), TypeCode::BYTE) => {
let byte = thread
.current_registers()
.bytes
.get(register_index as usize)
.as_value();
format!("{}", byte)
}
(Pointer::Constant(constant_index), TypeCode::CHARACTER) => {
let character = thread
.current_frame()
.chunk
.character_constants
.get(constant_index as usize)
.unwrap();
format!("{}", character)
}
(Pointer::Register(register_index), TypeCode::CHARACTER) => {
let character = thread
.current_registers()
.characters
.get(register_index as usize)
.as_value();
format!("{}", character)
}
(Pointer::Constant(constant_index), TypeCode::FLOAT) => {
let float = thread
.current_frame()
.chunk
.float_constants
.get(constant_index as usize)
.unwrap();
format!("{}", float)
}
(Pointer::Register(register_index), TypeCode::FLOAT) => {
let float = thread
.current_registers()
.floats
.get(register_index as usize)
.as_value();
format!("{}", float)
}
(Pointer::Constant(constant_index), TypeCode::INTEGER) => {
let integer = thread
.current_frame()
.chunk
.integer_constants
.get(constant_index as usize)
.unwrap();
format!("{}", integer)
}
(Pointer::Register(register_index), TypeCode::INTEGER) => {
let integer = thread
.current_registers()
.integers
.get(register_index as usize)
.as_value();
format!("{}", integer)
}
(Pointer::Constant(constant_index), TypeCode::STRING) => {
let string = thread
.current_frame()
.chunk
.string_constants
.get(constant_index as usize)
.unwrap();
format!("{}", string)
}
(Pointer::Register(register_index), TypeCode::STRING) => {
let string = thread
.current_registers()
.strings
.get(register_index as usize)
.as_value();
format!("{}", string)
}
(Pointer::Register(register_index), TypeCode::LIST) => {
let list = thread
.current_registers()
.lists
.get(register_index as usize)
.as_value();
format!("{}", list)
}
_ => todo!(),
};
@ -51,45 +148,130 @@ impl AbstractList {
match self.item_type {
TypeCode::BOOLEAN => {
for pointer in &self.item_pointers {
let boolean = thread.current_frame().get_boolean_from_pointer(*pointer);
let boolean = *thread
.current_registers()
.booleans
.get(pointer.index() as usize)
.as_value();
concrete_list.push(ConcreteValue::Boolean(boolean));
}
}
TypeCode::BYTE => {
for pointer in &self.item_pointers {
let byte = thread.current_frame().get_byte_from_pointer(*pointer);
let byte = *thread
.current_registers()
.bytes
.get(pointer.index() as usize)
.as_value();
concrete_list.push(ConcreteValue::Byte(byte));
}
}
TypeCode::CHARACTER => {
for pointer in &self.item_pointers {
let character = thread.current_frame().get_character_from_pointer(*pointer);
let character = match pointer {
Pointer::Register(register_index) => {
let character = *thread
.current_registers()
.characters
.get(*register_index as usize)
.as_value();
character
}
Pointer::Constant(constant_index) => {
let character = thread
.current_frame()
.chunk
.character_constants
.get(*constant_index as usize)
.unwrap();
*character
}
};
concrete_list.push(ConcreteValue::Character(character));
}
}
TypeCode::FLOAT => {
for pointer in &self.item_pointers {
let float = thread.current_frame().get_float_from_pointer(*pointer);
let float = match pointer {
Pointer::Register(register_index) => {
let float = *thread
.current_registers()
.floats
.get(*register_index as usize)
.as_value();
float
}
Pointer::Constant(constant_index) => {
let float = thread
.current_frame()
.chunk
.float_constants
.get(*constant_index as usize)
.unwrap();
*float
}
};
concrete_list.push(ConcreteValue::Float(float));
}
}
TypeCode::INTEGER => {
for pointer in &self.item_pointers {
let integer = thread.current_frame().get_integer_from_pointer(*pointer);
let integer = match pointer {
Pointer::Register(register_index) => {
let integer = *thread
.current_registers()
.integers
.get(*register_index as usize)
.as_value();
integer
}
Pointer::Constant(constant_index) => {
let integer = thread
.current_frame()
.chunk
.integer_constants
.get(*constant_index as usize)
.unwrap();
*integer
}
};
concrete_list.push(ConcreteValue::Integer(integer));
}
}
TypeCode::STRING => {
for pointer in &self.item_pointers {
let string = thread
.current_frame()
.get_string_from_pointer(*pointer)
.clone();
let string = match pointer {
Pointer::Register(register_index) => {
let string = thread
.current_registers()
.strings
.get(*register_index as usize)
.as_value();
string.clone()
}
Pointer::Constant(constant_index) => {
let string = thread
.current_frame()
.chunk
.string_constants
.get(*constant_index as usize)
.unwrap();
string.clone()
}
};
concrete_list.push(ConcreteValue::String(string));
}
@ -97,13 +279,33 @@ impl AbstractList {
TypeCode::LIST => {
for pointer in &self.item_pointers {
let list = thread
.current_frame()
.get_list_from_pointer(pointer)
.current_registers()
.lists
.get(pointer.index() as usize)
.as_value()
.to_concrete(thread);
concrete_list.push(list);
}
}
TypeCode::FUNCTION => {
for pointer in &self.item_pointers {
let prototype_index = thread
.current_registers()
.functions
.get(pointer.index() as usize)
.as_value()
.prototype_index as usize;
let chunk = thread
.current_frame()
.chunk
.prototypes
.get(prototype_index)
.unwrap();
concrete_list.push(ConcreteValue::Function(Arc::clone(chunk)));
}
}
_ => todo!(),
}

View File

@ -1,10 +1,13 @@
use std::fmt::{self, Display, Formatter};
use std::{
fmt::{self, Display, Formatter},
sync::Arc,
};
use serde::{Deserialize, Serialize};
use smartstring::{LazyCompact, SmartString};
use tracing::trace;
use crate::{Type, Value};
use crate::{Chunk, Type, Value};
use super::RangeValue;
@ -17,6 +20,7 @@ pub enum ConcreteValue {
Byte(u8),
Character(char),
Float(f64),
Function(Arc<Chunk>),
Integer(i64),
List(Vec<ConcreteValue>),
Range(RangeValue),
@ -114,6 +118,7 @@ impl ConcreteValue {
ConcreteValue::List(items) => items.first().map_or(Type::Any, |item| item.r#type()),
ConcreteValue::Range(range) => range.r#type(),
ConcreteValue::String(_) => Type::String,
ConcreteValue::Function(chunk) => Type::Function(chunk.r#type.clone()),
}
}
}
@ -131,6 +136,7 @@ impl Clone for ConcreteValue {
ConcreteValue::List(items) => ConcreteValue::List(items.clone()),
ConcreteValue::Range(range) => ConcreteValue::Range(*range),
ConcreteValue::String(string) => ConcreteValue::String(string.clone()),
ConcreteValue::Function(chunk) => ConcreteValue::Function(chunk.clone()),
}
}
}
@ -168,6 +174,7 @@ impl Display for ConcreteValue {
write!(f, "{range_value}")
}
ConcreteValue::String(string) => write!(f, "{string}"),
ConcreteValue::Function(chunk) => write!(f, "{}", chunk.r#type),
}
}
}

View File

@ -16,8 +16,9 @@ impl Display for Function {
let mut type_string = self.r#type.to_string();
if let Some(name) = &self.name {
debug_assert!(type_string.starts_with("fn "));
debug_assert!(type_string.starts_with("fn"));
type_string.insert(2, ' ');
type_string.insert_str(3, name);
}

View File

@ -1,287 +0,0 @@
use std::ops::Add;
use tracing::trace;
use crate::{
vm::{call_frame::RuntimeValue, Thread},
DustString, Instruction,
};
use super::Cache;
pub fn add_bytes(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
let destination_index = instruction.a_field() as usize;
let left_index = instruction.b_field() as usize;
let right_index = instruction.c_field() as usize;
let current_frame = thread.current_frame_mut();
let left_value = current_frame.get_byte_from_register(left_index).clone();
let right_value = current_frame.get_byte_from_register(right_index).clone();
let sum = left_value.add(&right_value);
current_frame
.registers
.bytes
.get_mut(destination_index)
.as_value_mut()
.set_inner(sum);
}
pub fn add_characters(
_: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let destination_index = instruction.a_field() as usize;
let left_index = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_character_constant(left_index)
} else {
current_frame.get_character_from_register(left_index)
};
let right_value = if right_is_constant {
current_frame.get_character_constant(right)
} else {
current_frame.get_character_from_register(right)
};
let concatenated = {
let mut concatenated = DustString::from(String::with_capacity(2));
concatenated.push(left_value.clone_inner());
concatenated.push(right_value.clone_inner());
RuntimeValue::Raw(concatenated)
};
current_frame
.registers
.strings
.get_mut(destination_index)
.set(concatenated);
}
pub fn add_floats(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
let destination = instruction.a_field() as usize;
let left = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_float_constant(left)
} else {
current_frame.get_float_from_register(left)
}
.clone();
let right_value = if right_is_constant {
current_frame.get_float_constant(right)
} else {
current_frame.get_float_from_register(right)
}
.clone();
let sum = left_value.add(&right_value);
current_frame
.registers
.floats
.get_mut(destination)
.as_value_mut()
.set_inner(sum);
}
pub fn add_integers(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
let destination = instruction.a_field() as usize;
let left = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_integer_constant(left)
} else {
current_frame.get_integer_from_register(left)
}
.clone();
let right_value = if right_is_constant {
current_frame.get_integer_constant(right)
} else {
current_frame.get_integer_from_register(right)
}
.clone();
let sum = left_value.add(&right_value);
current_frame
.registers
.integers
.get_mut(destination)
.as_value_mut()
.set_inner(sum);
}
pub fn add_strings(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
let destination = instruction.a_field() as usize;
let left = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_string_constant(left)
} else {
current_frame.get_string_from_register(left)
};
let right_value = if right_is_constant {
current_frame.get_string_constant(right)
} else {
current_frame.get_string_from_register(right)
};
let concatenated = DustString::from(format!("{left_value}{right_value}"));
current_frame
.registers
.strings
.get_mut(destination)
.as_value_mut()
.set_inner(concatenated);
}
pub fn add_character_string(
_: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let destination = instruction.a_field() as usize;
let left = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_character_constant(left)
} else {
current_frame.get_character_from_register(left)
};
let right_value = if right_is_constant {
current_frame.get_string_constant(right)
} else {
current_frame.get_string_from_register(right)
};
let concatenated = DustString::from(format!("{left_value}{right_value}"));
current_frame
.registers
.strings
.get_mut(destination)
.as_value_mut()
.set_inner(concatenated);
}
pub fn add_string_character(
_: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let destination = instruction.a_field() as usize;
let left = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_string_constant(left)
} else {
current_frame.get_string_from_register(left)
};
let right_value = if right_is_constant {
current_frame.get_character_constant(right)
} else {
current_frame.get_character_from_register(right)
};
let concatenated = DustString::from(format!("{left_value}{right_value}"));
current_frame
.registers
.strings
.get_mut(destination)
.as_value_mut()
.set_inner(concatenated);
}
pub fn optimized_add_integer(
_: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
cache: &mut Cache,
) {
if let Cache::IntegerMath([destination, left, right]) = cache {
trace!("OPTIMIZED_ADD using integer cache");
let sum = left.add(right);
*destination.borrow_mut() = sum;
} else {
let destination_index = instruction.a_field() as usize;
let left_index = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right_index = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
let value = current_frame.get_integer_constant_mut(left_index).to_rc();
current_frame.constants.integers[left_index] = value.clone();
value
} else {
let value = current_frame
.get_integer_from_register_mut(left_index)
.to_ref_cell();
current_frame.registers.integers[left_index].set(value.clone());
value
};
let right_value = if right_is_constant {
let value = current_frame.get_integer_constant_mut(right_index).to_rc();
current_frame.constants.integers[right_index] = value.clone();
value
} else {
let value = current_frame
.get_integer_from_register_mut(right_index)
.to_ref_cell();
current_frame.registers.integers[right_index].set(value.clone());
value
};
let sum = left_value.add(&right_value);
let destination = {
let mut value = current_frame
.get_integer_from_register_mut(destination_index)
.to_ref_cell();
value.set_inner(sum);
current_frame.registers.integers[destination_index].set(value.clone());
value
};
*cache = Cache::IntegerMath([destination, left_value, right_value]);
}
}

View File

@ -1,217 +0,0 @@
use tracing::trace;
use crate::{vm::Thread, Instruction};
use super::Cache;
pub fn equal_booleans(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let left_index = instruction.b_field() as usize;
let right_index = instruction.c_field() as usize;
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = current_frame.get_boolean_from_register(left_index);
let right_value = current_frame.get_boolean_from_register(right_index);
let is_equal = left_value == right_value;
if is_equal == comparator {
*ip += 1;
}
}
pub fn equal_bytes(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
let left = instruction.b_field() as usize;
let right = instruction.c_field() as usize;
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = current_frame.get_byte_from_register(left);
let right_value = current_frame.get_byte_from_register(right);
let is_equal = left_value == right_value;
if is_equal == comparator {
*ip += 1;
}
}
pub fn equal_characters(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let left_index = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right_index = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_character_constant(left_index)
} else {
current_frame.get_character_from_register(left_index)
};
let right_value = if right_is_constant {
current_frame.get_character_constant(right_index)
} else {
current_frame.get_character_from_register(right_index)
};
let is_equal = left_value == right_value;
if is_equal == comparator {
*ip += 1;
}
}
pub fn equal_floats(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
let left = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_float_constant(left)
} else {
current_frame.get_float_from_register(left)
};
let right_value = if right_is_constant {
current_frame.get_float_constant(right)
} else {
current_frame.get_float_from_register(right)
};
let is_equal = left_value == right_value;
if is_equal == comparator {
*ip += 1;
}
}
pub fn equal_integers(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let left = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_integer_constant(left)
} else {
current_frame.get_integer_from_register(left)
};
let right_value = if right_is_constant {
current_frame.get_integer_constant(right)
} else {
current_frame.get_integer_from_register(right)
};
let is_equal = left_value == right_value;
if is_equal == comparator {
*ip += 1;
}
}
pub fn equal_strings(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let left = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_string_constant(left)
} else {
current_frame.get_string_from_register(left)
};
let right_value = if right_is_constant {
current_frame.get_string_constant(right)
} else {
current_frame.get_string_from_register(right)
};
let is_equal = left_value == right_value;
if is_equal == comparator {
*ip += 1;
}
}
pub fn optimized_equal_integers(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
cache: &mut Cache,
) {
if let Cache::IntegerComparison([left, right]) = cache {
trace!("equal_INTEGERS_OPTIMIZED using cache");
let is_equal = left == right;
if is_equal {
*ip += 1;
}
} else {
let left_index = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right_index = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
let value = current_frame.get_integer_constant_mut(left_index).to_rc();
current_frame.constants.integers[left_index] = value.clone();
value
} else {
let value = current_frame
.get_integer_from_register_mut(left_index)
.to_ref_cell();
current_frame.registers.integers[left_index].set(value.clone());
value
};
let right_value = if right_is_constant {
let value = current_frame.get_integer_constant_mut(right_index).to_rc();
current_frame.constants.integers[right_index] = value.clone();
value
} else {
let value = current_frame
.get_integer_from_register_mut(right_index)
.to_ref_cell();
current_frame.registers.integers[right_index].set(value.clone());
value
};
let is_equal = left_value == right_value;
if is_equal == comparator {
*ip += 1;
}
*cache = Cache::IntegerComparison([left_value, right_value]);
}
}

View File

@ -1,48 +0,0 @@
use tracing::trace;
use crate::{vm::Thread, Instruction};
use super::Cache;
pub fn jump(ip: &mut usize, instruction: &Instruction, _: &mut Thread, _: &mut Cache) {
let offset = instruction.b_field() as usize;
let is_positive = instruction.c_field() != 0;
if is_positive {
trace!("JUMP +{}", offset);
} else {
trace!("JUMP -{}", offset);
}
if is_positive {
*ip += offset;
} else {
*ip -= offset + 1;
}
}
pub fn optimized_jump_forward(
ip: &mut usize,
instruction: &Instruction,
_: &mut Thread,
_: &mut Cache,
) {
let offset = instruction.b_field() as usize;
trace!("JUMP +{}", offset);
*ip += offset;
}
pub fn optimized_jump_backward(
ip: &mut usize,
instruction: &Instruction,
_: &mut Thread,
_: &mut Cache,
) {
let offset = instruction.b_field() as usize;
trace!("JUMP -{}", offset);
*ip -= offset + 1;
}

View File

@ -1,212 +0,0 @@
use tracing::trace;
use crate::{vm::Thread, Instruction};
use super::Cache;
pub fn less_booleans(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let left_index = instruction.b_field() as usize;
let right_index = instruction.c_field() as usize;
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = current_frame.get_boolean_from_register(left_index);
let right_value = current_frame.get_boolean_from_register(right_index);
let is_less_than = left_value < right_value;
if is_less_than == comparator {
*ip += 1;
}
}
pub fn less_bytes(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
let left = instruction.b_field() as usize;
let right = instruction.c_field() as usize;
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = current_frame.get_byte_from_register(left);
let right_value = current_frame.get_byte_from_register(right);
let is_less_than = left_value < right_value;
if is_less_than == comparator {
*ip += 1;
}
}
pub fn less_characters(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let left_index = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right_index = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_character_constant(left_index)
} else {
current_frame.get_character_from_register(left_index)
};
let right_value = if right_is_constant {
current_frame.get_character_constant(right_index)
} else {
current_frame.get_character_from_register(right_index)
};
let is_less_than = left_value < right_value;
if is_less_than == comparator {
*ip += 1;
}
}
pub fn less_floats(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
let left = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_float_constant(left)
} else {
current_frame.get_float_from_register(left)
};
let right_value = if right_is_constant {
current_frame.get_float_constant(right)
} else {
current_frame.get_float_from_register(right)
};
let is_less_than = left_value < right_value;
if is_less_than == comparator {
*ip += 1;
}
}
pub fn less_integers(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let left = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_integer_constant(left)
} else {
current_frame.get_integer_from_register(left)
};
let right_value = if right_is_constant {
current_frame.get_integer_constant(right)
} else {
current_frame.get_integer_from_register(right)
};
let is_less_than = left_value < right_value;
if is_less_than == comparator {
*ip += 1;
}
}
pub fn less_strings(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
let left = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_string_constant(left)
} else {
current_frame.get_string_from_register(left)
};
let right_value = if right_is_constant {
current_frame.get_string_constant(right)
} else {
current_frame.get_string_from_register(right)
};
let is_less_than = left_value < right_value;
if is_less_than == comparator {
*ip += 1;
}
}
pub fn optimized_less_integers(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
cache: &mut Cache,
) {
if let Cache::IntegerComparison([left, right]) = cache {
trace!("OPTIMIZED_LESS using integer cache");
let is_less_than = left < right;
if is_less_than {
*ip += 1;
}
} else {
let left_index = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right_index = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
let value = current_frame.get_integer_constant_mut(left_index).to_rc();
current_frame.constants.integers[left_index] = value.clone();
value
} else {
let value = current_frame
.get_integer_from_register_mut(left_index)
.to_ref_cell();
current_frame.registers.integers[left_index].set(value.clone());
value
};
let right_value = if right_is_constant {
let value = current_frame.get_integer_constant_mut(right_index).to_rc();
current_frame.constants.integers[right_index] = value.clone();
value
} else {
let value = current_frame
.get_integer_from_register_mut(right_index)
.to_ref_cell();
current_frame.registers.integers[right_index].set(value.clone());
value
};
let is_less_than = left_value < right_value;
if is_less_than == comparator {
*ip += 1;
}
*cache = Cache::IntegerComparison([left_value, right_value]);
}
}

View File

@ -1,227 +0,0 @@
use tracing::trace;
use crate::{vm::Thread, Instruction};
use super::Cache;
pub fn less_equal_booleans(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let left_index = instruction.b_field() as usize;
let right_index = instruction.c_field() as usize;
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = current_frame.get_boolean_from_register(left_index);
let right_value = current_frame.get_boolean_from_register(right_index);
let is_less_than_or_equal = left_value <= right_value;
if is_less_than_or_equal == comparator {
*ip += 1;
}
}
pub fn less_equal_bytes(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let left = instruction.b_field() as usize;
let right = instruction.c_field() as usize;
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = current_frame.get_byte_from_register(left);
let right_value = current_frame.get_byte_from_register(right);
let is_less_than_or_equal = left_value <= right_value;
if is_less_than_or_equal == comparator {
*ip += 1;
}
}
pub fn less_equal_characters(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let left_index = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right_index = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_character_constant(left_index)
} else {
current_frame.get_character_from_register(left_index)
};
let right_value = if right_is_constant {
current_frame.get_character_constant(right_index)
} else {
current_frame.get_character_from_register(right_index)
};
let is_less_than_or_equal = left_value <= right_value;
if is_less_than_or_equal == comparator {
*ip += 1;
}
}
pub fn less_equal_floats(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let left = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_float_constant(left)
} else {
current_frame.get_float_from_register(left)
};
let right_value = if right_is_constant {
current_frame.get_float_constant(right)
} else {
current_frame.get_float_from_register(right)
};
let is_less_than_or_equal = left_value <= right_value;
if is_less_than_or_equal == comparator {
*ip += 1;
}
}
pub fn less_equal_integers(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let left = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_integer_constant(left)
} else {
current_frame.get_integer_from_register(left)
};
let right_value = if right_is_constant {
current_frame.get_integer_constant(right)
} else {
current_frame.get_integer_from_register(right)
};
let is_less_than_or_equal = left_value <= right_value;
if is_less_than_or_equal == comparator {
*ip += 1;
}
}
pub fn less_equal_strings(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
_: &mut Cache,
) {
let left = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
current_frame.get_string_constant(left)
} else {
current_frame.get_string_from_register(left)
};
let right_value = if right_is_constant {
current_frame.get_string_constant(right)
} else {
current_frame.get_string_from_register(right)
};
let is_less_than_or_equal = left_value <= right_value;
if is_less_than_or_equal == comparator {
*ip += 1;
}
}
pub fn optimized_less_equal_integers(
ip: &mut usize,
instruction: &Instruction,
thread: &mut Thread,
cache: &mut Cache,
) {
if let Cache::IntegerComparison([left, right]) = cache {
trace!("LESS_INTEGERS_OPTIMIZED using cache");
let is_less_than_or_equal = left <= right;
if is_less_than_or_equal {
*ip += 1;
}
} else {
let left_index = instruction.b_field() as usize;
let left_is_constant = instruction.b_is_constant();
let right_index = instruction.c_field() as usize;
let right_is_constant = instruction.c_is_constant();
let comparator = instruction.d_field();
let current_frame = thread.current_frame_mut();
let left_value = if left_is_constant {
let value = current_frame.get_integer_constant_mut(left_index).to_rc();
current_frame.constants.integers[left_index] = value.clone();
value
} else {
let value = current_frame
.get_integer_from_register_mut(left_index)
.to_ref_cell();
current_frame.registers.integers[left_index].set(value.clone());
value
};
let right_value = if right_is_constant {
let value = current_frame.get_integer_constant_mut(right_index).to_rc();
current_frame.constants.integers[right_index] = value.clone();
value
} else {
let value = current_frame
.get_integer_from_register_mut(right_index)
.to_ref_cell();
current_frame.registers.integers[right_index].set(value.clone());
value
};
let is_less_than_or_equal = left_value <= right_value;
if is_less_than_or_equal == comparator {
*ip += 1;
}
*cache = Cache::IntegerComparison([left_value, right_value]);
}
}

View File

@ -1,623 +0,0 @@
mod add;
mod equal;
mod jump;
mod less;
mod less_equal;
use add::{
add_bytes, add_character_string, add_characters, add_floats, add_integers,
add_string_character, add_strings, optimized_add_integer,
};
use equal::{
equal_booleans, equal_bytes, equal_characters, equal_floats, equal_integers, equal_strings,
optimized_equal_integers,
};
use jump::{jump, optimized_jump_backward, optimized_jump_forward};
use less::{
less_booleans, less_bytes, less_characters, less_floats, less_integers, less_strings,
optimized_less_integers,
};
use less_equal::{
less_equal_booleans, less_equal_bytes, less_equal_characters, less_equal_floats,
less_equal_integers, less_equal_strings, optimized_less_equal_integers,
};
use tracing::info;
use std::fmt::{self, Display, Formatter};
use crate::{instruction::TypeCode, AbstractList, ConcreteValue, Instruction, Operation, Value};
use super::{call_frame::RuntimeValue, thread::Thread, Pointer};
pub type ActionLogic = fn(&mut usize, &Instruction, &mut Thread, &mut Cache);
#[derive(Debug)]
pub struct ActionSequence {
actions: Vec<Action>,
}
impl ActionSequence {
pub fn new<T: IntoIterator<Item = Instruction> + DoubleEndedIterator<Item = Instruction>>(
instructions: T,
) -> Self {
let mut actions = Vec::new();
let mut instructions_reversed = instructions.rev();
while let Some(instruction) = instructions_reversed.next() {
if instruction.operation() == Operation::JUMP {
let backward_offset = instruction.b_field() as usize;
let is_positive = instruction.c_field() != 0;
if !is_positive {
let mut loop_actions = Vec::with_capacity(backward_offset);
let jump_action = Action::optimized(instruction);
loop_actions.push(jump_action);
for _ in 0..backward_offset {
let instruction = instructions_reversed.next().unwrap();
let action = Action::optimized(instruction);
loop_actions.push(action);
}
loop_actions.reverse();
let cache = Cache::LoopActions(ActionSequence {
actions: loop_actions,
});
let action = Action {
instruction,
logic: r#loop,
cache,
};
actions.push(action);
continue;
}
}
let action = Action::unoptimized(instruction);
actions.push(action);
}
actions.reverse();
ActionSequence { actions }
}
pub fn run(&mut self, thread: &mut Thread) {
let mut local_ip = 0;
while local_ip < self.actions.len() {
let action = if cfg!(debug_assertions) {
self.actions.get_mut(local_ip).unwrap()
} else {
unsafe { self.actions.get_unchecked_mut(local_ip) }
};
local_ip += 1;
info!("Run {action}");
(action.logic)(
&mut local_ip,
&action.instruction,
thread,
&mut action.cache,
);
}
}
}
impl Display for ActionSequence {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "[")?;
for (index, action) in self.actions.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{action}")?;
}
write!(f, "]")
}
}
#[derive(Debug)]
pub struct Action {
instruction: Instruction,
logic: ActionLogic,
cache: Cache,
}
#[derive(Debug)]
pub enum Cache {
Empty,
IntegerMath([RuntimeValue<i64>; 3]),
IntegerComparison([RuntimeValue<i64>; 2]),
LoopActions(ActionSequence),
}
impl Action {
pub fn unoptimized(instruction: Instruction) -> Self {
let logic = match instruction.operation() {
Operation::POINT => point,
Operation::CLOSE => close,
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::ADD => match (instruction.b_type(), instruction.c_type()) {
(TypeCode::INTEGER, TypeCode::INTEGER) => add_integers,
(TypeCode::FLOAT, TypeCode::FLOAT) => add_floats,
(TypeCode::BYTE, TypeCode::BYTE) => add_bytes,
(TypeCode::STRING, TypeCode::STRING) => add_strings,
(TypeCode::CHARACTER, TypeCode::CHARACTER) => add_characters,
(TypeCode::STRING, TypeCode::CHARACTER) => add_string_character,
(TypeCode::CHARACTER, TypeCode::STRING) => add_character_string,
_ => unreachable!(),
},
Operation::SUBTRACT => subtract,
Operation::MULTIPLY => multiply,
Operation::DIVIDE => divide,
Operation::MODULO => modulo,
Operation::NEGATE => negate,
Operation::NOT => not,
Operation::EQUAL => match (instruction.b_type(), instruction.c_type()) {
(TypeCode::BOOLEAN, TypeCode::BOOLEAN) => equal_booleans,
(TypeCode::BYTE, TypeCode::BYTE) => equal_bytes,
(TypeCode::CHARACTER, TypeCode::CHARACTER) => equal_characters,
(TypeCode::FLOAT, TypeCode::FLOAT) => equal_floats,
(TypeCode::INTEGER, TypeCode::INTEGER) => equal_integers,
(TypeCode::STRING, TypeCode::STRING) => equal_strings,
_ => todo!(),
},
Operation::LESS => match (instruction.b_type(), instruction.c_type()) {
(TypeCode::BOOLEAN, TypeCode::BOOLEAN) => less_booleans,
(TypeCode::BYTE, TypeCode::BYTE) => less_bytes,
(TypeCode::CHARACTER, TypeCode::CHARACTER) => less_characters,
(TypeCode::FLOAT, TypeCode::FLOAT) => less_floats,
(TypeCode::INTEGER, TypeCode::INTEGER) => less_integers,
(TypeCode::STRING, TypeCode::STRING) => less_strings,
_ => todo!(),
},
Operation::LESS_EQUAL => match (instruction.b_type(), instruction.c_type()) {
(TypeCode::BOOLEAN, TypeCode::BOOLEAN) => less_equal_booleans,
(TypeCode::BYTE, TypeCode::BYTE) => less_equal_bytes,
(TypeCode::CHARACTER, TypeCode::CHARACTER) => less_equal_characters,
(TypeCode::FLOAT, TypeCode::FLOAT) => less_equal_floats,
(TypeCode::INTEGER, TypeCode::INTEGER) => less_equal_integers,
(TypeCode::STRING, TypeCode::STRING) => less_equal_strings,
_ => todo!(),
},
Operation::TEST => test,
Operation::TEST_SET => test_set,
Operation::CALL => call,
Operation::CALL_NATIVE => call_native,
Operation::JUMP => jump,
Operation::RETURN => r#return,
_ => todo!(),
};
Action {
instruction,
logic,
cache: Cache::Empty,
}
}
pub fn optimized(instruction: Instruction) -> Self {
let logic = match instruction.operation() {
Operation::JUMP => match instruction.c_field() {
0 => optimized_jump_backward,
_ => optimized_jump_forward,
},
Operation::ADD => match (instruction.b_type(), instruction.c_type()) {
(TypeCode::INTEGER, TypeCode::INTEGER) => optimized_add_integer,
_ => todo!(),
},
Operation::EQUAL => match (instruction.b_type(), instruction.c_type()) {
(TypeCode::INTEGER, TypeCode::INTEGER) => optimized_equal_integers,
_ => todo!(),
},
Operation::LESS => match (instruction.b_type(), instruction.c_type()) {
(TypeCode::INTEGER, TypeCode::INTEGER) => optimized_less_integers,
_ => todo!(),
},
Operation::LESS_EQUAL => match (instruction.b_type(), instruction.c_type()) {
(TypeCode::INTEGER, TypeCode::INTEGER) => optimized_less_equal_integers,
_ => todo!(),
},
_ => todo!(),
};
Action {
instruction,
logic,
cache: Cache::Empty,
}
}
}
impl Display for Action {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if let Cache::LoopActions(actions) = &self.cache {
write!(f, "LOOP: {actions}")?;
} else {
write!(f, "{}", self.instruction.operation())?;
}
Ok(())
}
}
fn r#loop(_: &mut usize, _: &Instruction, thread: &mut Thread, cache: &mut Cache) {
if let Cache::LoopActions(actions) = cache {
actions.run(thread);
}
}
fn point(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
todo!()
}
fn close(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
todo!()
}
fn load_encoded(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
let destination = instruction.a_field();
let value_type = instruction.b_type();
let jump_next = instruction.c_field() != 0;
match value_type {
TypeCode::BOOLEAN => {
let value = instruction.b_field() != 0;
thread
.current_frame_mut()
.registers
.booleans
.get_mut(destination as usize)
.as_value_mut()
.set_inner(value);
}
TypeCode::BYTE => {
let value = instruction.b_field() as u8;
thread
.current_frame_mut()
.registers
.bytes
.get_mut(destination as usize)
.as_value_mut()
.set_inner(value);
}
_ => unreachable!(),
}
if jump_next {
*ip += 1;
}
}
fn load_constant(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
let destination = instruction.a_field() as usize;
let constant_index = instruction.b_field() as usize;
let constant_type = instruction.b_type();
let jump_next = instruction.c_field() != 0;
let current_frame = thread.current_frame_mut();
match constant_type {
TypeCode::CHARACTER => {
let constant = current_frame.get_character_constant(constant_index).clone();
current_frame
.registers
.characters
.get_mut(destination)
.set(constant);
}
TypeCode::FLOAT => {
let constant = current_frame.get_float_constant(constant_index).clone();
current_frame
.registers
.floats
.get_mut(destination)
.set(constant);
}
TypeCode::INTEGER => {
let constant = current_frame.get_integer_constant(constant_index).clone();
current_frame
.registers
.integers
.get_mut(destination)
.set(constant);
}
TypeCode::STRING => {
let constant = current_frame.get_string_constant(constant_index).clone();
current_frame
.registers
.strings
.get_mut(destination)
.set(constant);
}
_ => unreachable!(),
}
if jump_next {
*ip += 1;
}
}
fn load_list(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
let destination = instruction.a_field() as usize;
let start_register = instruction.b_field() as usize;
let item_type = instruction.b_type();
let end_register = instruction.c_field() as usize;
let jump_next = instruction.d_field();
let current_frame = thread.current_frame_mut();
let mut item_pointers = Vec::with_capacity(end_register - start_register + 1);
match item_type {
TypeCode::BOOLEAN => {
for register_index in start_register..=end_register {
let register_is_closed = current_frame.registers.booleans.is_closed(register_index);
if register_is_closed {
continue;
}
item_pointers.push(Pointer::Register(register_index));
}
}
TypeCode::BYTE => {
for register_index in start_register..=end_register {
let register_is_closed = current_frame.registers.bytes.is_closed(register_index);
if register_is_closed {
continue;
}
item_pointers.push(Pointer::Register(register_index));
}
}
TypeCode::CHARACTER => {
for register_index in start_register..=end_register {
let register_is_closed =
current_frame.registers.characters.is_closed(register_index);
if register_is_closed {
continue;
}
item_pointers.push(Pointer::Register(register_index));
}
}
TypeCode::FLOAT => {
for register_index in start_register..=end_register {
let register_is_closed = current_frame.registers.floats.is_closed(register_index);
if register_is_closed {
continue;
}
item_pointers.push(Pointer::Register(register_index));
}
}
TypeCode::INTEGER => {
for register_index in start_register..=end_register {
let register_is_closed = current_frame.registers.integers.is_closed(register_index);
if register_is_closed {
continue;
}
item_pointers.push(Pointer::Register(register_index));
}
}
TypeCode::STRING => {
for register_index in start_register..=end_register {
let register_is_closed = current_frame.registers.strings.is_closed(register_index);
if register_is_closed {
continue;
}
item_pointers.push(Pointer::Register(register_index));
}
}
_ => unreachable!(),
}
let list = RuntimeValue::Raw(AbstractList {
item_type,
item_pointers,
});
current_frame.registers.lists.get_mut(destination).set(list);
if jump_next {
*ip += 1;
}
}
fn load_function(_: &mut usize, _: &Instruction, _: &mut Thread, _: &mut Cache) {
todo!()
}
fn load_self(_: &mut usize, _: &Instruction, _: &mut Thread, _: &mut Cache) {
todo!()
}
fn subtract(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
todo!()
}
fn multiply(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
todo!()
}
fn divide(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
todo!()
}
fn modulo(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
todo!()
}
fn test(ip: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
todo!()
}
fn test_set(_: &mut usize, _: &Instruction, _: &mut Thread, _: &mut Cache) {
todo!()
}
fn negate(_: &mut usize, _: &Instruction, _: &mut Thread, _: &mut Cache) {
todo!()
}
fn not(_: &mut usize, _: &Instruction, _: &mut Thread, _: &mut Cache) {
todo!()
}
fn call(_: &mut usize, _: &Instruction, _: &mut Thread, _: &mut Cache) {
todo!()
}
fn call_native(_: &mut usize, _: &Instruction, _: &mut Thread, _: &mut Cache) {
todo!()
}
fn r#return(_: &mut usize, instruction: &Instruction, thread: &mut Thread, _: &mut Cache) {
let should_return_value = instruction.b_field() != 0;
let return_register = instruction.c_field() as usize;
let return_type = instruction.b_type();
let current_frame = thread.current_frame();
// if should_return_value {
// match return_type {
// TypeCode::BOOLEAN => {
// let return_value = current_frame
// .get_boolean_from_register(return_register)
// .clone_inner();
// thread.return_value = Some(Value::boolean(return_value));
// }
// TypeCode::BYTE => {
// let return_value = current_frame
// .get_byte_from_register(return_register)
// .clone_inner();
// thread.return_value = Some(Value::byte(return_value));
// }
// TypeCode::CHARACTER => {
// let return_value = current_frame
// .get_character_from_register(return_register)
// .clone_inner();
// thread.return_value = Some(Value::character(return_value));
// }
// TypeCode::FLOAT => {
// let return_value = current_frame
// .get_float_from_register(return_register)
// .clone_inner();
// thread.return_value = Some(Value::float(return_value));
// }
// TypeCode::INTEGER => {
// let return_value = current_frame
// .get_integer_from_register(return_register)
// .clone_inner();
// thread.return_value = Some(Value::integer(return_value));
// }
// TypeCode::STRING => {
// let return_value = current_frame
// .get_string_from_register(return_register)
// .clone_inner();
// thread.return_value = Some(Value::string(return_value));
// }
// TypeCode::LIST => {
// let abstract_list = current_frame
// .get_list_from_register(return_register)
// .clone_inner();
// let mut concrete_list = Vec::with_capacity(abstract_list.item_pointers.len());
// match abstract_list.item_type {
// TypeCode::BOOLEAN => {
// for pointer in abstract_list.item_pointers {
// let boolean = current_frame
// .get_boolean_from_pointer(&pointer)
// .clone_inner();
// let value = ConcreteValue::Boolean(boolean);
// concrete_list.push(value);
// }
// }
// TypeCode::BYTE => {
// for pointer in abstract_list.item_pointers {
// let byte = current_frame.get_byte_from_pointer(&pointer).clone_inner();
// let value = ConcreteValue::Byte(byte);
// concrete_list.push(value);
// }
// }
// TypeCode::CHARACTER => {
// for pointer in abstract_list.item_pointers {
// let character = current_frame
// .get_character_from_pointer(&pointer)
// .clone_inner();
// let value = ConcreteValue::Character(character);
// concrete_list.push(value);
// }
// }
// TypeCode::FLOAT => {
// for pointer in abstract_list.item_pointers {
// let float =
// current_frame.get_float_from_pointer(&pointer).clone_inner();
// let value = ConcreteValue::Float(float);
// concrete_list.push(value);
// }
// }
// TypeCode::INTEGER => {
// for pointer in abstract_list.item_pointers {
// let integer = current_frame
// .get_integer_from_pointer(&pointer)
// .clone_inner();
// let value = ConcreteValue::Integer(integer);
// concrete_list.push(value);
// }
// }
// TypeCode::STRING => {
// for pointer in abstract_list.item_pointers {
// let string = current_frame
// .get_string_from_pointer(&pointer)
// .clone_inner();
// let value = ConcreteValue::String(string);
// concrete_list.push(value);
// }
// }
// _ => todo!(),
// }
// thread.return_value = Some(Value::Concrete(ConcreteValue::list(
// concrete_list,
// abstract_list.item_type,
// )));
// }
// _ => unreachable!(),
// }
// }
}

View File

@ -1,317 +0,0 @@
use std::{
fmt::{self, Debug, Display, Formatter},
ops::{Index, IndexMut, RangeInclusive},
sync::Arc,
};
use smallvec::SmallVec;
use crate::{AbstractList, Chunk, DustString, Function};
#[derive(Debug)]
pub struct CallFrame {
pub chunk: Arc<Chunk>,
pub ip: usize,
pub return_register: u16,
pub registers: RegisterTable,
}
impl CallFrame {
pub fn new(chunk: Arc<Chunk>, return_register: u16) -> Self {
let registers = RegisterTable {
booleans: RegisterList::new(chunk.boolean_register_count as usize),
bytes: RegisterList::new(chunk.byte_register_count as usize),
characters: RegisterList::new(chunk.character_register_count as usize),
floats: RegisterList::new(chunk.float_register_count as usize),
integers: RegisterList::new(chunk.integer_register_count as usize),
strings: RegisterList::new(chunk.string_register_count as usize),
lists: RegisterList::new(chunk.list_register_count as usize),
functions: RegisterList::new(chunk.function_register_count as usize),
};
Self {
chunk,
ip: 0,
return_register,
registers,
}
}
pub fn get_boolean_from_pointer(&self, pointer: Pointer) -> bool {
match pointer {
Pointer::Register(register_index) => {
*self.registers.booleans.get(register_index).as_value()
}
Pointer::Constant(_) => panic!("Attempted to get boolean from constant pointer"),
}
}
pub fn get_byte_from_pointer(&self, pointer: Pointer) -> u8 {
match pointer {
Pointer::Register(register_index) => {
*self.registers.bytes.get(register_index).as_value()
}
Pointer::Constant(_) => panic!("Attempted to get byte from constant pointer"),
}
}
pub fn get_character_from_pointer(&self, pointer: Pointer) -> char {
match pointer {
Pointer::Register(register_index) => {
*self.registers.characters.get(register_index).as_value()
}
Pointer::Constant(constant_index) => self.get_character_constant(constant_index),
}
}
pub fn get_character_constant(&self, constant_index: usize) -> char {
if cfg!(debug_assertions) {
*self.chunk.character_constants.get(constant_index).unwrap()
} else {
unsafe { *self.chunk.character_constants.get_unchecked(constant_index) }
}
}
pub fn get_float_from_pointer(&self, pointer: Pointer) -> f64 {
match pointer {
Pointer::Register(register_index) => {
*self.registers.floats.get(register_index).as_value()
}
Pointer::Constant(constant_index) => self.get_float_constant(constant_index),
}
}
pub fn get_float_constant(&self, constant_index: usize) -> f64 {
if cfg!(debug_assertions) {
*self.chunk.float_constants.get(constant_index).unwrap()
} else {
unsafe { *self.chunk.float_constants.get_unchecked(constant_index) }
}
}
pub fn get_integer_from_pointer(&self, pointer: Pointer) -> i64 {
match pointer {
Pointer::Register(register_index) => {
*self.registers.integers.get(register_index).as_value()
}
Pointer::Constant(constant_index) => self.get_integer_constant(constant_index),
}
}
pub fn get_integer_constant(&self, constant_index: usize) -> i64 {
if cfg!(debug_assertions) {
*self.chunk.integer_constants.get(constant_index).unwrap()
} else {
unsafe { *self.chunk.integer_constants.get_unchecked(constant_index) }
}
}
pub fn get_string_from_pointer(&self, pointer: Pointer) -> &DustString {
match pointer {
Pointer::Register(register_index) => {
self.registers.strings.get(register_index).as_value()
}
Pointer::Constant(constant_index) => self.get_string_constant(constant_index),
}
}
pub fn get_string_constant(&self, constant_index: usize) -> &DustString {
if cfg!(debug_assertions) {
self.chunk.string_constants.get(constant_index).unwrap()
} else {
unsafe { self.chunk.string_constants.get_unchecked(constant_index) }
}
}
pub fn get_list_from_pointer(&self, pointer: &Pointer) -> &AbstractList {
match pointer {
Pointer::Register(register_index) => {
self.registers.lists.get(*register_index).as_value()
}
Pointer::Constant(_) => panic!("Attempted to get list from constant pointer"),
}
}
pub fn get_function_from_pointer(&self, pointer: &Pointer) -> &Function {
match pointer {
Pointer::Register(register_index) => {
self.registers.functions.get(*register_index).as_value()
}
Pointer::Constant(_) => panic!("Attempted to get function from constant pointer"),
}
}
}
impl Display for CallFrame {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,
"FunctionCall: {} | IP: {}",
self.chunk
.name
.as_ref()
.unwrap_or(&DustString::from("anonymous")),
self.ip,
)
}
}
#[derive(Debug)]
pub struct RegisterTable {
pub booleans: RegisterList<bool>,
pub bytes: RegisterList<u8>,
pub characters: RegisterList<char>,
pub floats: RegisterList<f64>,
pub integers: RegisterList<i64>,
pub strings: RegisterList<DustString>,
pub lists: RegisterList<AbstractList>,
pub functions: RegisterList<Function>,
}
#[derive(Debug)]
pub struct RegisterList<T, const STACK_LEN: usize = 64> {
pub registers: SmallVec<[Register<T>; STACK_LEN]>,
}
impl<T, const STACK_LEN: usize> RegisterList<T, STACK_LEN>
where
T: Clone + Default,
{
pub fn new(length: usize) -> Self {
let mut registers = SmallVec::with_capacity(length);
for _ in 0..length {
registers.push(Register::default());
}
Self { registers }
}
pub fn get(&self, index: usize) -> &Register<T> {
if cfg!(debug_assertions) {
self.registers.get(index).unwrap()
} else {
unsafe { self.registers.get_unchecked(index) }
}
}
pub fn get_many_mut(&mut self, indices: RangeInclusive<usize>) -> &mut [Register<T>] {
let registers = if cfg!(debug_assertions) {
self.registers.get_disjoint_mut([indices]).unwrap()
} else {
unsafe { self.registers.get_disjoint_unchecked_mut([indices]) }
};
registers[0]
}
pub fn get_mut(&mut self, index: usize) -> &mut Register<T> {
if cfg!(debug_assertions) {
let length = self.registers.len();
self.registers
.get_mut(index)
.unwrap_or_else(|| panic!("Index out of bounds: {index}. Length is {length}"))
} else {
unsafe { self.registers.get_unchecked_mut(index) }
}
}
pub fn set_to_new_register(&mut self, index: usize, new_value: T) {
assert!(index < self.registers.len(), "Register index out of bounds");
self.registers[index] = Register::value(new_value)
}
pub fn close(&mut self, index: usize) {
if cfg!(debug_assertions) {
self.registers.get_mut(index).unwrap().close()
} else {
unsafe { self.registers.get_unchecked_mut(index).close() }
}
}
pub fn is_closed(&self, index: usize) -> bool {
if cfg!(debug_assertions) {
self.registers.get(index).unwrap().is_closed()
} else {
unsafe { self.registers.get_unchecked(index).is_closed() }
}
}
}
impl<T> Index<usize> for RegisterList<T> {
type Output = Register<T>;
fn index(&self, index: usize) -> &Self::Output {
&self.registers[index]
}
}
impl<T> IndexMut<usize> for RegisterList<T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.registers[index]
}
}
#[derive(Clone, Copy, Debug)]
pub struct Register<T> {
value: T,
is_closed: bool,
}
impl<T> Register<T> {
pub fn value(value: T) -> Self {
Self {
value,
is_closed: false,
}
}
pub fn is_closed(&self) -> bool {
self.is_closed
}
pub fn close(&mut self) {
self.is_closed = true;
}
pub fn set(&mut self, new_value: T) {
self.value = new_value;
}
pub fn as_value(&self) -> &T {
&self.value
}
pub fn as_value_mut(&mut self) -> &mut T {
&mut self.value
}
}
impl<T: Copy> Register<T> {
pub fn copy_value(&self) -> T {
self.value
}
}
impl<T: Clone> Register<T> {
pub fn clone_value(&self) -> T {
self.value.clone()
}
}
impl<T: Default> Default for Register<T> {
fn default() -> Self {
Self {
value: Default::default(),
is_closed: false,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Pointer {
Register(usize),
Constant(usize),
}

View File

@ -1,17 +1,19 @@
//! Virtual machine and errors
// mod action;
mod call_frame;
mod thread;
use std::{sync::Arc, thread::Builder};
use std::{
ops::{Index, IndexMut, RangeInclusive},
sync::Arc,
thread::Builder,
};
pub use call_frame::{CallFrame, Pointer, Register, RegisterTable};
pub use thread::Thread;
use crossbeam_channel::bounded;
use tracing::{span, Level};
use crate::{compile, Chunk, DustError, Value};
use crate::{compile, AbstractList, Chunk, DustError, DustString, Function, Value};
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
let chunk = compile(source)?;
@ -56,3 +58,236 @@ impl Vm {
rx.recv().unwrap_or(None)
}
}
#[derive(Clone, Debug)]
pub struct CallFrame {
pub chunk: Arc<Chunk>,
pub ip: usize,
pub return_register: u16,
}
impl CallFrame {
pub fn new(chunk: Arc<Chunk>, return_register: u16) -> Self {
Self {
chunk,
ip: 0,
return_register,
}
}
pub fn get_character_constant(&self, constant_index: usize) -> char {
if cfg!(debug_assertions) {
*self.chunk.character_constants.get(constant_index).unwrap()
} else {
unsafe { *self.chunk.character_constants.get_unchecked(constant_index) }
}
}
pub fn get_float_constant(&self, constant_index: usize) -> f64 {
if cfg!(debug_assertions) {
*self.chunk.float_constants.get(constant_index).unwrap()
} else {
unsafe { *self.chunk.float_constants.get_unchecked(constant_index) }
}
}
pub fn get_integer_constant(&self, constant_index: usize) -> i64 {
if cfg!(debug_assertions) {
*self.chunk.integer_constants.get(constant_index).unwrap()
} else {
unsafe { *self.chunk.integer_constants.get_unchecked(constant_index) }
}
}
pub fn get_string_constant(&self, constant_index: usize) -> &DustString {
if cfg!(debug_assertions) {
self.chunk.string_constants.get(constant_index).unwrap()
} else {
unsafe { self.chunk.string_constants.get_unchecked(constant_index) }
}
}
}
#[derive(Debug)]
pub struct RegisterTable {
pub booleans: RegisterList<bool>,
pub bytes: RegisterList<u8>,
pub characters: RegisterList<char>,
pub floats: RegisterList<f64>,
pub integers: RegisterList<i64>,
pub strings: RegisterList<DustString>,
pub lists: RegisterList<AbstractList>,
pub functions: RegisterList<Function>,
}
impl RegisterTable {
pub fn new(chunk: &Chunk) -> Self {
Self {
booleans: RegisterList::new(chunk.boolean_register_count as usize),
bytes: RegisterList::new(chunk.byte_register_count as usize),
characters: RegisterList::new(chunk.character_register_count as usize),
floats: RegisterList::new(chunk.float_register_count as usize),
integers: RegisterList::new(chunk.integer_register_count as usize),
strings: RegisterList::new(chunk.string_register_count as usize),
lists: RegisterList::new(chunk.list_register_count as usize),
functions: RegisterList::new(chunk.function_register_count as usize),
}
}
}
#[derive(Debug)]
pub struct RegisterList<T, const STACK_LEN: usize = 64> {
pub registers: Vec<Register<T>>,
}
impl<T, const STACK_LEN: usize> RegisterList<T, STACK_LEN>
where
T: Clone + Default,
{
pub fn new(length: usize) -> Self {
let mut registers = Vec::with_capacity(length);
for _ in 0..length {
registers.push(Register::default());
}
Self { registers }
}
pub fn get(&self, index: usize) -> &Register<T> {
if cfg!(debug_assertions) {
self.registers.get(index).unwrap()
} else {
unsafe { self.registers.get_unchecked(index) }
}
}
pub fn get_many_mut(&mut self, indices: RangeInclusive<usize>) -> &mut [Register<T>] {
let registers = if cfg!(debug_assertions) {
self.registers.get_disjoint_mut([indices]).unwrap()
} else {
unsafe { self.registers.get_disjoint_unchecked_mut([indices]) }
};
registers[0]
}
pub fn get_mut(&mut self, index: usize) -> &mut Register<T> {
if cfg!(debug_assertions) {
let length = self.registers.len();
self.registers
.get_mut(index)
.unwrap_or_else(|| panic!("Index out of bounds: {index}. Length is {length}"))
} else {
unsafe { self.registers.get_unchecked_mut(index) }
}
}
pub fn set_to_new_register(&mut self, index: usize, new_value: T) {
assert!(index < self.registers.len(), "Register index out of bounds");
self.registers[index] = Register::value(new_value)
}
pub fn close(&mut self, index: usize) {
if cfg!(debug_assertions) {
self.registers.get_mut(index).unwrap().close()
} else {
unsafe { self.registers.get_unchecked_mut(index).close() }
}
}
pub fn is_closed(&self, index: usize) -> bool {
if cfg!(debug_assertions) {
self.registers.get(index).unwrap().is_closed()
} else {
unsafe { self.registers.get_unchecked(index).is_closed() }
}
}
}
impl<T> Index<usize> for RegisterList<T> {
type Output = Register<T>;
fn index(&self, index: usize) -> &Self::Output {
&self.registers[index]
}
}
impl<T> IndexMut<usize> for RegisterList<T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.registers[index]
}
}
#[derive(Clone, Copy, Debug)]
pub struct Register<T> {
value: T,
is_closed: bool,
}
impl<T> Register<T> {
pub fn value(value: T) -> Self {
Self {
value,
is_closed: false,
}
}
pub fn is_closed(&self) -> bool {
self.is_closed
}
pub fn close(&mut self) {
self.is_closed = true;
}
pub fn set(&mut self, new_value: T) {
self.value = new_value;
}
pub fn as_value(&self) -> &T {
&self.value
}
pub fn as_value_mut(&mut self) -> &mut T {
&mut self.value
}
}
impl<T: Copy> Register<T> {
pub fn copy_value(&self) -> T {
self.value
}
}
impl<T: Clone> Register<T> {
pub fn clone_value(&self) -> T {
self.value.clone()
}
}
impl<T: Default> Default for Register<T> {
fn default() -> Self {
Self {
value: Default::default(),
is_closed: false,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Pointer {
Register(u16),
Constant(u16),
}
impl Pointer {
pub fn index(&self) -> u16 {
match self {
Pointer::Register(index) => *index,
Pointer::Constant(index) => *index,
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ fn add_assign_expects_mutable_variable() {
assert_eq!(
compile(source),
Err(DustError::Compile {
Err(DustError {
error: CompileError::ExpectedMutableVariable {
found: Token::Integer("1").to_owned(),
position: Span(0, 1)
@ -22,7 +22,7 @@ fn divide_assign_expects_mutable_variable() {
assert_eq!(
compile(source),
Err(DustError::Compile {
Err(DustError {
error: CompileError::ExpectedMutableVariable {
found: Token::Integer("1").to_owned(),
position: Span(0, 1)
@ -38,7 +38,7 @@ fn multiply_assign_expects_mutable_variable() {
assert_eq!(
compile(source),
Err(DustError::Compile {
Err(DustError {
error: CompileError::ExpectedMutableVariable {
found: Token::Integer("1").to_owned(),
position: Span(0, 1)
@ -54,7 +54,7 @@ fn subtract_assign_expects_mutable_variable() {
assert_eq!(
compile(source),
Err(DustError::Compile {
Err(DustError {
error: CompileError::ExpectedMutableVariable {
found: Token::Integer("1").to_owned(),
position: Span(0, 1)
@ -70,7 +70,7 @@ fn modulo_assign_expects_mutable_variable() {
assert_eq!(
compile(source),
Err(DustError::Compile {
Err(DustError {
error: CompileError::ExpectedMutableVariable {
found: Token::Integer("1").to_owned(),
position: Span(0, 1)