Implement function calls in the VM
This commit is contained in:
parent
a08a1e560a
commit
d5b4278571
@ -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()?;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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!(
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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<'_> {
|
||||
|
@ -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})")
|
||||
}
|
||||
}
|
||||
|
@ -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")?;
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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::{
|
||||
|
@ -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)
|
||||
}
|
@ -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();
|
||||
}
|
@ -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>) {}
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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, "<")?;
|
||||
|
@ -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!(),
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
@ -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]);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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]);
|
||||
}
|
||||
}
|
@ -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]);
|
||||
}
|
||||
}
|
@ -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!(),
|
||||
// }
|
||||
// }
|
||||
}
|
@ -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),
|
||||
}
|
@ -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
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user