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
|
/// 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
|
/// 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.
|
/// 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 boolean_register_count = self.next_boolean_register() as usize;
|
||||||
let byte_register_count = self.next_byte_register() as usize;
|
let byte_register_count = self.next_byte_register() as usize;
|
||||||
let character_register_count = self.next_character_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)
|
.unwrap_or(self.minimum_string_register)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_list_register(&self) -> u16 {
|
fn next_list_register(&mut self) -> u16 {
|
||||||
self.instructions
|
self.instructions
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
@ -1242,10 +1242,18 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
let mut item_type = Type::None;
|
let mut item_type = Type::None;
|
||||||
let mut start_register = 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.parse_expression()?;
|
||||||
|
self.allow(Token::Comma)?;
|
||||||
|
|
||||||
if item_type == Type::None {
|
if item_type == Type::None {
|
||||||
item_type = self.get_last_instruction_type();
|
item_type = self.get_last_instruction_type();
|
||||||
@ -1272,18 +1280,123 @@ impl<'src> Compiler<'src> {
|
|||||||
start_register = Some(first_index);
|
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 = 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(
|
let load_list = Instruction::load_list(
|
||||||
destination,
|
destination,
|
||||||
item_type.type_code(),
|
item_type.type_code(),
|
||||||
start_register.unwrap_or(0),
|
start_register.unwrap_or(0),
|
||||||
length,
|
end_register,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2,11 +2,12 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
use super::InstructionFields;
|
use super::{InstructionFields, TypeCode};
|
||||||
|
|
||||||
pub struct Close {
|
pub struct Close {
|
||||||
pub from: u16,
|
pub from: u16,
|
||||||
pub to: u16,
|
pub to: u16,
|
||||||
|
pub r#type: TypeCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Close {
|
impl From<Instruction> for Close {
|
||||||
@ -14,6 +15,7 @@ impl From<Instruction> for Close {
|
|||||||
Close {
|
Close {
|
||||||
from: instruction.b_field(),
|
from: instruction.b_field(),
|
||||||
to: instruction.c_field(),
|
to: instruction.c_field(),
|
||||||
|
r#type: instruction.b_type(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,11 +24,13 @@ impl From<Close> for Instruction {
|
|||||||
fn from(close: Close) -> Self {
|
fn from(close: Close) -> Self {
|
||||||
let operation = Operation::CLOSE;
|
let operation = Operation::CLOSE;
|
||||||
let b_field = close.from;
|
let b_field = close.from;
|
||||||
|
let b_type = close.r#type;
|
||||||
let c_field = close.to;
|
let c_field = close.to;
|
||||||
|
|
||||||
InstructionFields {
|
InstructionFields {
|
||||||
operation,
|
operation,
|
||||||
b_field,
|
b_field,
|
||||||
|
b_type,
|
||||||
c_field,
|
c_field,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
@ -36,8 +40,17 @@ impl From<Close> for Instruction {
|
|||||||
|
|
||||||
impl Display for Close {
|
impl Display for Close {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
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 destination: u16,
|
||||||
pub item_type: TypeCode,
|
pub item_type: TypeCode,
|
||||||
pub start_register: u16,
|
pub start_register: u16,
|
||||||
pub length: u16,
|
pub end_register: u16,
|
||||||
pub jump_next: bool,
|
pub jump_next: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,14 +17,14 @@ impl From<Instruction> for LoadList {
|
|||||||
let destination = instruction.a_field();
|
let destination = instruction.a_field();
|
||||||
let start_register = instruction.b_field();
|
let start_register = instruction.b_field();
|
||||||
let item_type = instruction.b_type();
|
let item_type = instruction.b_type();
|
||||||
let length = instruction.c_field();
|
let end_register = instruction.c_field();
|
||||||
let jump_next = instruction.d_field();
|
let jump_next = instruction.d_field();
|
||||||
|
|
||||||
LoadList {
|
LoadList {
|
||||||
destination,
|
destination,
|
||||||
item_type,
|
item_type,
|
||||||
start_register,
|
start_register,
|
||||||
length,
|
end_register,
|
||||||
jump_next,
|
jump_next,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ impl From<LoadList> for Instruction {
|
|||||||
a_field: load_list.destination,
|
a_field: load_list.destination,
|
||||||
b_field: load_list.start_register,
|
b_field: load_list.start_register,
|
||||||
b_type: load_list.item_type,
|
b_type: load_list.item_type,
|
||||||
c_field: load_list.length,
|
c_field: load_list.end_register,
|
||||||
d_field: load_list.jump_next,
|
d_field: load_list.jump_next,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
@ -51,19 +51,38 @@ impl Display for LoadList {
|
|||||||
destination,
|
destination,
|
||||||
item_type,
|
item_type,
|
||||||
start_register,
|
start_register,
|
||||||
length,
|
end_register,
|
||||||
jump_next,
|
jump_next,
|
||||||
} = self;
|
} = self;
|
||||||
let type_caps = item_type.to_string().to_uppercase();
|
|
||||||
|
|
||||||
write!(f, "R_LIST_{destination} = [")?;
|
write!(f, "R_LIST_{destination} = [")?;
|
||||||
|
|
||||||
for (i, register_index) in (*start_register..(start_register + length)).enumerate() {
|
match *item_type {
|
||||||
if i > 0 {
|
TypeCode::BOOLEAN => {
|
||||||
write!(f, ", ")?;
|
write!(f, "R_BOOL_{start_register}..=R_BOOL_{end_register}")?;
|
||||||
}
|
}
|
||||||
|
TypeCode::BYTE => {
|
||||||
write!(f, "R_{type_caps}_{register_index}")?;
|
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, "]")?;
|
write!(f, "]")?;
|
||||||
|
@ -326,8 +326,8 @@ impl Instruction {
|
|||||||
Instruction::from(Point { destination, to })
|
Instruction::from(Point { destination, to })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(from: u16, to: u16) -> Instruction {
|
pub fn close(from: u16, to: u16, r#type: TypeCode) -> Instruction {
|
||||||
Instruction::from(Close { from, to })
|
Instruction::from(Close { from, to, r#type })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_encoded(
|
pub fn load_encoded(
|
||||||
@ -370,14 +370,14 @@ impl Instruction {
|
|||||||
destination: u16,
|
destination: u16,
|
||||||
item_type: TypeCode,
|
item_type: TypeCode,
|
||||||
start_register: u16,
|
start_register: u16,
|
||||||
length: u16,
|
end_register: u16,
|
||||||
jump_next: bool,
|
jump_next: bool,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
Instruction::from(LoadList {
|
Instruction::from(LoadList {
|
||||||
destination,
|
destination,
|
||||||
item_type,
|
item_type,
|
||||||
start_register,
|
start_register,
|
||||||
length,
|
end_register,
|
||||||
jump_next,
|
jump_next,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -583,7 +583,7 @@ impl Instruction {
|
|||||||
| Operation::TEST_SET
|
| Operation::TEST_SET
|
||||||
| Operation::JUMP
|
| Operation::JUMP
|
||||||
| Operation::RETURN => false,
|
| Operation::RETURN => false,
|
||||||
_ => self.operation().panic_from_unknown_code(),
|
unknown => panic!("Unknown operation: {}", unknown.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -592,6 +592,7 @@ impl Instruction {
|
|||||||
|
|
||||||
match operation {
|
match operation {
|
||||||
Operation::POINT => Point::from(*self).to_string(),
|
Operation::POINT => Point::from(*self).to_string(),
|
||||||
|
Operation::CLOSE => Close::from(*self).to_string(),
|
||||||
Operation::LOAD_ENCODED => LoadEncoded::from(*self).to_string(),
|
Operation::LOAD_ENCODED => LoadEncoded::from(*self).to_string(),
|
||||||
Operation::LOAD_CONSTANT => LoadConstant::from(*self).to_string(),
|
Operation::LOAD_CONSTANT => LoadConstant::from(*self).to_string(),
|
||||||
Operation::LOAD_FUNCTION => LoadFunction::from(*self).to_string(),
|
Operation::LOAD_FUNCTION => LoadFunction::from(*self).to_string(),
|
||||||
@ -613,8 +614,7 @@ impl Instruction {
|
|||||||
Operation::CALL_NATIVE => CallNative::from(*self).to_string(),
|
Operation::CALL_NATIVE => CallNative::from(*self).to_string(),
|
||||||
Operation::JUMP => Jump::from(*self).to_string(),
|
Operation::JUMP => Jump::from(*self).to_string(),
|
||||||
Operation::RETURN => Return::from(*self).to_string(),
|
Operation::RETURN => Return::from(*self).to_string(),
|
||||||
|
unknown => panic!("Unknown operation: {}", unknown.0),
|
||||||
_ => operation.panic_from_unknown_code(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,7 @@ impl Operation {
|
|||||||
pub fn name(&self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
match *self {
|
match *self {
|
||||||
Self::POINT => "POINT",
|
Self::POINT => "POINT",
|
||||||
|
Self::CLOSE => "CLOSE",
|
||||||
Self::LOAD_ENCODED => "LOAD_ENCODED",
|
Self::LOAD_ENCODED => "LOAD_ENCODED",
|
||||||
Self::LOAD_CONSTANT => "LOAD_CONSTANT",
|
Self::LOAD_CONSTANT => "LOAD_CONSTANT",
|
||||||
Self::LOAD_FUNCTION => "LOAD_FUNCTION",
|
Self::LOAD_FUNCTION => "LOAD_FUNCTION",
|
||||||
@ -74,7 +75,7 @@ impl Operation {
|
|||||||
Self::CALL_NATIVE => "CALL_NATIVE",
|
Self::CALL_NATIVE => "CALL_NATIVE",
|
||||||
Self::JUMP => "JUMP",
|
Self::JUMP => "JUMP",
|
||||||
Self::RETURN => "RETURN",
|
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
|
Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn panic_from_unknown_code(self) -> ! {
|
|
||||||
panic!("Unknown operation code: {}", self.0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Operation {
|
impl Debug for Operation {
|
||||||
|
@ -70,7 +70,50 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 23] = [
|
|||||||
|
|
||||||
pub fn point(_: InstructionFields, thread: &mut Thread) {}
|
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) {
|
pub fn load_encoded(instruction: InstructionFields, thread: &mut Thread) {
|
||||||
let destination = instruction.a_field;
|
let destination = instruction.a_field;
|
||||||
@ -139,20 +182,79 @@ pub fn load_list(instruction: InstructionFields, thread: &mut Thread) {
|
|||||||
let destination = instruction.a_field;
|
let destination = instruction.a_field;
|
||||||
let start_register = instruction.b_field;
|
let start_register = instruction.b_field;
|
||||||
let item_type = instruction.b_type;
|
let item_type = instruction.b_type;
|
||||||
let length = instruction.c_field;
|
let end_register = instruction.c_field;
|
||||||
let jump_next = instruction.d_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 {
|
let pointer = match item_type {
|
||||||
TypeCode::BOOLEAN => Pointer::RegisterBoolean(register_index as usize),
|
TypeCode::BOOLEAN => {
|
||||||
TypeCode::BYTE => Pointer::RegisterByte(register_index as usize),
|
let is_closed = thread.is_boolean_register_closed(register_index);
|
||||||
TypeCode::CHARACTER => Pointer::RegisterCharacter(register_index as usize),
|
|
||||||
TypeCode::FLOAT => Pointer::RegisterFloat(register_index as usize),
|
if is_closed {
|
||||||
TypeCode::INTEGER => Pointer::RegisterInteger(register_index as usize),
|
continue;
|
||||||
TypeCode::STRING => Pointer::RegisterString(register_index as usize),
|
}
|
||||||
TypeCode::LIST => Pointer::RegisterList(register_index as usize),
|
|
||||||
|
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!(),
|
_ => unimplemented!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -80,18 +80,28 @@ impl Default for RegisterTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Register<T> {
|
pub enum Register<T> {
|
||||||
Empty,
|
Empty,
|
||||||
Value(T),
|
Value(T),
|
||||||
|
Closed(T),
|
||||||
Pointer(Pointer),
|
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> {
|
impl<T: Display> Display for Register<T> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Empty => write!(f, "empty"),
|
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:?})"),
|
Self::Pointer(pointer) => write!(f, "Pointer({pointer:?})"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,7 @@ use std::{collections::HashMap, sync::Arc, thread::JoinHandle};
|
|||||||
|
|
||||||
use tracing::{info, trace};
|
use tracing::{info, trace};
|
||||||
|
|
||||||
use crate::{
|
use crate::{AbstractList, Chunk, ConcreteValue, DustString, Span, Value, vm::CallFrame};
|
||||||
AbstractList, Chunk, ConcreteValue, DustString, Span, Value, instruction::TypeCode,
|
|
||||||
vm::CallFrame,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::call_frame::{Pointer, Register};
|
use super::call_frame::{Pointer, Register};
|
||||||
|
|
||||||
@ -180,6 +177,7 @@ impl Thread {
|
|||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => value,
|
Register::Value(value) => value,
|
||||||
|
Register::Closed(value) => value,
|
||||||
Register::Pointer(pointer) => self.get_pointer_to_boolean(pointer),
|
Register::Pointer(pointer) => self.get_pointer_to_boolean(pointer),
|
||||||
Register::Empty => panic!("Attempted to get value from empty register"),
|
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||||
}
|
}
|
||||||
@ -215,6 +213,38 @@ impl Thread {
|
|||||||
*old_register = new_register;
|
*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 {
|
pub fn get_byte_register(&self, register_index: usize) -> &u8 {
|
||||||
let register = if cfg!(debug_assertions) {
|
let register = if cfg!(debug_assertions) {
|
||||||
self.call_stack
|
self.call_stack
|
||||||
@ -237,6 +267,7 @@ impl Thread {
|
|||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => value,
|
Register::Value(value) => value,
|
||||||
|
Register::Closed(value) => value,
|
||||||
Register::Pointer(pointer) => self.get_pointer_to_byte(pointer),
|
Register::Pointer(pointer) => self.get_pointer_to_byte(pointer),
|
||||||
Register::Empty => panic!("Attempted to get value from empty register"),
|
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||||
}
|
}
|
||||||
@ -272,6 +303,38 @@ impl Thread {
|
|||||||
*old_register = new_register;
|
*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 {
|
pub fn get_character_register(&self, register_index: usize) -> &char {
|
||||||
let register = if cfg!(debug_assertions) {
|
let register = if cfg!(debug_assertions) {
|
||||||
self.call_stack
|
self.call_stack
|
||||||
@ -294,6 +357,7 @@ impl Thread {
|
|||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => value,
|
Register::Value(value) => value,
|
||||||
|
Register::Closed(value) => value,
|
||||||
Register::Pointer(pointer) => self.get_pointer_to_character(pointer),
|
Register::Pointer(pointer) => self.get_pointer_to_character(pointer),
|
||||||
Register::Empty => panic!("Attempted to get value from empty register"),
|
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||||
}
|
}
|
||||||
@ -334,6 +398,38 @@ impl Thread {
|
|||||||
*old_register = new_register;
|
*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 {
|
pub fn get_float_register(&self, register_index: usize) -> &f64 {
|
||||||
let register = if cfg!(debug_assertions) {
|
let register = if cfg!(debug_assertions) {
|
||||||
self.call_stack
|
self.call_stack
|
||||||
@ -356,6 +452,7 @@ impl Thread {
|
|||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => value,
|
Register::Value(value) => value,
|
||||||
|
Register::Closed(value) => value,
|
||||||
Register::Pointer(pointer) => self.get_pointer_to_float(pointer),
|
Register::Pointer(pointer) => self.get_pointer_to_float(pointer),
|
||||||
Register::Empty => panic!("Attempted to get value from empty register"),
|
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||||
}
|
}
|
||||||
@ -394,6 +491,38 @@ impl Thread {
|
|||||||
*old_register = new_register;
|
*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 {
|
pub fn get_integer_register(&self, register_index: usize) -> &i64 {
|
||||||
let register = if cfg!(debug_assertions) {
|
let register = if cfg!(debug_assertions) {
|
||||||
self.call_stack
|
self.call_stack
|
||||||
@ -416,6 +545,7 @@ impl Thread {
|
|||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => value,
|
Register::Value(value) => value,
|
||||||
|
Register::Closed(value) => value,
|
||||||
Register::Pointer(pointer) => self.get_pointer_to_integer(pointer),
|
Register::Pointer(pointer) => self.get_pointer_to_integer(pointer),
|
||||||
Register::Empty => panic!("Attempted to get value from empty register"),
|
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||||
}
|
}
|
||||||
@ -454,6 +584,38 @@ impl Thread {
|
|||||||
*old_register = new_register;
|
*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 {
|
pub fn get_string_register(&self, register_index: usize) -> &DustString {
|
||||||
let register = if cfg!(debug_assertions) {
|
let register = if cfg!(debug_assertions) {
|
||||||
self.call_stack
|
self.call_stack
|
||||||
@ -476,6 +638,7 @@ impl Thread {
|
|||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => value,
|
Register::Value(value) => value,
|
||||||
|
Register::Closed(value) => value,
|
||||||
Register::Pointer(pointer) => self.get_pointer_to_string(pointer),
|
Register::Pointer(pointer) => self.get_pointer_to_string(pointer),
|
||||||
Register::Empty => panic!("Attempted to get value from empty register"),
|
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||||
}
|
}
|
||||||
@ -518,6 +681,48 @@ impl Thread {
|
|||||||
*old_register = new_register;
|
*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 {
|
pub fn get_list_register(&self, register_index: usize) -> &AbstractList {
|
||||||
let register = if cfg!(debug_assertions) {
|
let register = if cfg!(debug_assertions) {
|
||||||
self.call_stack
|
self.call_stack
|
||||||
@ -540,6 +745,7 @@ impl Thread {
|
|||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => value,
|
Register::Value(value) => value,
|
||||||
|
Register::Closed(value) => value,
|
||||||
Register::Pointer(pointer) => self.get_pointer_to_list(pointer),
|
Register::Pointer(pointer) => self.get_pointer_to_list(pointer),
|
||||||
Register::Empty => panic!("Attempted to get value from empty register"),
|
Register::Empty => panic!("Attempted to get value from empty register"),
|
||||||
}
|
}
|
||||||
@ -579,6 +785,45 @@ impl Thread {
|
|||||||
*old_register = new_register;
|
*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 {
|
pub fn get_constant(&self, constant_index: usize) -> &ConcreteValue {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
self.chunk.constants.get(constant_index).unwrap()
|
self.chunk.constants.get(constant_index).unwrap()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use dust_lang::{
|
use dust_lang::{
|
||||||
AbstractList, Chunk, ConcreteValue, DustString, FunctionType, Instruction, Span, Type, Value,
|
Chunk, ConcreteValue, DustString, FunctionType, Instruction, Span, Type, Value, compile,
|
||||||
compile, instruction::TypeCode, run, vm::Pointer,
|
instruction::TypeCode, run,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -165,7 +165,7 @@ fn load_boolean_list() {
|
|||||||
instructions: vec![
|
instructions: vec![
|
||||||
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
|
||||||
Instruction::load_encoded(1, false 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),
|
Instruction::r#return(true, 0, TypeCode::LIST),
|
||||||
],
|
],
|
||||||
positions: vec![Span(0, 13), Span(13, 13)],
|
positions: vec![Span(0, 13), Span(13, 13)],
|
||||||
@ -191,7 +191,7 @@ fn load_byte_list() {
|
|||||||
instructions: vec![
|
instructions: vec![
|
||||||
Instruction::load_encoded(0, 0x2a, TypeCode::BYTE, false),
|
Instruction::load_encoded(0, 0x2a, TypeCode::BYTE, false),
|
||||||
Instruction::load_encoded(1, 0x42, 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),
|
Instruction::r#return(true, 0, TypeCode::LIST),
|
||||||
],
|
],
|
||||||
positions: vec![Span(0, 15), Span(15, 15)],
|
positions: vec![Span(0, 15), Span(15, 15)],
|
||||||
@ -217,7 +217,7 @@ fn load_character_list() {
|
|||||||
instructions: vec![
|
instructions: vec![
|
||||||
Instruction::load_constant(0, 0, TypeCode::CHARACTER, false),
|
Instruction::load_constant(0, 0, TypeCode::CHARACTER, false),
|
||||||
Instruction::load_constant(1, 1, 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),
|
Instruction::r#return(true, 0, TypeCode::LIST),
|
||||||
],
|
],
|
||||||
positions: vec![Span(0, 9), Span(9, 9)],
|
positions: vec![Span(0, 9), Span(9, 9)],
|
||||||
@ -244,7 +244,7 @@ fn load_float_list() {
|
|||||||
instructions: vec![
|
instructions: vec![
|
||||||
Instruction::load_constant(0, 0, TypeCode::FLOAT, false),
|
Instruction::load_constant(0, 0, TypeCode::FLOAT, false),
|
||||||
Instruction::load_constant(1, 1, 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),
|
Instruction::r#return(true, 0, TypeCode::LIST),
|
||||||
],
|
],
|
||||||
positions: vec![Span(0, 15), Span(15, 15)],
|
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(0, 0, TypeCode::INTEGER, false),
|
||||||
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
|
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
|
||||||
Instruction::load_constant(2, 2, 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),
|
Instruction::r#return(true, 0, TypeCode::LIST),
|
||||||
],
|
],
|
||||||
positions: vec![Span(0, 9), Span(9, 9)],
|
positions: vec![Span(0, 9), Span(9, 9)],
|
||||||
@ -304,7 +304,7 @@ fn load_string_list() {
|
|||||||
instructions: vec![
|
instructions: vec![
|
||||||
Instruction::load_constant(0, 0, TypeCode::STRING, false),
|
Instruction::load_constant(0, 0, TypeCode::STRING, false),
|
||||||
Instruction::load_constant(1, 1, 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),
|
Instruction::r#return(true, 0, TypeCode::LIST),
|
||||||
],
|
],
|
||||||
positions: vec![Span(0, 19), Span(19, 19)],
|
positions: vec![Span(0, 19), Span(19, 19)],
|
||||||
@ -334,11 +334,11 @@ fn load_nested_list() {
|
|||||||
instructions: vec![
|
instructions: vec![
|
||||||
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
|
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
|
||||||
Instruction::load_constant(1, 1, 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(2, 2, TypeCode::INTEGER, false),
|
||||||
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
|
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
|
||||||
Instruction::load_list(1, TypeCode::INTEGER, 2, 2, false),
|
Instruction::load_list(1, TypeCode::INTEGER, 2, 3, false),
|
||||||
Instruction::load_list(2, TypeCode::LIST, 0, 2, false),
|
Instruction::load_list(2, TypeCode::LIST, 0, 1, false),
|
||||||
Instruction::r#return(true, 2, TypeCode::LIST),
|
Instruction::r#return(true, 2, TypeCode::LIST),
|
||||||
],
|
],
|
||||||
positions: vec![
|
positions: vec![
|
||||||
@ -367,3 +367,78 @@ fn load_nested_list() {
|
|||||||
assert_eq!(chunk, compile(source).unwrap());
|
assert_eq!(chunk, compile(source).unwrap());
|
||||||
assert_eq!(return_value, run(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