Implement lists; Add tests
This commit is contained in:
parent
8cc5661944
commit
1d0824165d
@ -225,7 +225,7 @@ impl<'src> Compiler<'src> {
|
||||
/// Note for maintainers: Do not give a name when compiling functions, only the main chunk. This
|
||||
/// 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(self) -> Chunk {
|
||||
pub fn finish(mut self) -> Chunk {
|
||||
let boolean_register_count = self.next_boolean_register() as usize;
|
||||
let byte_register_count = self.next_byte_register() as usize;
|
||||
let character_register_count = self.next_character_register() as usize;
|
||||
@ -352,7 +352,7 @@ impl<'src> Compiler<'src> {
|
||||
.unwrap_or(self.minimum_string_register)
|
||||
}
|
||||
|
||||
fn next_list_register(&self) -> u16 {
|
||||
fn next_list_register(&mut self) -> u16 {
|
||||
self.instructions
|
||||
.iter()
|
||||
.rev()
|
||||
@ -1242,10 +1242,18 @@ impl<'src> Compiler<'src> {
|
||||
|
||||
let mut item_type = Type::None;
|
||||
let mut start_register = None;
|
||||
let mut length = 0;
|
||||
|
||||
while !self.allow(Token::RightBracket)? && !self.is_eof() {
|
||||
while !self.allow(Token::RightBracket)? {
|
||||
let next_boolean_register = self.next_boolean_register();
|
||||
let next_byte_register = self.next_byte_register();
|
||||
let next_character_register = self.next_character_register();
|
||||
let next_float_register = self.next_float_register();
|
||||
let next_integer_register = self.next_integer_register();
|
||||
let next_string_register = self.next_string_register();
|
||||
let next_list_register = self.next_list_register();
|
||||
|
||||
self.parse_expression()?;
|
||||
self.allow(Token::Comma)?;
|
||||
|
||||
if item_type == Type::None {
|
||||
item_type = self.get_last_instruction_type();
|
||||
@ -1272,18 +1280,123 @@ impl<'src> Compiler<'src> {
|
||||
start_register = Some(first_index);
|
||||
}
|
||||
|
||||
length += 1;
|
||||
match self.get_last_instruction_type() {
|
||||
Type::Boolean => {
|
||||
let used_boolean_registers =
|
||||
self.next_boolean_register() - next_boolean_register;
|
||||
|
||||
self.allow(Token::Comma)?;
|
||||
if used_boolean_registers > 1 {
|
||||
let close = Instruction::close(
|
||||
next_boolean_register,
|
||||
next_boolean_register + used_boolean_registers,
|
||||
TypeCode::BOOLEAN,
|
||||
);
|
||||
|
||||
self.emit_instruction(close, Type::None, self.current_position);
|
||||
}
|
||||
}
|
||||
Type::Byte => {
|
||||
let used_byte_registers = self.next_byte_register() - 1 - next_byte_register;
|
||||
|
||||
if used_byte_registers > 1 {
|
||||
let close = Instruction::close(
|
||||
next_byte_register,
|
||||
next_byte_register + used_byte_registers,
|
||||
TypeCode::BYTE,
|
||||
);
|
||||
|
||||
self.emit_instruction(close, Type::None, self.current_position);
|
||||
}
|
||||
}
|
||||
Type::Character => {
|
||||
let used_character_registers =
|
||||
self.next_character_register() - 1 - next_character_register;
|
||||
|
||||
if used_character_registers > 1 {
|
||||
let close = Instruction::close(
|
||||
next_character_register,
|
||||
next_character_register + used_character_registers,
|
||||
TypeCode::CHARACTER,
|
||||
);
|
||||
|
||||
self.emit_instruction(close, Type::None, self.current_position);
|
||||
}
|
||||
}
|
||||
Type::Float => {
|
||||
let used_float_registers = self.next_float_register() - 1 - next_float_register;
|
||||
|
||||
if used_float_registers > 1 {
|
||||
let close = Instruction::close(
|
||||
next_float_register,
|
||||
next_float_register + used_float_registers,
|
||||
TypeCode::FLOAT,
|
||||
);
|
||||
|
||||
self.emit_instruction(close, Type::None, self.current_position);
|
||||
}
|
||||
}
|
||||
Type::Integer => {
|
||||
let used_integer_registers =
|
||||
self.next_integer_register() - 1 - next_integer_register;
|
||||
|
||||
if used_integer_registers > 1 {
|
||||
let close = Instruction::close(
|
||||
next_integer_register,
|
||||
next_integer_register + used_integer_registers,
|
||||
TypeCode::INTEGER,
|
||||
);
|
||||
|
||||
self.emit_instruction(close, Type::None, self.current_position);
|
||||
}
|
||||
}
|
||||
Type::String => {
|
||||
let used_string_registers =
|
||||
self.next_string_register() - 1 - next_string_register;
|
||||
|
||||
if used_string_registers > 1 {
|
||||
let close = Instruction::close(
|
||||
next_string_register,
|
||||
next_string_register + used_string_registers,
|
||||
TypeCode::STRING,
|
||||
);
|
||||
|
||||
self.emit_instruction(close, Type::None, self.current_position);
|
||||
}
|
||||
}
|
||||
Type::List(_) => {
|
||||
let used_list_registers = self.next_list_register() - next_list_register - 1;
|
||||
|
||||
if used_list_registers > 1 {
|
||||
let close = Instruction::close(
|
||||
next_list_register,
|
||||
next_list_register + used_list_registers - 1,
|
||||
TypeCode::LIST,
|
||||
);
|
||||
|
||||
self.emit_instruction(close, Type::None, self.current_position);
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
let destination = self.next_list_register();
|
||||
let end = self.previous_position.1;
|
||||
let end_register = match item_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,
|
||||
Type::List(_) => self.next_list_register() - 1,
|
||||
_ => todo!(),
|
||||
};
|
||||
let destination = self.next_list_register();
|
||||
let load_list = Instruction::load_list(
|
||||
destination,
|
||||
item_type.type_code(),
|
||||
start_register.unwrap_or(0),
|
||||
length,
|
||||
end_register,
|
||||
false,
|
||||
);
|
||||
|
||||
|
@ -2,11 +2,12 @@ use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionFields;
|
||||
use super::{InstructionFields, TypeCode};
|
||||
|
||||
pub struct Close {
|
||||
pub from: u16,
|
||||
pub to: u16,
|
||||
pub r#type: TypeCode,
|
||||
}
|
||||
|
||||
impl From<Instruction> for Close {
|
||||
@ -14,6 +15,7 @@ impl From<Instruction> for Close {
|
||||
Close {
|
||||
from: instruction.b_field(),
|
||||
to: instruction.c_field(),
|
||||
r#type: instruction.b_type(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -22,11 +24,13 @@ impl From<Close> for Instruction {
|
||||
fn from(close: Close) -> Self {
|
||||
let operation = Operation::CLOSE;
|
||||
let b_field = close.from;
|
||||
let b_type = close.r#type;
|
||||
let c_field = close.to;
|
||||
|
||||
InstructionFields {
|
||||
operation,
|
||||
b_field,
|
||||
b_type,
|
||||
c_field,
|
||||
..Default::default()
|
||||
}
|
||||
@ -36,8 +40,17 @@ impl From<Close> for Instruction {
|
||||
|
||||
impl Display for Close {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
let Close { from, to } = self;
|
||||
let Close { from, to, r#type } = self;
|
||||
|
||||
write!(f, "{from}..={to}")
|
||||
match *r#type {
|
||||
TypeCode::BOOLEAN => write!(f, "R_BOOL_{from}..=R_BOOL_{to}"),
|
||||
TypeCode::BYTE => write!(f, "R_BYTE_{from}..=R_BYTE_{to}"),
|
||||
TypeCode::CHARACTER => write!(f, "R_CHAR_{from}..=R_CHAR_{to}"),
|
||||
TypeCode::FLOAT => write!(f, "R_FLOAT_{from}..=R_FLOAT_{to}"),
|
||||
TypeCode::INTEGER => write!(f, "R_INT_{from}..=R_INT_{to}"),
|
||||
TypeCode::STRING => write!(f, "R_STR_{from}..=R_STR_{to}"),
|
||||
TypeCode::LIST => write!(f, "R_LIST_{from}..=R_LIST_{to}"),
|
||||
unsupported => panic!("Unsupported type code: {:?}", unsupported),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ pub struct LoadList {
|
||||
pub destination: u16,
|
||||
pub item_type: TypeCode,
|
||||
pub start_register: u16,
|
||||
pub length: u16,
|
||||
pub end_register: u16,
|
||||
pub jump_next: bool,
|
||||
}
|
||||
|
||||
@ -17,14 +17,14 @@ impl From<Instruction> for LoadList {
|
||||
let destination = instruction.a_field();
|
||||
let start_register = instruction.b_field();
|
||||
let item_type = instruction.b_type();
|
||||
let length = instruction.c_field();
|
||||
let end_register = instruction.c_field();
|
||||
let jump_next = instruction.d_field();
|
||||
|
||||
LoadList {
|
||||
destination,
|
||||
item_type,
|
||||
start_register,
|
||||
length,
|
||||
end_register,
|
||||
jump_next,
|
||||
}
|
||||
}
|
||||
@ -37,7 +37,7 @@ impl From<LoadList> for Instruction {
|
||||
a_field: load_list.destination,
|
||||
b_field: load_list.start_register,
|
||||
b_type: load_list.item_type,
|
||||
c_field: load_list.length,
|
||||
c_field: load_list.end_register,
|
||||
d_field: load_list.jump_next,
|
||||
..Default::default()
|
||||
}
|
||||
@ -51,19 +51,38 @@ impl Display for LoadList {
|
||||
destination,
|
||||
item_type,
|
||||
start_register,
|
||||
length,
|
||||
end_register,
|
||||
jump_next,
|
||||
} = self;
|
||||
let type_caps = item_type.to_string().to_uppercase();
|
||||
|
||||
write!(f, "R_LIST_{destination} = [")?;
|
||||
|
||||
for (i, register_index) in (*start_register..(start_register + length)).enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
match *item_type {
|
||||
TypeCode::BOOLEAN => {
|
||||
write!(f, "R_BOOL_{start_register}..=R_BOOL_{end_register}")?;
|
||||
}
|
||||
|
||||
write!(f, "R_{type_caps}_{register_index}")?;
|
||||
TypeCode::BYTE => {
|
||||
write!(f, "R_BYTE_{start_register}..=R_BYTE_{end_register}")?;
|
||||
}
|
||||
TypeCode::CHARACTER => {
|
||||
write!(f, "R_CHAR_{start_register}..=R_CHAR_{end_register}")?;
|
||||
}
|
||||
TypeCode::FLOAT => {
|
||||
write!(f, "R_FLOAT_{start_register}..=R_FLOAT_{end_register}")?;
|
||||
}
|
||||
TypeCode::INTEGER => {
|
||||
write!(f, "R_INT_{start_register}..=R_INT_{end_register}")?;
|
||||
}
|
||||
TypeCode::STRING => {
|
||||
write!(f, "R_STR_{start_register}..=R_STR_{end_register}")?;
|
||||
}
|
||||
TypeCode::LIST => {
|
||||
write!(f, "R_LIST_{start_register}..=R_LIST_{end_register}")?;
|
||||
}
|
||||
TypeCode::FUNCTION => {
|
||||
write!(f, "R_FN_{start_register}..=R_FN_{end_register}")?;
|
||||
}
|
||||
unknown => panic!("Unknown type code: {}", unknown.0),
|
||||
}
|
||||
|
||||
write!(f, "]")?;
|
||||
|
@ -326,8 +326,8 @@ impl Instruction {
|
||||
Instruction::from(Point { destination, to })
|
||||
}
|
||||
|
||||
pub fn close(from: u16, to: u16) -> Instruction {
|
||||
Instruction::from(Close { from, to })
|
||||
pub fn close(from: u16, to: u16, r#type: TypeCode) -> Instruction {
|
||||
Instruction::from(Close { from, to, r#type })
|
||||
}
|
||||
|
||||
pub fn load_encoded(
|
||||
@ -370,14 +370,14 @@ impl Instruction {
|
||||
destination: u16,
|
||||
item_type: TypeCode,
|
||||
start_register: u16,
|
||||
length: u16,
|
||||
end_register: u16,
|
||||
jump_next: bool,
|
||||
) -> Instruction {
|
||||
Instruction::from(LoadList {
|
||||
destination,
|
||||
item_type,
|
||||
start_register,
|
||||
length,
|
||||
end_register,
|
||||
jump_next,
|
||||
})
|
||||
}
|
||||
@ -583,7 +583,7 @@ impl Instruction {
|
||||
| Operation::TEST_SET
|
||||
| Operation::JUMP
|
||||
| Operation::RETURN => false,
|
||||
_ => self.operation().panic_from_unknown_code(),
|
||||
unknown => panic!("Unknown operation: {}", unknown.0),
|
||||
}
|
||||
}
|
||||
|
||||
@ -592,6 +592,7 @@ impl Instruction {
|
||||
|
||||
match operation {
|
||||
Operation::POINT => Point::from(*self).to_string(),
|
||||
Operation::CLOSE => Close::from(*self).to_string(),
|
||||
Operation::LOAD_ENCODED => LoadEncoded::from(*self).to_string(),
|
||||
Operation::LOAD_CONSTANT => LoadConstant::from(*self).to_string(),
|
||||
Operation::LOAD_FUNCTION => LoadFunction::from(*self).to_string(),
|
||||
@ -613,8 +614,7 @@ impl Instruction {
|
||||
Operation::CALL_NATIVE => CallNative::from(*self).to_string(),
|
||||
Operation::JUMP => Jump::from(*self).to_string(),
|
||||
Operation::RETURN => Return::from(*self).to_string(),
|
||||
|
||||
_ => operation.panic_from_unknown_code(),
|
||||
unknown => panic!("Unknown operation: {}", unknown.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ impl Operation {
|
||||
pub fn name(&self) -> &'static str {
|
||||
match *self {
|
||||
Self::POINT => "POINT",
|
||||
Self::CLOSE => "CLOSE",
|
||||
Self::LOAD_ENCODED => "LOAD_ENCODED",
|
||||
Self::LOAD_CONSTANT => "LOAD_CONSTANT",
|
||||
Self::LOAD_FUNCTION => "LOAD_FUNCTION",
|
||||
@ -74,7 +75,7 @@ impl Operation {
|
||||
Self::CALL_NATIVE => "CALL_NATIVE",
|
||||
Self::JUMP => "JUMP",
|
||||
Self::RETURN => "RETURN",
|
||||
_ => self.panic_from_unknown_code(),
|
||||
unknown => panic!("Unknown operation: {}", unknown.0),
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,10 +96,6 @@ impl Operation {
|
||||
Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL
|
||||
)
|
||||
}
|
||||
|
||||
pub fn panic_from_unknown_code(self) -> ! {
|
||||
panic!("Unknown operation code: {}", self.0);
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Operation {
|
||||
|
@ -70,7 +70,50 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 23] = [
|
||||
|
||||
pub fn point(_: InstructionFields, thread: &mut Thread) {}
|
||||
|
||||
pub fn close(_: InstructionFields, thread: &mut Thread) {}
|
||||
pub fn close(instruction: InstructionFields, thread: &mut Thread) {
|
||||
let from = instruction.b_field as usize;
|
||||
let to = instruction.c_field as usize;
|
||||
let r#type = instruction.b_type;
|
||||
|
||||
match r#type {
|
||||
TypeCode::BOOLEAN => {
|
||||
for register_index in from..=to {
|
||||
thread.close_boolean_register(register_index);
|
||||
}
|
||||
}
|
||||
TypeCode::BYTE => {
|
||||
for register_index in from..=to {
|
||||
thread.close_byte_register(register_index);
|
||||
}
|
||||
}
|
||||
TypeCode::CHARACTER => {
|
||||
for register_index in from..=to {
|
||||
thread.close_character_register(register_index);
|
||||
}
|
||||
}
|
||||
TypeCode::FLOAT => {
|
||||
for register_index in from..=to {
|
||||
thread.close_float_register(register_index);
|
||||
}
|
||||
}
|
||||
TypeCode::INTEGER => {
|
||||
for register_index in from..=to {
|
||||
thread.close_integer_register(register_index);
|
||||
}
|
||||
}
|
||||
TypeCode::STRING => {
|
||||
for register_index in from..=to {
|
||||
thread.close_string_register(register_index);
|
||||
}
|
||||
}
|
||||
TypeCode::LIST => {
|
||||
for register_index in from..=to {
|
||||
thread.close_list_register(register_index);
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_encoded(instruction: InstructionFields, thread: &mut Thread) {
|
||||
let destination = instruction.a_field;
|
||||
@ -139,20 +182,79 @@ pub fn load_list(instruction: InstructionFields, thread: &mut Thread) {
|
||||
let destination = instruction.a_field;
|
||||
let start_register = instruction.b_field;
|
||||
let item_type = instruction.b_type;
|
||||
let length = instruction.c_field;
|
||||
let end_register = instruction.c_field;
|
||||
let jump_next = instruction.d_field;
|
||||
|
||||
let mut item_pointers = Vec::with_capacity(length as usize);
|
||||
let length = (end_register - start_register + 1) as usize;
|
||||
let mut item_pointers = Vec::with_capacity(length);
|
||||
|
||||
for register_index in start_register..=end_register {
|
||||
let register_index = register_index as usize;
|
||||
|
||||
for register_index in start_register..start_register + length {
|
||||
let pointer = match item_type {
|
||||
TypeCode::BOOLEAN => Pointer::RegisterBoolean(register_index as usize),
|
||||
TypeCode::BYTE => Pointer::RegisterByte(register_index as usize),
|
||||
TypeCode::CHARACTER => Pointer::RegisterCharacter(register_index as usize),
|
||||
TypeCode::FLOAT => Pointer::RegisterFloat(register_index as usize),
|
||||
TypeCode::INTEGER => Pointer::RegisterInteger(register_index as usize),
|
||||
TypeCode::STRING => Pointer::RegisterString(register_index as usize),
|
||||
TypeCode::LIST => Pointer::RegisterList(register_index as usize),
|
||||
TypeCode::BOOLEAN => {
|
||||
let is_closed = thread.is_boolean_register_closed(register_index);
|
||||
|
||||
if is_closed {
|
||||
continue;
|
||||
}
|
||||
|
||||
Pointer::RegisterBoolean(register_index)
|
||||
}
|
||||
TypeCode::BYTE => {
|
||||
let is_closed = thread.is_byte_register_closed(register_index);
|
||||
|
||||
if is_closed {
|
||||
continue;
|
||||
}
|
||||
|
||||
Pointer::RegisterByte(register_index)
|
||||
}
|
||||
TypeCode::CHARACTER => {
|
||||
let is_closed = thread.is_character_register_closed(register_index);
|
||||
|
||||
if is_closed {
|
||||
continue;
|
||||
}
|
||||
|
||||
Pointer::RegisterCharacter(register_index)
|
||||
}
|
||||
TypeCode::FLOAT => {
|
||||
let is_closed = thread.is_float_register_closed(register_index);
|
||||
|
||||
if is_closed {
|
||||
continue;
|
||||
}
|
||||
|
||||
Pointer::RegisterFloat(register_index)
|
||||
}
|
||||
TypeCode::INTEGER => {
|
||||
let is_closed = thread.is_integer_register_closed(register_index);
|
||||
|
||||
if is_closed {
|
||||
continue;
|
||||
}
|
||||
|
||||
Pointer::RegisterInteger(register_index)
|
||||
}
|
||||
TypeCode::STRING => {
|
||||
let is_closed = thread.is_string_register_closed(register_index);
|
||||
|
||||
if is_closed {
|
||||
continue;
|
||||
}
|
||||
|
||||
Pointer::RegisterString(register_index)
|
||||
}
|
||||
TypeCode::LIST => {
|
||||
let is_closed = thread.is_list_register_closed(register_index);
|
||||
|
||||
if is_closed {
|
||||
continue;
|
||||
}
|
||||
|
||||
Pointer::RegisterList(register_index)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
|
@ -80,18 +80,28 @@ impl Default for RegisterTable {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Register<T> {
|
||||
Empty,
|
||||
Value(T),
|
||||
Closed(T),
|
||||
Pointer(Pointer),
|
||||
}
|
||||
|
||||
impl<T> Register<T> {
|
||||
pub fn close(mut self) {
|
||||
if let Self::Value(value) = self {
|
||||
self = Self::Closed(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Display> Display for Register<T> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Empty => write!(f, "empty"),
|
||||
Self::Value(value) => write!(f, "{value}"),
|
||||
Self::Closed(value) => write!(f, "Closed({value})"),
|
||||
Self::Value(value) => write!(f, "Value({value})"),
|
||||
Self::Pointer(pointer) => write!(f, "Pointer({pointer:?})"),
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,7 @@ use std::{collections::HashMap, sync::Arc, thread::JoinHandle};
|
||||
|
||||
use tracing::{info, trace};
|
||||
|
||||
use crate::{
|
||||
AbstractList, Chunk, ConcreteValue, DustString, Span, Value, instruction::TypeCode,
|
||||
vm::CallFrame,
|
||||
};
|
||||
use crate::{AbstractList, Chunk, ConcreteValue, DustString, Span, Value, vm::CallFrame};
|
||||
|
||||
use super::call_frame::{Pointer, Register};
|
||||
|
||||
@ -180,6 +177,7 @@ impl Thread {
|
||||
|
||||
match register {
|
||||
Register::Value(value) => value,
|
||||
Register::Closed(value) => value,
|
||||
Register::Pointer(pointer) => self.get_pointer_to_boolean(pointer),
|
||||
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||
}
|
||||
@ -215,6 +213,38 @@ impl Thread {
|
||||
*old_register = new_register;
|
||||
}
|
||||
|
||||
pub fn is_boolean_register_closed(&self, register_index: usize) -> bool {
|
||||
let register = if cfg!(debug_assertions) {
|
||||
self.call_stack
|
||||
.last()
|
||||
.unwrap()
|
||||
.registers
|
||||
.booleans
|
||||
.get(register_index)
|
||||
.unwrap()
|
||||
} else {
|
||||
unsafe {
|
||||
self.call_stack
|
||||
.last()
|
||||
.unwrap_unchecked()
|
||||
.registers
|
||||
.booleans
|
||||
.get_unchecked(register_index)
|
||||
}
|
||||
};
|
||||
|
||||
matches!(register, Register::Closed(_))
|
||||
}
|
||||
|
||||
pub fn close_boolean_register(&mut self, register_index: usize) {
|
||||
self.current_frame_mut()
|
||||
.registers
|
||||
.booleans
|
||||
.get_mut(register_index)
|
||||
.unwrap()
|
||||
.close();
|
||||
}
|
||||
|
||||
pub fn get_byte_register(&self, register_index: usize) -> &u8 {
|
||||
let register = if cfg!(debug_assertions) {
|
||||
self.call_stack
|
||||
@ -237,6 +267,7 @@ impl Thread {
|
||||
|
||||
match register {
|
||||
Register::Value(value) => value,
|
||||
Register::Closed(value) => value,
|
||||
Register::Pointer(pointer) => self.get_pointer_to_byte(pointer),
|
||||
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||
}
|
||||
@ -272,6 +303,38 @@ impl Thread {
|
||||
*old_register = new_register;
|
||||
}
|
||||
|
||||
pub fn is_byte_register_closed(&self, register_index: usize) -> bool {
|
||||
let register = if cfg!(debug_assertions) {
|
||||
self.call_stack
|
||||
.last()
|
||||
.unwrap()
|
||||
.registers
|
||||
.bytes
|
||||
.get(register_index)
|
||||
.unwrap()
|
||||
} else {
|
||||
unsafe {
|
||||
self.call_stack
|
||||
.last()
|
||||
.unwrap_unchecked()
|
||||
.registers
|
||||
.bytes
|
||||
.get_unchecked(register_index)
|
||||
}
|
||||
};
|
||||
|
||||
matches!(register, Register::Closed(_))
|
||||
}
|
||||
|
||||
pub fn close_byte_register(&mut self, register_index: usize) {
|
||||
self.current_frame_mut()
|
||||
.registers
|
||||
.bytes
|
||||
.get_mut(register_index)
|
||||
.unwrap()
|
||||
.close();
|
||||
}
|
||||
|
||||
pub fn get_character_register(&self, register_index: usize) -> &char {
|
||||
let register = if cfg!(debug_assertions) {
|
||||
self.call_stack
|
||||
@ -294,6 +357,7 @@ impl Thread {
|
||||
|
||||
match register {
|
||||
Register::Value(value) => value,
|
||||
Register::Closed(value) => value,
|
||||
Register::Pointer(pointer) => self.get_pointer_to_character(pointer),
|
||||
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||
}
|
||||
@ -334,6 +398,38 @@ impl Thread {
|
||||
*old_register = new_register;
|
||||
}
|
||||
|
||||
pub fn is_character_register_closed(&self, register_index: usize) -> bool {
|
||||
let register = if cfg!(debug_assertions) {
|
||||
self.call_stack
|
||||
.last()
|
||||
.unwrap()
|
||||
.registers
|
||||
.characters
|
||||
.get(register_index)
|
||||
.unwrap()
|
||||
} else {
|
||||
unsafe {
|
||||
self.call_stack
|
||||
.last()
|
||||
.unwrap_unchecked()
|
||||
.registers
|
||||
.characters
|
||||
.get_unchecked(register_index)
|
||||
}
|
||||
};
|
||||
|
||||
matches!(register, Register::Closed(_))
|
||||
}
|
||||
|
||||
pub fn close_character_register(&mut self, register_index: usize) {
|
||||
self.current_frame_mut()
|
||||
.registers
|
||||
.characters
|
||||
.get_mut(register_index)
|
||||
.unwrap()
|
||||
.close();
|
||||
}
|
||||
|
||||
pub fn get_float_register(&self, register_index: usize) -> &f64 {
|
||||
let register = if cfg!(debug_assertions) {
|
||||
self.call_stack
|
||||
@ -356,6 +452,7 @@ impl Thread {
|
||||
|
||||
match register {
|
||||
Register::Value(value) => value,
|
||||
Register::Closed(value) => value,
|
||||
Register::Pointer(pointer) => self.get_pointer_to_float(pointer),
|
||||
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||
}
|
||||
@ -394,6 +491,38 @@ impl Thread {
|
||||
*old_register = new_register;
|
||||
}
|
||||
|
||||
pub fn is_float_register_closed(&self, register_index: usize) -> bool {
|
||||
let register = if cfg!(debug_assertions) {
|
||||
self.call_stack
|
||||
.last()
|
||||
.unwrap()
|
||||
.registers
|
||||
.floats
|
||||
.get(register_index)
|
||||
.unwrap()
|
||||
} else {
|
||||
unsafe {
|
||||
self.call_stack
|
||||
.last()
|
||||
.unwrap_unchecked()
|
||||
.registers
|
||||
.floats
|
||||
.get_unchecked(register_index)
|
||||
}
|
||||
};
|
||||
|
||||
matches!(register, Register::Closed(_))
|
||||
}
|
||||
|
||||
pub fn close_float_register(&mut self, register_index: usize) {
|
||||
self.current_frame_mut()
|
||||
.registers
|
||||
.floats
|
||||
.get_mut(register_index)
|
||||
.unwrap()
|
||||
.close();
|
||||
}
|
||||
|
||||
pub fn get_integer_register(&self, register_index: usize) -> &i64 {
|
||||
let register = if cfg!(debug_assertions) {
|
||||
self.call_stack
|
||||
@ -416,6 +545,7 @@ impl Thread {
|
||||
|
||||
match register {
|
||||
Register::Value(value) => value,
|
||||
Register::Closed(value) => value,
|
||||
Register::Pointer(pointer) => self.get_pointer_to_integer(pointer),
|
||||
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||
}
|
||||
@ -454,6 +584,38 @@ impl Thread {
|
||||
*old_register = new_register;
|
||||
}
|
||||
|
||||
pub fn is_integer_register_closed(&self, register_index: usize) -> bool {
|
||||
let register = if cfg!(debug_assertions) {
|
||||
self.call_stack
|
||||
.last()
|
||||
.unwrap()
|
||||
.registers
|
||||
.integers
|
||||
.get(register_index)
|
||||
.unwrap()
|
||||
} else {
|
||||
unsafe {
|
||||
self.call_stack
|
||||
.last()
|
||||
.unwrap_unchecked()
|
||||
.registers
|
||||
.integers
|
||||
.get_unchecked(register_index)
|
||||
}
|
||||
};
|
||||
|
||||
matches!(register, Register::Closed(_))
|
||||
}
|
||||
|
||||
pub fn close_integer_register(&mut self, register_index: usize) {
|
||||
self.current_frame_mut()
|
||||
.registers
|
||||
.integers
|
||||
.get_mut(register_index)
|
||||
.unwrap()
|
||||
.close();
|
||||
}
|
||||
|
||||
pub fn get_string_register(&self, register_index: usize) -> &DustString {
|
||||
let register = if cfg!(debug_assertions) {
|
||||
self.call_stack
|
||||
@ -476,6 +638,7 @@ impl Thread {
|
||||
|
||||
match register {
|
||||
Register::Value(value) => value,
|
||||
Register::Closed(value) => value,
|
||||
Register::Pointer(pointer) => self.get_pointer_to_string(pointer),
|
||||
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||
}
|
||||
@ -518,6 +681,48 @@ impl Thread {
|
||||
*old_register = new_register;
|
||||
}
|
||||
|
||||
pub fn is_string_register_closed(&self, register_index: usize) -> bool {
|
||||
let register = if cfg!(debug_assertions) {
|
||||
self.call_stack
|
||||
.last()
|
||||
.unwrap()
|
||||
.registers
|
||||
.strings
|
||||
.get(register_index)
|
||||
.unwrap()
|
||||
} else {
|
||||
unsafe {
|
||||
self.call_stack
|
||||
.last()
|
||||
.unwrap_unchecked()
|
||||
.registers
|
||||
.strings
|
||||
.get_unchecked(register_index)
|
||||
}
|
||||
};
|
||||
|
||||
matches!(register, Register::Closed(_))
|
||||
}
|
||||
|
||||
pub fn close_string_register(&mut self, register_index: usize) {
|
||||
let current_frame = self.current_frame_mut();
|
||||
|
||||
current_frame.registers.strings.push(Register::Empty);
|
||||
|
||||
let old_register = current_frame.registers.strings.swap_remove(register_index);
|
||||
|
||||
if let Register::Value(value) = old_register {
|
||||
current_frame
|
||||
.registers
|
||||
.strings
|
||||
.push(Register::Closed(value));
|
||||
|
||||
let _ = current_frame.registers.strings.swap_remove(register_index);
|
||||
} else {
|
||||
panic!("Attempted to close non-value register");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_list_register(&self, register_index: usize) -> &AbstractList {
|
||||
let register = if cfg!(debug_assertions) {
|
||||
self.call_stack
|
||||
@ -540,6 +745,7 @@ impl Thread {
|
||||
|
||||
match register {
|
||||
Register::Value(value) => value,
|
||||
Register::Closed(value) => value,
|
||||
Register::Pointer(pointer) => self.get_pointer_to_list(pointer),
|
||||
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||
}
|
||||
@ -579,6 +785,45 @@ impl Thread {
|
||||
*old_register = new_register;
|
||||
}
|
||||
|
||||
pub fn is_list_register_closed(&self, register_index: usize) -> bool {
|
||||
let register = if cfg!(debug_assertions) {
|
||||
self.call_stack
|
||||
.last()
|
||||
.unwrap()
|
||||
.registers
|
||||
.lists
|
||||
.get(register_index)
|
||||
.unwrap()
|
||||
} else {
|
||||
unsafe {
|
||||
self.call_stack
|
||||
.last()
|
||||
.unwrap_unchecked()
|
||||
.registers
|
||||
.lists
|
||||
.get_unchecked(register_index)
|
||||
}
|
||||
};
|
||||
|
||||
matches!(register, Register::Closed(_))
|
||||
}
|
||||
|
||||
pub fn close_list_register(&mut self, register_index: usize) {
|
||||
let current_frame = self.current_frame_mut();
|
||||
|
||||
current_frame.registers.lists.push(Register::Empty);
|
||||
|
||||
let old_register = current_frame.registers.lists.swap_remove(register_index);
|
||||
|
||||
if let Register::Value(value) = old_register {
|
||||
current_frame.registers.lists.push(Register::Closed(value));
|
||||
|
||||
let _ = current_frame.registers.lists.swap_remove(register_index);
|
||||
} else {
|
||||
panic!("Attempted to close non-value register");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_constant(&self, constant_index: usize) -> &ConcreteValue {
|
||||
if cfg!(debug_assertions) {
|
||||
self.chunk.constants.get(constant_index).unwrap()
|
||||
|
@ -1,6 +1,6 @@
|
||||
use dust_lang::{
|
||||
AbstractList, Chunk, ConcreteValue, DustString, FunctionType, Instruction, Span, Type, Value,
|
||||
compile, instruction::TypeCode, run, vm::Pointer,
|
||||
Chunk, ConcreteValue, DustString, FunctionType, Instruction, Span, Type, Value, compile,
|
||||
instruction::TypeCode, run,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@ -165,7 +165,7 @@ fn load_boolean_list() {
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::load_encoded(1, false as u8, TypeCode::BOOLEAN, false),
|
||||
Instruction::load_list(0, TypeCode::BOOLEAN, 0, 2, false),
|
||||
Instruction::load_list(0, TypeCode::BOOLEAN, 0, 1, false),
|
||||
Instruction::r#return(true, 0, TypeCode::LIST),
|
||||
],
|
||||
positions: vec![Span(0, 13), Span(13, 13)],
|
||||
@ -191,7 +191,7 @@ fn load_byte_list() {
|
||||
instructions: vec![
|
||||
Instruction::load_encoded(0, 0x2a, TypeCode::BYTE, false),
|
||||
Instruction::load_encoded(1, 0x42, TypeCode::BYTE, false),
|
||||
Instruction::load_list(0, TypeCode::BYTE, 0, 2, false),
|
||||
Instruction::load_list(0, TypeCode::BYTE, 0, 1, false),
|
||||
Instruction::r#return(true, 0, TypeCode::LIST),
|
||||
],
|
||||
positions: vec![Span(0, 15), Span(15, 15)],
|
||||
@ -217,7 +217,7 @@ fn load_character_list() {
|
||||
instructions: vec![
|
||||
Instruction::load_constant(0, 0, TypeCode::CHARACTER, false),
|
||||
Instruction::load_constant(1, 1, TypeCode::CHARACTER, false),
|
||||
Instruction::load_list(0, TypeCode::CHARACTER, 0, 2, false),
|
||||
Instruction::load_list(0, TypeCode::CHARACTER, 0, 1, false),
|
||||
Instruction::r#return(true, 0, TypeCode::LIST),
|
||||
],
|
||||
positions: vec![Span(0, 9), Span(9, 9)],
|
||||
@ -244,7 +244,7 @@ fn load_float_list() {
|
||||
instructions: vec![
|
||||
Instruction::load_constant(0, 0, TypeCode::FLOAT, false),
|
||||
Instruction::load_constant(1, 1, TypeCode::FLOAT, false),
|
||||
Instruction::load_list(0, TypeCode::FLOAT, 0, 2, false),
|
||||
Instruction::load_list(0, TypeCode::FLOAT, 0, 1, false),
|
||||
Instruction::r#return(true, 0, TypeCode::LIST),
|
||||
],
|
||||
positions: vec![Span(0, 15), Span(15, 15)],
|
||||
@ -272,7 +272,7 @@ fn load_integer_list() {
|
||||
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(0, TypeCode::INTEGER, 0, 3, false),
|
||||
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
|
||||
Instruction::r#return(true, 0, TypeCode::LIST),
|
||||
],
|
||||
positions: vec![Span(0, 9), Span(9, 9)],
|
||||
@ -304,7 +304,7 @@ fn load_string_list() {
|
||||
instructions: vec![
|
||||
Instruction::load_constant(0, 0, TypeCode::STRING, false),
|
||||
Instruction::load_constant(1, 1, TypeCode::STRING, false),
|
||||
Instruction::load_list(0, TypeCode::STRING, 0, 2, false),
|
||||
Instruction::load_list(0, TypeCode::STRING, 0, 1, false),
|
||||
Instruction::r#return(true, 0, TypeCode::LIST),
|
||||
],
|
||||
positions: vec![Span(0, 19), Span(19, 19)],
|
||||
@ -334,11 +334,11 @@ fn load_nested_list() {
|
||||
instructions: vec![
|
||||
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
|
||||
Instruction::load_list(0, TypeCode::INTEGER, 0, 1, false),
|
||||
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(1, TypeCode::INTEGER, 2, 2, false),
|
||||
Instruction::load_list(2, TypeCode::LIST, 0, 2, false),
|
||||
Instruction::load_list(1, TypeCode::INTEGER, 2, 3, false),
|
||||
Instruction::load_list(2, TypeCode::LIST, 0, 1, false),
|
||||
Instruction::r#return(true, 2, TypeCode::LIST),
|
||||
],
|
||||
positions: vec![
|
||||
@ -367,3 +367,78 @@ fn load_nested_list() {
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_deeply_nested_list() {
|
||||
let source = "[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]";
|
||||
let chunk = Chunk {
|
||||
r#type: FunctionType {
|
||||
return_type: Type::List(TypeCode::LIST),
|
||||
..FunctionType::default()
|
||||
},
|
||||
instructions: vec![
|
||||
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(0, TypeCode::INTEGER, 0, 1, false),
|
||||
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(1, TypeCode::INTEGER, 2, 3, false),
|
||||
Instruction::load_list(2, TypeCode::LIST, 0, 1, false),
|
||||
Instruction::close(0, 1, TypeCode::LIST),
|
||||
Instruction::load_constant(4, 4, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(5, 5, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(3, TypeCode::INTEGER, 4, 5, false),
|
||||
Instruction::load_constant(6, 6, TypeCode::INTEGER, false),
|
||||
Instruction::load_constant(7, 7, TypeCode::INTEGER, false),
|
||||
Instruction::load_list(4, TypeCode::INTEGER, 6, 7, false),
|
||||
Instruction::load_list(5, TypeCode::LIST, 3, 4, false),
|
||||
Instruction::close(3, 4, TypeCode::LIST),
|
||||
Instruction::load_list(6, TypeCode::LIST, 2, 5, false),
|
||||
Instruction::r#return(true, 6, TypeCode::LIST),
|
||||
],
|
||||
positions: vec![
|
||||
Span(3, 4),
|
||||
Span(6, 7),
|
||||
Span(2, 8),
|
||||
Span(11, 12),
|
||||
Span(14, 15),
|
||||
Span(10, 16),
|
||||
Span(1, 17),
|
||||
Span(17, 18),
|
||||
Span(21, 22),
|
||||
Span(24, 25),
|
||||
Span(20, 26),
|
||||
Span(29, 30),
|
||||
Span(32, 33),
|
||||
Span(28, 34),
|
||||
Span(19, 35),
|
||||
Span(35, 36),
|
||||
Span(0, 36),
|
||||
Span(36, 36),
|
||||
],
|
||||
constants: vec![
|
||||
ConcreteValue::Integer(1),
|
||||
ConcreteValue::Integer(2),
|
||||
ConcreteValue::Integer(3),
|
||||
ConcreteValue::Integer(4),
|
||||
ConcreteValue::Integer(5),
|
||||
ConcreteValue::Integer(6),
|
||||
ConcreteValue::Integer(7),
|
||||
ConcreteValue::Integer(8),
|
||||
],
|
||||
..Chunk::default()
|
||||
};
|
||||
let return_value = Some(Value::Concrete(ConcreteValue::List(vec![
|
||||
ConcreteValue::List(vec![
|
||||
ConcreteValue::List(vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)]),
|
||||
ConcreteValue::List(vec![ConcreteValue::Integer(3), ConcreteValue::Integer(4)]),
|
||||
]),
|
||||
ConcreteValue::List(vec![
|
||||
ConcreteValue::List(vec![ConcreteValue::Integer(5), ConcreteValue::Integer(6)]),
|
||||
ConcreteValue::List(vec![ConcreteValue::Integer(7), ConcreteValue::Integer(8)]),
|
||||
]),
|
||||
])));
|
||||
|
||||
assert_eq!(chunk, compile(source).unwrap());
|
||||
assert_eq!(return_value, run(source).unwrap());
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user