1
0

Begin rewriting tests

This commit is contained in:
Jeff 2025-02-07 15:37:48 -05:00
parent 4b38a93409
commit 8cc5661944
45 changed files with 772 additions and 3735 deletions

View File

@ -40,77 +40,5 @@ name = "threads"
harness = false
[[test]]
name = "logic_and"
path = "tests/logic/and.rs"
[[test]]
name = "logic_and_and"
path = "tests/logic/and_and.rs"
[[test]]
name = "logic_or"
path = "tests/logic/or.rs"
[[test]]
name = "logic_variables"
path = "tests/logic/variables.rs"
[[test]]
name = "math_add"
path = "tests/math/add.rs"
[[test]]
name = "math_add_assign"
path = "tests/math/add_assign.rs"
[[test]]
name = "math_add_errors"
path = "tests/math/add_errors.rs"
[[test]]
name = "math_divide"
path = "tests/math/divide.rs"
[[test]]
name = "math_divide_assign"
path = "tests/math/divide_assign.rs"
[[test]]
name = "math_divide_erros"
path = "tests/math/divide_errors.rs"
[[test]]
name = "math_modulo"
path = "tests/math/modulo.rs"
[[test]]
name = "math_modulo_assign"
path = "tests/math/modulo_assign.rs"
[[test]]
name = "math_modulo_errors"
path = "tests/math/modulo_errors.rs"
[[test]]
name = "math_multiply"
path = "tests/math/multiply.rs"
[[test]]
name = "math_multiply_assign"
path = "tests/math/multiply_assign.rs"
[[test]]
name = "math_multiply_errors"
path = "tests/math/multiply_errors.rs"
[[test]]
name = "math_subtract"
path = "tests/math/subtract.rs"
[[test]]
name = "math_subtract_assign"
path = "tests/math/subtract_assign.rs"
[[test]]
name = "math_subtract_errors"
path = "tests/math/subtract_errors.rs"
name = "load_values"
path = "tests/values/load_values.rs"

View File

@ -16,28 +16,26 @@
//! that implements the [Write][] trait.
//!
//! ```text
//! ╭─────────────────────────────────────────────────────────────────╮
//! │ dust │
//! │ │
//! │ write_line("Hello world!") │
//! │ │
//! │ 3 instructions, 1 constants, 0 locals, returns none │
//! │ │
//! │ Instructions │
//! │ ╭─────┬────────────┬─────────────────┬────────────────────────╮ │
//! │ │ i │ POSITION │ OPERATION │ INFO │ │
//! │ ├─────┼────────────┼─────────────────┼────────────────────────┤ │
//! │ │ 0 │ (11, 25) │ LOAD_CONSTANT │ R0 = C0 │ │
//! │ │ 1 │ (0, 26) │ CALL_NATIVE │ write_line(R0) │ │
//! │ │ 2 │ (26, 26) │ RETURN │ RETURN │ │
//! │ ╰─────┴────────────┴─────────────────┴────────────────────────╯ │
//! │ Constants │
//! │ ╭─────┬──────────────────────────┬──────────────────────────╮ │
//! │ │ i │ TYPE │ VALUE │ │
//! │ ├─────┼──────────────────────────┼──────────────────────────┤ │
//! │ │ 0 │ str │ Hello world! │ │
//! │ ╰─────┴──────────────────────────┴──────────────────────────╯ │
//! ╰─────────────────────────────────────────────────────────────────╯
//! ╭──────────────────────────────────────────────────────────────────────────────────╮
//! │ write_line("hello world") │
//! │ │
//! │ 3 instructions, 1 constants, 0 locals, returns none │
//! │ │
//! │ Instructions │
//! │ ╭─────┬────────────┬─────────────────┬─────────────────────────────────────────╮ │
//! │ │ i │ POSITION │ OPERATION │ INFO │ │
//! │ ├─────┼────────────┼─────────────────┼─────────────────────────────────────────┤ │
//! │ │ 0 │ (11, 24) │ LOAD_CONSTANT │ R_STR_0 = C0 │ │
//! │ │ 1 │ (0, 25) │ CALL_NATIVE │ write_line(R0) │ │
//! │ │ 2 │ (25, 25) │ RETURN │ RETURN │ │
//! │ ╰─────┴────────────┴─────────────────┴─────────────────────────────────────────╯ │
//! │ Constants │
//! │ ╭─────┬──────────────────────────┬──────────────────────────╮ │
//! │ │ i │ TYPE │ VALUE │ │
//! │ ├─────┼──────────────────────────┼──────────────────────────┤ │
//! │ │ 0 │ str │ hello world │ │
//! │ ╰─────┴──────────────────────────┴──────────────────────────╯ │
//! ╰──────────────────────────────────────────────────────────────────────────────────╯
//! ```
use std::io::{self, Write};
@ -411,7 +409,10 @@ impl<'a, W: Write> Disassembler<'a, W> {
if let Some(source) = self.source {
let lazily_formatted = source.split_whitespace().collect::<Vec<&str>>().join(" ");
self.write_center_border("")?;
if self.chunk.name.is_some() {
self.write_center_border("")?;
}
self.write_center_border(&lazily_formatted)?;
self.write_center_border("")?;
}

View File

@ -32,26 +32,26 @@ use crate::{ConcreteValue, DustString, Function, FunctionType, Instruction, Span
/// Representation of a Dust program or function.
///
/// See the [module-level documentation](index.html) for more information.
#[derive(Clone, PartialOrd, Serialize, Deserialize)]
#[derive(Clone, Default, PartialOrd, Serialize, Deserialize)]
pub struct Chunk {
pub(crate) name: Option<DustString>,
pub(crate) r#type: FunctionType,
pub name: Option<DustString>,
pub r#type: FunctionType,
pub(crate) instructions: Vec<Instruction>,
pub(crate) positions: Vec<Span>,
pub(crate) constants: Vec<ConcreteValue>,
pub(crate) locals: Vec<Local>,
pub(crate) prototypes: Vec<Arc<Chunk>>,
pub instructions: Vec<Instruction>,
pub positions: Vec<Span>,
pub constants: Vec<ConcreteValue>,
pub locals: Vec<Local>,
pub prototypes: Vec<Arc<Chunk>>,
pub(crate) boolean_register_count: usize,
pub(crate) byte_register_count: usize,
pub(crate) character_register_count: usize,
pub(crate) float_register_count: usize,
pub(crate) integer_register_count: usize,
pub(crate) string_register_count: usize,
pub(crate) list_register_count: usize,
pub(crate) function_register_count: usize,
pub(crate) prototype_index: u16,
pub boolean_register_count: usize,
pub byte_register_count: usize,
pub character_register_count: usize,
pub float_register_count: usize,
pub integer_register_count: usize,
pub string_register_count: usize,
pub list_register_count: usize,
pub function_register_count: usize,
pub prototype_index: u16,
}
impl Chunk {

View File

@ -32,7 +32,7 @@ use std::{mem::replace, sync::Arc};
use crate::{
Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local,
NativeFunction, Operand, Operation, Scope, Span, Token, TokenKind, Type,
instruction::{CallNative, Jump, LoadList, Point, Return, TypeCode},
instruction::{CallNative, Jump, Point, Return, TypeCode},
};
/// Compiles the input and returns a chunk.
@ -591,7 +591,7 @@ impl<'src> Compiler<'src> {
if let Token::Boolean(text) = self.current_token {
self.advance()?;
let boolean = text.parse::<bool>().unwrap() as u16;
let boolean = text.parse::<bool>().unwrap() as u8;
let destination = self.next_boolean_register();
let load_encoded =
Instruction::load_encoded(destination, boolean, TypeCode::BOOLEAN, false);
@ -615,8 +615,7 @@ impl<'src> Compiler<'src> {
self.advance()?;
let byte = u8::from_str_radix(&text[2..], 16)
.map_err(|error| CompileError::ParseIntError { error, position })?
as u16;
.map_err(|error| CompileError::ParseIntError { error, position })?;
let destination = self.next_byte_register();
let load_encoded = Instruction::load_encoded(destination, byte, TypeCode::BYTE, false);
@ -1002,10 +1001,9 @@ impl<'src> Compiler<'src> {
};
let jump = Instruction::jump(1, true);
let destination = self.next_boolean_register();
let load_true =
Instruction::load_encoded(destination, true as u16, TypeCode::BOOLEAN, true);
let load_true = Instruction::load_encoded(destination, true as u8, TypeCode::BOOLEAN, true);
let load_false =
Instruction::load_encoded(destination, false as u16, TypeCode::BOOLEAN, false);
Instruction::load_encoded(destination, false as u8, TypeCode::BOOLEAN, false);
self.emit_instruction(comparison, Type::Boolean, operator_position);
self.emit_instruction(jump, Type::None, operator_position);
@ -1243,30 +1241,57 @@ impl<'src> Compiler<'src> {
self.advance()?;
let mut item_type = Type::None;
let mut start_register = 0;
let mut start_register = None;
let mut length = 0;
while !self.allow(Token::RightBracket)? && !self.is_eof() {
self.parse_expression()?;
item_type = self.get_last_instruction_type();
start_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,
_ => todo!(),
};
if item_type == Type::None {
item_type = self.get_last_instruction_type();
}
if start_register.is_none() {
let first_index = 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,
Type::None => {
return Err(CompileError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
});
}
_ => todo!(),
};
start_register = Some(first_index);
}
length += 1;
self.allow(Token::Comma)?;
}
let destination = self.next_list_register();
let end = self.previous_position.1;
let load_list = Instruction::load_list(destination, start_register, false);
let load_list = Instruction::load_list(
destination,
item_type.type_code(),
start_register.unwrap_or(0),
length,
false,
);
self.emit_instruction(load_list, Type::List(Box::new(item_type)), Span(start, end));
self.emit_instruction(
load_list,
Type::List(item_type.type_code()),
Span(start, end),
);
Ok(())
}
@ -1478,6 +1503,7 @@ impl<'src> Compiler<'src> {
Type::Float => self.next_float_register(),
Type::Integer => self.next_integer_register(),
Type::String => self.next_string_register(),
Type::None => 0,
_ => todo!(),
};
let return_type = function.r#type().return_type;

View File

@ -6,7 +6,7 @@ use super::{InstructionFields, TypeCode};
pub struct LoadEncoded {
pub destination: u16,
pub value: u16,
pub value: u8,
pub value_type: TypeCode,
pub jump_next: bool,
}
@ -15,7 +15,7 @@ impl From<Instruction> for LoadEncoded {
fn from(instruction: Instruction) -> Self {
LoadEncoded {
destination: instruction.a_field(),
value: instruction.b_field(),
value: instruction.b_field() as u8,
value_type: instruction.b_type(),
jump_next: instruction.c_field() != 0,
}
@ -26,7 +26,7 @@ impl From<LoadEncoded> for Instruction {
fn from(load_boolean: LoadEncoded) -> Self {
let operation = Operation::LOAD_ENCODED;
let a_field = load_boolean.destination;
let b_field = load_boolean.value;
let b_field = load_boolean.value as u16;
let b_type = load_boolean.value_type;
let c_field = load_boolean.jump_next as u16;
@ -57,11 +57,7 @@ impl Display for LoadEncoded {
write!(f, "R_BOOL_{destination} = {boolean}")?
}
TypeCode::BYTE => {
let byte = *value as u8;
write!(f, "R_BYTE_{destination} = 0x{byte:0X}")?
}
TypeCode::BYTE => write!(f, "R_BYTE_{destination} = 0x{value:0X}")?,
_ => panic!("Invalid type code {value_type} for LoadEncoded instruction"),
}

View File

@ -2,11 +2,13 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation};
use super::InstructionFields;
use super::{InstructionFields, TypeCode};
pub struct LoadList {
pub destination: u16,
pub item_type: TypeCode,
pub start_register: u16,
pub length: u16,
pub jump_next: bool,
}
@ -14,11 +16,15 @@ impl From<Instruction> for LoadList {
fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field();
let start_register = instruction.b_field();
let jump_next = instruction.c_field() != 0;
let item_type = instruction.b_type();
let length = instruction.c_field();
let jump_next = instruction.d_field();
LoadList {
destination,
item_type,
start_register,
length,
jump_next,
}
}
@ -30,7 +36,9 @@ impl From<LoadList> for Instruction {
operation: Operation::LOAD_LIST,
a_field: load_list.destination,
b_field: load_list.start_register,
c_field: load_list.jump_next as u16,
b_type: load_list.item_type,
c_field: load_list.length,
d_field: load_list.jump_next,
..Default::default()
}
.build()
@ -41,11 +49,24 @@ impl Display for LoadList {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let LoadList {
destination,
item_type,
start_register,
length,
jump_next,
} = self;
let type_caps = item_type.to_string().to_uppercase();
write!(f, "R{destination} = [R{start_register}..R{destination}]")?;
write!(f, "R_LIST_{destination} = [")?;
for (i, register_index) in (*start_register..(start_register + length)).enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "R_{type_caps}_{register_index}")?;
}
write!(f, "]")?;
if *jump_next {
write!(f, " JUMP +1")?;

View File

@ -332,7 +332,7 @@ impl Instruction {
pub fn load_encoded(
destination: u16,
value: u16,
value: u8,
value_type: TypeCode,
jump_next: bool,
) -> Instruction {
@ -366,10 +366,18 @@ impl Instruction {
})
}
pub fn load_list(destination: u16, start_register: u16, jump_next: bool) -> Instruction {
pub fn load_list(
destination: u16,
item_type: TypeCode,
start_register: u16,
length: u16,
jump_next: bool,
) -> Instruction {
Instruction::from(LoadList {
destination,
item_type,
start_register,
length,
jump_next,
})
}

View File

@ -51,13 +51,17 @@ impl Display for Return {
} = self;
if *should_return_value {
write!(f, "RETURN ")?;
match *r#type {
TypeCode::BOOLEAN => write!(f, "RETURN R_BOOL_{return_register}"),
TypeCode::BYTE => write!(f, "RETURN R_BYTE_{return_register}"),
TypeCode::CHARACTER => write!(f, "RETURN R_CHAR_{return_register}"),
TypeCode::FLOAT => write!(f, "RETURN R_FLOAT_{return_register}"),
TypeCode::INTEGER => write!(f, "RETURN R_INT_{return_register}"),
TypeCode::STRING => write!(f, "RETURN R_STR_{return_register}"),
TypeCode::BOOLEAN => write!(f, "R_BOOL_{return_register}"),
TypeCode::BYTE => write!(f, "R_BYTE_{return_register}"),
TypeCode::CHARACTER => write!(f, "R_CHAR_{return_register}"),
TypeCode::FLOAT => write!(f, "R_FLOAT_{return_register}"),
TypeCode::INTEGER => write!(f, "R_INT_{return_register}"),
TypeCode::STRING => write!(f, "R_STR_{return_register}"),
TypeCode::LIST => write!(f, "R_LIST_{return_register}"),
TypeCode::FUNCTION => write!(f, "R_FN_{return_register}"),
unsupported => unreachable!("Unsupported return type: {:?}", unsupported),
}
} else {

View File

@ -1,6 +1,8 @@
use std::fmt::Display;
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct TypeCode(pub u8);
impl TypeCode {
@ -28,6 +30,8 @@ impl Display for TypeCode {
TypeCode::FLOAT => write!(f, "float"),
TypeCode::INTEGER => write!(f, "int"),
TypeCode::STRING => write!(f, "str"),
TypeCode::LIST => write!(f, "list"),
TypeCode::FUNCTION => write!(f, "fn"),
_ => self.panic_from_unknown_code(),
}
}

View File

@ -24,7 +24,7 @@ pub enum Type {
concrete_type: Option<Box<Type>>,
},
Integer,
List(Box<Type>),
List(TypeCode),
Map {
pairs: Vec<(u8, Type)>,
},
@ -45,10 +45,6 @@ impl Type {
Type::Function(Box::new(function_type))
}
pub fn list(element_type: Type) -> Self {
Type::List(Box::new(element_type))
}
pub fn type_code(&self) -> TypeCode {
match self {
Type::Boolean => TypeCode::BOOLEAN,
@ -58,6 +54,8 @@ impl Type {
Type::Integer => TypeCode::INTEGER,
Type::None => TypeCode::NONE,
Type::String => TypeCode::STRING,
Type::List(_) => TypeCode::LIST,
Type::Function(_) => TypeCode::FUNCTION,
_ => todo!(),
}
}
@ -121,7 +119,7 @@ impl Type {
}
}
(Type::List(left_type), Type::List(right_type)) => {
if left_type.check(right_type).is_err() {
if left_type != right_type {
return Err(TypeConflict {
actual: other.clone(),
expected: self.clone(),
@ -307,6 +305,16 @@ impl FunctionType {
}
}
impl Default for FunctionType {
fn default() -> Self {
FunctionType {
type_parameters: Vec::new(),
value_parameters: Vec::new(),
return_type: Type::None,
}
}
}
impl Display for FunctionType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "fn ")?;

View File

@ -2,6 +2,7 @@ use std::fmt::{self, Display, Formatter};
use crate::{
Type,
instruction::TypeCode,
vm::{Pointer, Thread},
};
@ -9,7 +10,7 @@ use super::DustString;
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct AbstractList {
pub item_type: Type,
pub item_type: TypeCode,
pub item_pointers: Vec<Pointer>,
}
@ -25,12 +26,12 @@ impl AbstractList {
}
let item_display = match self.item_type {
Type::Boolean => thread.get_pointer_to_boolean(pointer).to_string(),
Type::Byte => thread.get_pointer_to_byte(pointer).to_string(),
Type::Character => thread.get_pointer_to_character(pointer).to_string(),
Type::Float => thread.get_pointer_to_float(pointer).to_string(),
Type::Integer => thread.get_pointer_to_integer(pointer).to_string(),
Type::String => thread.get_pointer_to_string(pointer).to_string(),
TypeCode::BOOLEAN => thread.get_pointer_to_boolean(pointer).to_string(),
TypeCode::BYTE => thread.get_pointer_to_byte(pointer).to_string(),
TypeCode::CHARACTER => thread.get_pointer_to_character(pointer).to_string(),
TypeCode::FLOAT => thread.get_pointer_to_float(pointer).to_string(),
TypeCode::INTEGER => thread.get_pointer_to_integer(pointer).to_string(),
TypeCode::STRING => thread.get_pointer_to_string(pointer).to_string(),
_ => todo!(),
};

View File

@ -111,9 +111,12 @@ impl ConcreteValue {
ConcreteValue::Float(_) => Type::Float,
ConcreteValue::Integer(_) => Type::Integer,
ConcreteValue::List(list) => {
let item_type = list.first().map_or(Type::Any, |item| item.r#type());
let item_type = list
.first()
.map_or(Type::Any, |item| item.r#type())
.type_code();
Type::List(Box::new(item_type))
Type::List(item_type)
}
ConcreteValue::Range(range) => range.r#type(),
ConcreteValue::String(_) => Type::String,

View File

@ -117,9 +117,7 @@ impl Value {
pub fn r#type(&self) -> Type {
match self {
Value::Concrete(concrete_value) => concrete_value.r#type(),
Value::AbstractList(AbstractList { item_type, .. }) => {
Type::List(Box::new(item_type.clone()))
}
Value::AbstractList(AbstractList { item_type, .. }) => Type::List(*item_type),
Value::Function(Function { r#type, .. }) => Type::Function(Box::new(r#type.clone())),
}
}

View File

@ -1,5 +1,5 @@
use crate::{
Instruction, Value,
AbstractList, ConcreteValue, Instruction, Value,
instruction::{InstructionFields, TypeCode},
};
@ -123,7 +123,7 @@ pub fn load_constant(instruction: InstructionFields, thread: &mut Thread) {
thread.set_integer_register(destination, register);
}
TypeCode::STRING => {
let register = Register::Pointer(Pointer::Constant(constant_index));
let register = Register::Pointer(Pointer::ConstantString(constant_index));
thread.set_string_register(destination, register);
}
@ -135,7 +135,42 @@ pub fn load_constant(instruction: InstructionFields, thread: &mut Thread) {
}
}
pub fn load_list(instruction: InstructionFields, thread: &mut Thread) {}
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 jump_next = instruction.d_field;
let mut item_pointers = Vec::with_capacity(length 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),
_ => unimplemented!(),
};
item_pointers.push(pointer);
}
let abstract_list = AbstractList {
item_type,
item_pointers,
};
let register = Register::Value(abstract_list);
thread.set_list_register(destination as usize, register);
if jump_next {
thread.current_frame_mut().ip += 1;
}
}
pub fn load_function(instruction: InstructionFields, thread: &mut Thread) {}
@ -369,6 +404,17 @@ pub fn r#return(instruction: InstructionFields, thread: &mut Thread) {
let return_value = thread.get_string_register(return_register).clone();
thread.return_value = Some(Some(Value::string(return_value)));
}
TypeCode::LIST => {
let abstract_list = thread.get_list_register(return_register).clone();
let mut concrete_list = Vec::with_capacity(abstract_list.item_pointers.len());
for pointer in &abstract_list.item_pointers {
concrete_list.push(thread.get_value_from_pointer(pointer));
}
thread.return_value =
Some(Some(Value::Concrete(ConcreteValue::List(concrete_list))));
}
_ => unimplemented!(),
}
} else {

View File

@ -5,7 +5,7 @@ use std::{
use smallvec::{SmallVec, smallvec};
use crate::{Chunk, DustString};
use crate::{AbstractList, Chunk, DustString, Function};
use super::action::ActionSequence;
@ -55,6 +55,8 @@ pub struct RegisterTable {
pub floats: SmallVec<[Register<f64>; 64]>,
pub integers: SmallVec<[Register<i64>; 64]>,
pub strings: SmallVec<[Register<DustString>; 64]>,
pub lists: SmallVec<[Register<AbstractList>; 64]>,
pub functions: SmallVec<[Register<Function>; 64]>,
}
impl RegisterTable {
@ -66,6 +68,8 @@ impl RegisterTable {
floats: smallvec![Register::Empty; 64],
integers: smallvec![Register::Empty; 64],
strings: smallvec![Register::Empty; 64],
lists: smallvec![Register::Empty; 64],
functions: smallvec![Register::Empty; 64],
}
}
}
@ -88,26 +92,42 @@ impl<T: Display> Display for Register<T> {
match self {
Self::Empty => write!(f, "empty"),
Self::Value(value) => write!(f, "{value}"),
Self::Pointer(pointer) => write!(f, "{pointer}"),
Self::Pointer(pointer) => write!(f, "Pointer({pointer:?})"),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Pointer {
Register(usize),
Constant(usize),
Stack(usize, usize),
RegisterBoolean(usize),
RegisterByte(usize),
RegisterCharacter(usize),
RegisterFloat(usize),
RegisterInteger(usize),
RegisterString(usize),
RegisterList(usize),
RegisterFunction(usize),
ConstantCharacter(usize),
ConstantFloat(usize),
ConstantInteger(usize),
ConstantString(usize),
}
impl Display for Pointer {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Register(index) => write!(f, "PR{}", index),
Self::Constant(index) => write!(f, "PC{}", index),
Self::Stack(call_index, register_index) => {
write!(f, "PS{}R{}", call_index, register_index)
}
Self::RegisterBoolean(index) => write!(f, "P_R_BOOL_{index}"),
Self::RegisterByte(index) => write!(f, "P_R_BYTE_{index}"),
Self::RegisterCharacter(index) => write!(f, "P_R_CHAR_{index}"),
Self::RegisterFloat(index) => write!(f, "P_R_FLOAT_{index}"),
Self::RegisterInteger(index) => write!(f, "P_R_INT_{index}"),
Self::RegisterString(index) => write!(f, "P_R_STR_{index}"),
Self::RegisterList(index) => write!(f, "P_R_LIST_{index}"),
Self::RegisterFunction(index) => write!(f, "P_R_FN_{index}"),
Self::ConstantCharacter(index) => write!(f, "P_C_CHAR_{index}"),
Self::ConstantFloat(index) => write!(f, "P_C_FLOAT_{index}"),
Self::ConstantInteger(index) => write!(f, "P_C_INT_{index}"),
Self::ConstantString(index) => write!(f, "P_C_STR_{index}"),
}
}
}

View File

@ -2,7 +2,10 @@ use std::{collections::HashMap, sync::Arc, thread::JoinHandle};
use tracing::{info, trace};
use crate::{Chunk, ConcreteValue, DustString, Span, Value, vm::CallFrame};
use crate::{
AbstractList, Chunk, ConcreteValue, DustString, Span, Value, instruction::TypeCode,
vm::CallFrame,
};
use super::call_frame::{Pointer, Register};
@ -85,6 +88,76 @@ impl Thread {
}
}
pub fn get_value_from_pointer(&self, pointer: &Pointer) -> ConcreteValue {
match pointer {
Pointer::RegisterBoolean(register_index) => {
let boolean = *self.get_boolean_register(*register_index);
ConcreteValue::Boolean(boolean)
}
Pointer::RegisterByte(register_index) => {
let byte = *self.get_byte_register(*register_index);
ConcreteValue::Byte(byte)
}
Pointer::RegisterCharacter(register_index) => {
let character = *self.get_character_register(*register_index);
ConcreteValue::Character(character)
}
Pointer::RegisterFloat(register_index) => {
let float = *self.get_float_register(*register_index);
ConcreteValue::Float(float)
}
Pointer::RegisterInteger(register_index) => {
let integer = *self.get_integer_register(*register_index);
ConcreteValue::Integer(integer)
}
Pointer::RegisterString(register_index) => {
let string = self.get_string_register(*register_index).clone();
ConcreteValue::String(string)
}
Pointer::RegisterList(register_index) => {
let abstract_list = self.get_list_register(*register_index).clone();
let mut concrete_list = Vec::with_capacity(abstract_list.item_pointers.len());
for pointer in &abstract_list.item_pointers {
concrete_list.push(self.get_value_from_pointer(pointer));
}
ConcreteValue::List(concrete_list)
}
Pointer::RegisterFunction(_) => unimplemented!(),
Pointer::ConstantCharacter(constant_index) => {
let character = *self.get_constant(*constant_index).as_character().unwrap();
ConcreteValue::Character(character)
}
Pointer::ConstantFloat(constant_index) => {
let float = *self.get_constant(*constant_index).as_float().unwrap();
ConcreteValue::Float(float)
}
Pointer::ConstantInteger(constant_index) => {
let integer = *self.get_constant(*constant_index).as_integer().unwrap();
ConcreteValue::Integer(integer)
}
Pointer::ConstantString(constant_index) => {
let string = self
.get_constant(*constant_index)
.as_string()
.unwrap()
.clone();
ConcreteValue::String(string)
}
}
}
pub fn get_boolean_register(&self, register_index: usize) -> &bool {
let register = if cfg!(debug_assertions) {
self.call_stack
@ -114,28 +187,8 @@ impl Thread {
pub fn get_pointer_to_boolean(&self, pointer: &Pointer) -> &bool {
match pointer {
Pointer::Register(register_index) => self.get_boolean_register(*register_index),
Pointer::Constant(constant_index) => {
self.get_constant(*constant_index).as_boolean().unwrap()
}
Pointer::Stack(call_index, register_index) => {
let call_frame = if cfg!(debug_assertions) {
self.call_stack.get(*call_index).unwrap()
} else {
unsafe { self.call_stack.get_unchecked(*call_index) }
};
let register = if cfg!(debug_assertions) {
call_frame.registers.booleans.get(*register_index).unwrap()
} else {
unsafe { call_frame.registers.booleans.get_unchecked(*register_index) }
};
match register {
Register::Value(value) => value,
Register::Pointer(pointer) => self.get_pointer_to_boolean(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
}
Pointer::RegisterBoolean(register_index) => self.get_boolean_register(*register_index),
_ => panic!("Attempted to get boolean from non-boolean pointer"),
}
}
@ -191,28 +244,8 @@ impl Thread {
pub fn get_pointer_to_byte(&self, pointer: &Pointer) -> &u8 {
match pointer {
Pointer::Register(register_index) => self.get_byte_register(*register_index),
Pointer::Constant(constant_index) => {
self.get_constant(*constant_index).as_byte().unwrap()
}
Pointer::Stack(call_index, register_index) => {
let call_frame = if cfg!(debug_assertions) {
self.call_stack.get(*call_index).unwrap()
} else {
unsafe { self.call_stack.get_unchecked(*call_index) }
};
let register = if cfg!(debug_assertions) {
call_frame.registers.bytes.get(*register_index).unwrap()
} else {
unsafe { call_frame.registers.bytes.get_unchecked(*register_index) }
};
match register {
Register::Value(value) => value,
Register::Pointer(pointer) => self.get_pointer_to_byte(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
}
Pointer::RegisterByte(register_index) => self.get_byte_register(*register_index),
_ => panic!("Attempted to get byte from non-byte pointer"),
}
}
@ -268,37 +301,13 @@ impl Thread {
pub fn get_pointer_to_character(&self, pointer: &Pointer) -> &char {
match pointer {
Pointer::Register(register_index) => self.get_character_register(*register_index),
Pointer::Constant(constant_index) => {
Pointer::RegisterCharacter(register_index) => {
self.get_character_register(*register_index)
}
Pointer::ConstantCharacter(constant_index) => {
self.get_constant(*constant_index).as_character().unwrap()
}
Pointer::Stack(call_index, register_index) => {
let call_frame = if cfg!(debug_assertions) {
self.call_stack.get(*call_index).unwrap()
} else {
unsafe { self.call_stack.get_unchecked(*call_index) }
};
let register = if cfg!(debug_assertions) {
call_frame
.registers
.characters
.get(*register_index)
.unwrap()
} else {
unsafe {
call_frame
.registers
.characters
.get_unchecked(*register_index)
}
};
match register {
Register::Value(value) => value,
Register::Pointer(pointer) => self.get_pointer_to_character(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
}
_ => panic!("Attempted to get character from non-character pointer"),
}
}
@ -354,28 +363,11 @@ impl Thread {
pub fn get_pointer_to_float(&self, pointer: &Pointer) -> &f64 {
match pointer {
Pointer::Register(register_index) => self.get_float_register(*register_index),
Pointer::Constant(constant_index) => {
Pointer::RegisterFloat(register_index) => self.get_float_register(*register_index),
Pointer::ConstantFloat(constant_index) => {
self.get_constant(*constant_index).as_float().unwrap()
}
Pointer::Stack(call_index, register_index) => {
let call_frame = if cfg!(debug_assertions) {
self.call_stack.get(*call_index).unwrap()
} else {
unsafe { self.call_stack.get_unchecked(*call_index) }
};
let register = if cfg!(debug_assertions) {
call_frame.registers.floats.get(*register_index).unwrap()
} else {
unsafe { call_frame.registers.floats.get_unchecked(*register_index) }
};
match register {
Register::Value(value) => value,
Register::Pointer(pointer) => self.get_pointer_to_float(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
}
_ => panic!("Attempted to get float from non-float pointer"),
}
}
@ -431,28 +423,11 @@ impl Thread {
pub fn get_pointer_to_integer(&self, pointer: &Pointer) -> &i64 {
match pointer {
Pointer::Register(register_index) => self.get_integer_register(*register_index),
Pointer::Constant(constant_index) => {
Pointer::RegisterInteger(register_index) => self.get_integer_register(*register_index),
Pointer::ConstantInteger(constant_index) => {
self.get_constant(*constant_index).as_integer().unwrap()
}
Pointer::Stack(call_index, register_index) => {
let call_frame = if cfg!(debug_assertions) {
self.call_stack.get(*call_index).unwrap()
} else {
unsafe { self.call_stack.get_unchecked(*call_index) }
};
let register = if cfg!(debug_assertions) {
call_frame.registers.integers.get(*register_index).unwrap()
} else {
unsafe { call_frame.registers.integers.get_unchecked(*register_index) }
};
match register {
Register::Value(value) => value,
Register::Pointer(pointer) => self.get_pointer_to_integer(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
}
_ => panic!("Attempted to get integer from non-integer pointer"),
}
}
@ -508,28 +483,11 @@ impl Thread {
pub fn get_pointer_to_string(&self, pointer: &Pointer) -> &DustString {
match pointer {
Pointer::Register(register_index) => self.get_string_register(*register_index),
Pointer::Constant(constant_index) => {
Pointer::RegisterString(register_index) => self.get_string_register(*register_index),
Pointer::ConstantString(constant_index) => {
self.get_constant(*constant_index).as_string().unwrap()
}
Pointer::Stack(call_index, register_index) => {
let call_frame = if cfg!(debug_assertions) {
self.call_stack.get(*call_index).unwrap()
} else {
unsafe { self.call_stack.get_unchecked(*call_index) }
};
let register = if cfg!(debug_assertions) {
call_frame.registers.strings.get(*register_index).unwrap()
} else {
unsafe { call_frame.registers.strings.get_unchecked(*register_index) }
};
match register {
Register::Value(value) => value,
Register::Pointer(pointer) => self.get_pointer_to_string(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
}
_ => panic!("Attempted to get string from non-string pointer"),
}
}
@ -560,6 +518,67 @@ impl Thread {
*old_register = new_register;
}
pub fn get_list_register(&self, register_index: usize) -> &AbstractList {
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)
}
};
match register {
Register::Value(value) => value,
Register::Pointer(pointer) => self.get_pointer_to_list(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
}
pub fn get_pointer_to_list(&self, pointer: &Pointer) -> &AbstractList {
match pointer {
Pointer::RegisterList(register_index) => self.get_list_register(*register_index),
_ => panic!("Attempted to get list from non-list pointer"),
}
}
pub fn set_list_register(
&mut self,
register_index: usize,
new_register: Register<AbstractList>,
) {
let old_register = if cfg!(debug_assertions) {
self.call_stack
.last_mut()
.unwrap()
.registers
.lists
.get_mut(register_index)
.unwrap()
} else {
unsafe {
self.call_stack
.last_mut()
.unwrap_unchecked()
.registers
.lists
.get_unchecked_mut(register_index)
}
};
*old_register = new_register;
}
pub fn get_constant(&self, constant_index: usize) -> &ConcreteValue {
if cfg!(debug_assertions) {
self.chunk.constants.get(constant_index).unwrap()

View File

@ -1,81 +0,0 @@
use dust_lang::*;
#[test]
fn add_assign_expects_mutable_variable() {
let source = "1 += 2";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::ExpectedMutableVariable {
found: Token::Integer("1").to_owned(),
position: Span(0, 1)
},
source
})
);
}
#[test]
fn divide_assign_expects_mutable_variable() {
let source = "1 -= 2";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::ExpectedMutableVariable {
found: Token::Integer("1").to_owned(),
position: Span(0, 1)
},
source
})
);
}
#[test]
fn multiply_assign_expects_mutable_variable() {
let source = "1 *= 2";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::ExpectedMutableVariable {
found: Token::Integer("1").to_owned(),
position: Span(0, 1)
},
source
})
);
}
#[test]
fn subtract_assign_expects_mutable_variable() {
let source = "1 -= 2";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::ExpectedMutableVariable {
found: Token::Integer("1").to_owned(),
position: Span(0, 1)
},
source
})
);
}
#[test]
fn modulo_assign_expects_mutable_variable() {
let source = "1 %= 2";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::ExpectedMutableVariable {
found: Token::Integer("1").to_owned(),
position: Span(0, 1)
},
source
})
);
}

View File

@ -1,129 +0,0 @@
use dust_lang::*;
#[test]
fn constant() {
let source = "42";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer
},
vec![
(Instruction::load_constant(0, 0, false), Span(0, 2)),
(Instruction::r#return(true), Span(2, 2))
],
vec![ConcreteValue::Integer(42)],
vec![]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(42))));
}
#[test]
fn empty() {
let source = "";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None
},
vec![(Instruction::r#return(false), Span(0, 0))],
vec![],
vec![]
))
);
assert_eq!(run(source), Ok(None));
}
#[test]
fn parentheses_precedence() {
let source = "(1 + 2) * 3";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer
},
vec![
(
Instruction::add(0, Operand::Constant(0), Operand::Constant(1)),
Span(3, 4)
),
(
Instruction::multiply(1, Operand::Register(0), Operand::Constant(2)),
Span(8, 9)
),
(Instruction::r#return(true), Span(11, 11)),
],
vec![
ConcreteValue::Integer(1),
ConcreteValue::Integer(2),
ConcreteValue::Integer(3)
],
vec![]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(9))));
}
#[test]
fn math_operator_precedence() {
let source = "1 + 2 - 3 * 4 / 5";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer,
},
vec![
(
Instruction::add(0, Operand::Constant(0), Operand::Constant(1)),
Span(2, 3)
),
(
Instruction::multiply(1, Operand::Constant(2), Operand::Constant(3)),
Span(10, 11)
),
(
Instruction::divide(2, Operand::Register(1), Operand::Constant(4)),
Span(14, 15)
),
(
Instruction::subtract(3, Operand::Register(0), Operand::Register(2)),
Span(6, 7)
),
(Instruction::r#return(true), Span(17, 17)),
],
vec![
ConcreteValue::Integer(1),
ConcreteValue::Integer(2),
ConcreteValue::Integer(3),
ConcreteValue::Integer(4),
ConcreteValue::Integer(5),
],
vec![]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(1))));
}

View File

@ -1,169 +0,0 @@
use dust_lang::*;
#[test]
fn equal() {
let source = "1 == 2";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Boolean
},
vec![
(
Instruction::equal(0, true, Operand::Constant(0), Operand::Constant(1)),
Span(2, 4)
),
(Instruction::r#return(true), Span(6, 6)),
],
vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)],
vec![]
)),
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false))));
}
#[test]
fn greater() {
let source = "1 > 2";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Boolean
},
vec![
(
Instruction::less_equal(0, false, Operand::Constant(0), Operand::Constant(1)),
Span(2, 3)
),
(Instruction::r#return(true), Span(5, 5)),
],
vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)],
vec![]
)),
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false))));
}
#[test]
fn greater_than_or_equal() {
let source = "1 >= 2";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Boolean
},
vec![
(
Instruction::less(0, false, Operand::Constant(0), Operand::Constant(1)),
Span(2, 4)
),
(Instruction::r#return(true), Span(6, 6)),
],
vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)],
vec![]
)),
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false))));
}
#[test]
fn less_than() {
let source = "1 < 2";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Boolean
},
vec![
(
Instruction::less(0, true, Operand::Constant(0), Operand::Constant(1)),
Span(2, 3)
),
(Instruction::r#return(true), Span(5, 5)),
],
vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)],
vec![]
)),
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(true))));
}
#[test]
fn less_than_or_equal() {
let source = "1 <= 2";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Boolean
},
vec![
(
Instruction::less_equal(0, true, Operand::Constant(0), Operand::Constant(1)),
Span(2, 4)
),
(Instruction::r#return(true), Span(6, 6)),
],
vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)],
vec![]
)),
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(true))));
}
#[test]
fn not_equal() {
let source = "1 != 2";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Boolean
},
vec![
(
Instruction::equal(0, false, Operand::Constant(0), Operand::Constant(1)),
Span(2, 4)
),
(Instruction::r#return(true), Span(6, 6)),
],
vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)],
vec![]
)),
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(true))));
}

View File

@ -1,133 +0,0 @@
use dust_lang::*;
use smallvec::smallvec;
#[test]
fn function() {
let source = "fn(a: int, b: int) -> int { a + b }";
assert_eq!(
run(source),
Ok(Some(ConcreteValue::function(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::function(FunctionType {
type_parameters: None,
value_parameters: Some(smallvec![(0, Type::Integer), (1, Type::Integer)]),
return_type: Type::Integer,
})
},
vec![
(
Instruction::add(2, Operand::Register(0), Operand::Register(1)),
Span(30, 31)
),
(Instruction::r#return(true), Span(34, 35)),
],
vec![ConcreteValue::string("a"), ConcreteValue::string("b"),],
vec![
Local::new(0, 0, false, Scope::default()),
Local::new(1, 1, false, Scope::default())
]
))))
);
}
#[test]
fn function_call() {
let source = "fn(a: int, b: int) -> int { a + b }(1, 2)";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer
},
vec![
(Instruction::load_constant(0, 0, false), Span(0, 35)),
(Instruction::load_constant(1, 1, false), Span(36, 37)),
(Instruction::load_constant(2, 2, false), Span(39, 40)),
(Instruction::call(3, Operand::Constant(0), 2), Span(35, 41)),
(Instruction::r#return(true), Span(41, 41)),
],
vec![
ConcreteValue::function(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: Some(smallvec![(0, Type::Integer), (1, Type::Integer)]),
return_type: Type::Integer
},
vec![
(
Instruction::add(2, Operand::Register(0), Operand::Register(1)),
Span(30, 31)
),
(Instruction::r#return(true), Span(34, 35)),
],
vec![ConcreteValue::string("a"), ConcreteValue::string("b"),],
vec![
Local::new(0, 0, false, Scope::default()),
Local::new(1, 1, false, Scope::default())
]
)),
ConcreteValue::Integer(1),
ConcreteValue::Integer(2)
],
vec![]
)),
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(3))));
}
#[test]
fn function_declaration() {
let source = "fn add (a: int, b: int) -> int { a + b }";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None
},
vec![
(Instruction::load_constant(0, 0, false), Span(0, 40)),
(Instruction::r#return(false), Span(40, 40))
],
vec![
ConcreteValue::function(Chunk::with_data(
Some("add".into()),
FunctionType {
type_parameters: None,
value_parameters: Some(smallvec![(0, Type::Integer), (1, Type::Integer)]),
return_type: Type::Integer
},
vec![
(
Instruction::add(2, Operand::Register(0), Operand::Register(1)),
Span(35, 36)
),
(Instruction::r#return(true), Span(39, 40)),
],
vec![ConcreteValue::string("a"), ConcreteValue::string("b")],
vec![
Local::new(0, 0, false, Scope::default()),
Local::new(1, 1, false, Scope::default())
]
)),
ConcreteValue::string("add"),
],
vec![Local::new(1, 0, false, Scope::default(),),],
)),
);
assert_eq!(run(source), Ok(None));
}

View File

@ -1,159 +0,0 @@
use dust_lang::*;
#[test]
fn empty_list() {
let source = "[]";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::List(Box::new(Type::Any)),
},
vec![
(Instruction::load_list(0, 0), Span(0, 2)),
(Instruction::r#return(true), Span(2, 2)),
],
vec![],
vec![]
)),
);
assert_eq!(run(source), Ok(Some(ConcreteValue::list([]))));
}
#[test]
fn list() {
let source = "[1, 2, 3]";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::List(Box::new(Type::Integer)),
},
vec![
(Instruction::load_constant(0, 0, false), Span(1, 2)),
(Instruction::load_constant(1, 1, false), Span(4, 5)),
(Instruction::load_constant(2, 2, false), Span(7, 8)),
(Instruction::load_list(3, 0), Span(0, 9)),
(Instruction::r#return(true), Span(9, 9)),
],
vec![
ConcreteValue::Integer(1),
ConcreteValue::Integer(2),
ConcreteValue::Integer(3)
],
vec![]
)),
);
assert_eq!(
run(source),
Ok(Some(ConcreteValue::list([
ConcreteValue::Integer(1),
ConcreteValue::Integer(2),
ConcreteValue::Integer(3)
])))
);
}
#[test]
fn list_with_complex_expression() {
let source = "[1, 2 + 3 - 4 * 5]";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::List(Box::new(Type::Integer)),
},
vec![
(Instruction::load_constant(0, 0, false), Span(1, 2)),
(
Instruction::add(1, Operand::Constant(1), Operand::Constant(2)),
Span(6, 7)
),
(
Instruction::multiply(2, Operand::Constant(3), Operand::Constant(4)),
Span(14, 15)
),
(
Instruction::subtract(3, Operand::Register(1), Operand::Register(2)),
Span(10, 11)
),
(Instruction::close(1, 3), Span(17, 18)),
(Instruction::load_list(4, 0), Span(0, 18)),
(Instruction::r#return(true), Span(18, 18)),
],
vec![
ConcreteValue::Integer(1),
ConcreteValue::Integer(2),
ConcreteValue::Integer(3),
ConcreteValue::Integer(4),
ConcreteValue::Integer(5)
],
vec![]
)),
);
assert_eq!(
run(source),
Ok(Some(ConcreteValue::list([
ConcreteValue::Integer(1),
ConcreteValue::Integer(-15)
])))
);
}
#[test]
fn list_with_simple_expression() {
let source = "[1, 2 + 3, 4]";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::List(Box::new(Type::Integer)),
},
vec![
(Instruction::load_constant(0, 0, false), Span(1, 2)),
(
Instruction::add(1, Operand::Constant(1), Operand::Constant(2)),
Span(6, 7)
),
(Instruction::load_constant(2, 3, false), Span(11, 12)),
(Instruction::load_list(3, 0), Span(0, 13)),
(Instruction::r#return(true), Span(13, 13)),
],
vec![
ConcreteValue::Integer(1),
ConcreteValue::Integer(2),
ConcreteValue::Integer(3),
ConcreteValue::Integer(4),
],
vec![]
)),
);
assert_eq!(
run(source),
Ok(Some(ConcreteValue::list([
ConcreteValue::Integer(1),
ConcreteValue::Integer(5),
ConcreteValue::Integer(4),
])))
);
}

View File

@ -1,146 +0,0 @@
use dust_lang::*;
use smallvec::smallvec;
#[test]
fn true_and_true() {
let source = "true && true";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Boolean,
},
smallvec![
Instruction::load_encoded(0, true, false),
Instruction::test(0, true),
Instruction::jump(1, true),
Instruction::load_encoded(1, true, false),
Instruction::r#return(true),
],
smallvec![
Span(0, 4),
Span(5, 7),
Span(5, 7),
Span(8, 12),
Span(12, 12),
],
smallvec![],
smallvec![],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::boolean(true))));
}
#[test]
fn false_and_false() {
let source = "false && false";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Boolean,
},
smallvec![
Instruction::load_encoded(0, false, false),
Instruction::test(0, true),
Instruction::jump(1, true),
Instruction::load_encoded(1, false, false),
Instruction::r#return(true),
],
smallvec![
Span(0, 5),
Span(6, 8),
Span(6, 8),
Span(9, 14),
Span(14, 14),
],
smallvec![],
smallvec![],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::boolean(false))));
}
#[test]
fn false_and_true() {
let source = "false && true";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Boolean,
},
smallvec![
Instruction::load_encoded(0, false, false),
Instruction::test(0, true),
Instruction::jump(1, true),
Instruction::load_encoded(1, true, false),
Instruction::r#return(true),
],
smallvec![
Span(0, 5),
Span(6, 8),
Span(6, 8),
Span(9, 13),
Span(13, 13)
],
smallvec![],
smallvec![],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::boolean(false))));
}
#[test]
fn true_and_false() {
let source = "true && false";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Boolean,
},
smallvec![
Instruction::load_encoded(0, true, false),
Instruction::test(0, true),
Instruction::jump(1, true),
Instruction::load_encoded(1, false, false),
Instruction::r#return(true),
],
smallvec![
Span(0, 4),
Span(5, 7),
Span(5, 7),
Span(8, 13),
Span(13, 13)
],
smallvec![],
smallvec![],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::boolean(false))));
}

View File

@ -1,42 +0,0 @@
use dust_lang::*;
use smallvec::smallvec;
#[test]
fn true_and_true_and_true() {
let source = "true && true && true";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Boolean,
},
smallvec![
Instruction::load_encoded(0, true, false),
Instruction::test(0, true),
Instruction::jump(1, true),
Instruction::load_encoded(1, true, false),
Instruction::test(1, true),
Instruction::jump(1, true),
Instruction::load_encoded(2, true, false),
Instruction::r#return(true),
],
smallvec![
Span(0, 4),
Span(5, 7),
Span(5, 7),
Span(8, 12),
Span(13, 15),
Span(13, 15),
Span(16, 20),
Span(20, 20)
],
smallvec![],
smallvec![],
vec![],
))
);
}

View File

@ -1,38 +0,0 @@
use dust_lang::*;
use smallvec::smallvec;
#[test]
fn true_or_false() {
let source = "true || false";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Boolean,
},
smallvec![
Instruction::load_encoded(0, true, false),
Instruction::test(0, false),
Instruction::jump(1, true),
Instruction::load_encoded(1, false, false),
Instruction::r#return(true),
],
smallvec![
Span(0, 4),
Span(5, 7),
Span(5, 7),
Span(8, 13),
Span(13, 13),
],
smallvec![],
smallvec![],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::boolean(true))));
}

View File

@ -1,42 +0,0 @@
use dust_lang::*;
#[test]
fn r#while() {
let source = "let mut x = 0; while x < 5 { x = x + 1 } x";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer,
},
vec![
(Instruction::load_constant(0, 0, false), Span(12, 13)),
(
Instruction::less(0, true, Operand::Register(0), Operand::Constant(2)),
Span(23, 24)
),
(Instruction::jump(2, true), Span(41, 42)),
(
Instruction::add(0, Operand::Register(0), Operand::Constant(3)),
Span(35, 36)
),
(Instruction::jump(3, false), Span(41, 42)),
(Instruction::get_local(1, 0), Span(41, 42)),
(Instruction::r#return(true), Span(42, 42)),
],
vec![
ConcreteValue::Integer(0),
ConcreteValue::string("x"),
ConcreteValue::Integer(5),
ConcreteValue::Integer(1),
],
vec![Local::new(1, 0, true, Scope::default())]
)),
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(5))));
}

View File

@ -1,270 +0,0 @@
use dust_lang::*;
use smallvec::smallvec;
#[test]
fn add_bytes() {
let source = "0xfe + 0x01";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Byte,
},
smallvec![
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
Instruction::r#return(true),
],
smallvec![Span(5, 6), Span(11, 11),],
smallvec![Value::byte(0xfe), Value::byte(0x01)],
smallvec![],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::byte(0xff))));
}
#[test]
fn add_bytes_saturate() {
let source = "0xff + 0x01";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Byte,
},
smallvec![
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
Instruction::r#return(true),
],
smallvec![Span(5, 6), Span(11, 11)],
smallvec![Value::byte(0xff), Value::byte(0x01)],
smallvec![],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::byte(0xff))));
}
#[test]
fn add_characters() {
let source = "'a' + 'b'";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::String,
},
smallvec![
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
Instruction::r#return(true)
],
smallvec![Span(4, 5), Span(11, 11)],
smallvec![Value::character('a'), Value::character('b')],
smallvec![],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::string("ab"))));
}
#[test]
fn add_character_and_string() {
let source = "'a' + \"b\"";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::String,
},
smallvec![
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
Instruction::r#return(true),
],
smallvec![Span(4, 5), Span(9, 9),],
smallvec![Value::character('a'), Value::string("b")],
smallvec![],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::string("ab"))));
}
#[test]
fn add_floats() {
let source = "1.0 + 2.0";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Float,
},
smallvec![
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
Instruction::r#return(true),
],
smallvec![Span(4, 5), Span(9, 9),],
smallvec![Value::float(1.0), Value::float(2.0)],
smallvec![],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::float(3.0))));
}
#[test]
fn add_floats_saturatate() {
let source = "1.7976931348623157E+308 + 0.00000001";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Float,
},
smallvec![
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
Instruction::r#return(true),
],
smallvec![Span(24, 25), Span(36, 36),],
smallvec![Value::float(f64::MAX), Value::float(0.00000001)],
smallvec![],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::float(f64::MAX))));
}
#[test]
fn add_integers() {
let source = "1 + 2";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer,
},
smallvec![
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
Instruction::r#return(true)
],
smallvec![Span(2, 3), Span(5, 5),],
smallvec![Value::integer(1), Value::integer(2)],
smallvec![],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::integer(3))));
}
#[test]
fn add_integers_saturate() {
let source = "9223372036854775807 + 1";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer,
},
smallvec![
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
Instruction::r#return(true)
],
smallvec![Span(20, 21), Span(23, 23),],
smallvec![Value::integer(i64::MAX), Value::integer(1)],
smallvec![],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::integer(i64::MAX))));
}
#[test]
fn add_strings() {
let source = "\"Hello, \" + \"world!\"";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::String,
},
smallvec![
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
Instruction::r#return(true),
],
smallvec![Span(10, 11), Span(20, 20)],
smallvec![Value::string("Hello, "), Value::string("world!")],
smallvec![],
vec![]
))
);
}
#[test]
fn add_string_and_character() {
let source = "\"a\" + 'b'";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::String,
},
smallvec![
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
Instruction::r#return(true),
],
smallvec![Span(4, 5), Span(9, 9),],
smallvec![Value::string("a"), Value::character('b')],
smallvec![],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::string("ab"))));
}

View File

@ -1,31 +0,0 @@
use dust_lang::*;
use smallvec::smallvec;
#[test]
fn add_assign() {
let source = "let mut a = 1; a += 2; a";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer,
},
smallvec![
Instruction::load_constant(0, 0, false),
Instruction::add(0, Argument::Register(0), Argument::Constant(2)),
Instruction::get_local(1, 0),
Instruction::r#return(true)
],
smallvec![Span(12, 13), Span(17, 19), Span(23, 24), Span(24, 24)],
smallvec![Value::integer(1), Value::string("a"), Value::integer(2)],
smallvec![Local::new(1, 0, true, Scope::default())],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::integer(3))));
}

View File

@ -1,405 +0,0 @@
use dust_lang::*;
#[test]
fn add_boolean_left() {
let source = "true + 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddType {
argument_type: Type::Boolean,
position: Span(0, 4)
},
source,
})
);
}
#[test]
fn add_boolean_right() {
let source = "1 + true";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddType {
argument_type: Type::Boolean,
position: Span(4, 8)
},
source,
})
);
}
#[test]
fn add_function_left() {
let source = "fn(){} + 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddType {
argument_type: Type::function(FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None
}),
position: Span(0, 6)
},
source,
})
);
}
#[test]
fn add_function_right() {
let source = "1 + fn(){}";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddType {
argument_type: Type::function(FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None
}),
position: Span(4, 10)
},
source,
})
);
}
#[test]
fn add_list_left() {
let source = "[1, 2] + 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddType {
argument_type: Type::List(Box::new(Type::Integer)),
position: Span(0, 6)
},
source,
})
);
}
#[test]
fn add_list_right() {
let source = "1 + [1, 2]";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddType {
argument_type: Type::List(Box::new(Type::Integer)),
position: Span(4, 10)
},
source,
})
);
}
// #[test]
// fn add_range_left() {
// todo!("Add ranges")
// }
// #[test]
// fn add_range_right() {
// todo!("Add ranges")
// }
//
#[test]
fn add_byte_and_character() {
let source = "0xff + 'a'";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::Byte,
right_type: Type::Character,
position: Span(0, 10)
},
source,
})
);
}
#[test]
fn add_byte_and_integer() {
let source = "0xff + 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::Byte,
right_type: Type::Integer,
position: Span(0, 8)
},
source,
})
);
}
#[test]
fn add_byte_and_string() {
let source = "0xff + \"hello\"";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::Byte,
right_type: Type::String,
position: Span(0, 14)
},
source,
})
);
}
#[test]
fn add_character_and_byte() {
let source = "'a' + 0xff";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::Character,
right_type: Type::Byte,
position: Span(0, 10)
},
source,
})
);
}
#[test]
fn add_character_and_float() {
let source = "'a' + 1.0";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::Character,
right_type: Type::Float,
position: Span(0, 9)
},
source,
})
);
}
#[test]
fn add_character_and_integer() {
let source = "'a' + 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::Character,
right_type: Type::Integer,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn add_float_and_byte() {
let source = "1.0 + 0xff";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::Float,
right_type: Type::Byte,
position: Span(0, 10)
},
source,
})
);
}
#[test]
fn add_float_and_character() {
let source = "1.0 + 'a'";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::Float,
right_type: Type::Character,
position: Span(0, 9)
},
source,
})
);
}
#[test]
fn add_float_and_integer() {
let source = "1.0 + 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::Float,
right_type: Type::Integer,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn add_float_and_string() {
let source = "1.0 + \"hello\"";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::Float,
right_type: Type::String,
position: Span(0, 13)
},
source,
})
);
}
#[test]
fn add_integer_and_byte() {
let source = "1 + 0xff";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::Integer,
right_type: Type::Byte,
position: Span(0, 8)
},
source,
})
);
}
#[test]
fn add_integer_and_character() {
let source = "1 + 'a'";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::Integer,
right_type: Type::Character,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn add_integer_and_float() {
let source = "1 + 1.0";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::Integer,
right_type: Type::Float,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn add_integer_and_string() {
let source = "1 + \"hello\"";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::Integer,
right_type: Type::String,
position: Span(0, 11)
},
source,
})
);
}
#[test]
fn add_string_and_byte() {
let source = "\"hello\" + 0xff";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::String,
right_type: Type::Byte,
position: Span(0, 14)
},
source,
})
);
}
#[test]
fn add_string_and_float() {
let source = "\"hello\" + 1.0";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::String,
right_type: Type::Float,
position: Span(0, 13)
},
source,
})
);
}
#[test]
fn add_string_and_integer() {
let source = "\"hello\" + 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotAddArguments {
left_type: Type::String,
right_type: Type::Integer,
position: Span(0, 11)
},
source,
})
);
}

View File

@ -1,85 +0,0 @@
use dust_lang::*;
#[test]
fn divide_bytes() {
let source = "0xff / 0x01";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Byte,
},
vec![
(
Instruction::divide(0, Operand::Constant(0), Operand::Constant(1)),
Span(5, 6)
),
(Instruction::r#return(true), Span(11, 11))
],
vec![ConcreteValue::Byte(0xff), ConcreteValue::Byte(0x01)],
vec![]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Byte(0xff))));
}
#[test]
fn divide_floats() {
let source = "2.0 / 2.0";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Float,
},
vec![
(
Instruction::divide(0, Operand::Constant(0), Operand::Constant(0)),
Span(4, 5)
),
(Instruction::r#return(true), Span(9, 9))
],
vec![ConcreteValue::Float(2.0)],
vec![]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Float(1.0))));
}
#[test]
fn divide_integers() {
let source = "2 / 2";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer,
},
vec![
(
Instruction::divide(0, Operand::Constant(0), Operand::Constant(0)),
Span(2, 3)
),
(Instruction::r#return(true), Span(5, 5))
],
vec![ConcreteValue::Integer(2)],
vec![]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(1))));
}

View File

@ -1,31 +0,0 @@
use dust_lang::*;
#[test]
fn divide_assign() {
let source = "let mut a = 2; a /= 2; a";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer,
},
vec![
(Instruction::load_constant(0, 0, false), Span(12, 13)),
(
Instruction::divide(0, Operand::Register(0), Operand::Constant(0)),
Span(17, 19)
),
(Instruction::get_local(1, 0), Span(23, 24)),
(Instruction::r#return(true), Span(24, 24))
],
vec![ConcreteValue::Integer(2), ConcreteValue::string("a")],
vec![Local::new(1, 0, true, Scope::default())]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(1))));
}

View File

@ -1,229 +0,0 @@
use dust_lang::*;
#[test]
fn divide_boolean_left() {
let source = "true / 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotDivideType {
argument_type: Type::Boolean,
position: Span(0, 4)
},
source,
})
);
}
#[test]
fn divide_boolean_right() {
let source = "1 / true";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotDivideType {
argument_type: Type::Boolean,
position: Span(4, 8)
},
source,
})
);
}
#[test]
fn divide_character_left() {
let source = "'a' / 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotDivideType {
argument_type: Type::Character,
position: Span(0, 3)
},
source,
})
);
}
#[test]
fn divide_character_right() {
let source = "1 / 'a'";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotDivideType {
argument_type: Type::Character,
position: Span(4, 7)
},
source,
})
);
}
#[test]
fn divide_float_and_character() {
let source = "1.0 / 'a'";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotDivideType {
argument_type: Type::Character,
position: Span(6, 9)
},
source,
})
);
}
#[test]
fn divide_float_and_integer() {
let source = "1.0 / 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotDivideArguments {
left_type: Type::Float,
right_type: Type::Integer,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn divide_function_left() {
let source = "fn(){} / 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotDivideType {
argument_type: Type::function(FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None
}),
position: Span(0, 6)
},
source,
})
);
}
#[test]
fn divide_function_right() {
let source = "1 / fn(){}";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotDivideType {
argument_type: Type::function(FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None
}),
position: Span(4, 10)
},
source,
})
);
}
#[test]
fn divide_integer_and_float() {
let source = "1 / 1.0";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotDivideArguments {
left_type: Type::Integer,
right_type: Type::Float,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn divide_list_left() {
let source = "[1, 2] / 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotDivideType {
argument_type: Type::List(Box::new(Type::Integer)),
position: Span(0, 6)
},
source,
})
);
}
#[test]
fn divide_list_right() {
let source = "1 / [1, 2]";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotDivideType {
argument_type: Type::List(Box::new(Type::Integer)),
position: Span(4, 10)
},
source,
})
);
}
// #[test]
// fn add_range_left() {
// todo!("Add ranges")
// }
// #[test]
// fn add_range_right() {
// todo!("Add ranges")
// }
#[test]
fn divide_string_left() {
let source = "\"hello\" / 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotDivideType {
argument_type: Type::String,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn divide_string_right() {
let source = "1 / \"hello\"";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotDivideType {
argument_type: Type::String,
position: Span(4, 11)
},
source,
})
);
}

View File

@ -1,57 +0,0 @@
use dust_lang::*;
#[test]
fn modulo_floats() {
let source = "2.0 % 2.0";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Float,
},
vec![
(
Instruction::modulo(0, Operand::Constant(0), Operand::Constant(0)),
Span(4, 5)
),
(Instruction::r#return(true), Span(9, 9))
],
vec![ConcreteValue::Float(2.0)],
vec![]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Float(0.0))));
}
#[test]
fn modulo_integers() {
let source = "2 % 2";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer,
},
vec![
(
Instruction::modulo(0, Operand::Constant(0), Operand::Constant(0)),
Span(2, 3)
),
(Instruction::r#return(true), Span(5, 5))
],
vec![ConcreteValue::Integer(2)],
vec![]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(0))));
}

View File

@ -1,229 +0,0 @@
use dust_lang::*;
#[test]
fn modulo_boolean_left() {
let source = "true % 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotModuloType {
argument_type: Type::Boolean,
position: Span(0, 4)
},
source,
})
);
}
#[test]
fn modulo_boolean_right() {
let source = "1 % true";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotModuloType {
argument_type: Type::Boolean,
position: Span(4, 8)
},
source,
})
);
}
#[test]
fn modulo_character_left() {
let source = "'a' % 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotModuloType {
argument_type: Type::Character,
position: Span(0, 3)
},
source,
})
);
}
#[test]
fn modulo_character_right() {
let source = "1 % 'a'";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotModuloType {
argument_type: Type::Character,
position: Span(4, 7)
},
source,
})
);
}
#[test]
fn modulo_float_and_character() {
let source = "1.0 % 'a'";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotModuloType {
argument_type: Type::Character,
position: Span(6, 9)
},
source,
})
);
}
#[test]
fn modulo_float_and_integer() {
let source = "1.0 % 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotModuloArguments {
left_type: Type::Float,
right_type: Type::Integer,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn modulo_function_left() {
let source = "fn(){} % 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotModuloType {
argument_type: Type::function(FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None
}),
position: Span(0, 6)
},
source,
})
);
}
#[test]
fn modulo_function_right() {
let source = "1 % fn(){}";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotModuloType {
argument_type: Type::function(FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None
}),
position: Span(4, 10)
},
source,
})
);
}
#[test]
fn modulo_integer_and_float() {
let source = "1 % 1.0";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotModuloArguments {
left_type: Type::Integer,
right_type: Type::Float,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn modulo_list_left() {
let source = "[1, 2] % 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotModuloType {
argument_type: Type::List(Box::new(Type::Integer)),
position: Span(0, 6)
},
source,
})
);
}
#[test]
fn modulo_list_right() {
let source = "1 % [1, 2]";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotModuloType {
argument_type: Type::List(Box::new(Type::Integer)),
position: Span(4, 10)
},
source,
})
);
}
// #[test]
// fn add_range_left() {
// todo!("Add ranges")
// }
// #[test]
// fn add_range_right() {
// todo!("Add ranges")
// }
#[test]
fn modulo_string_left() {
let source = "\"hello\" % 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotModuloType {
argument_type: Type::String,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn modulo_string_right() {
let source = "1 % \"hello\"";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotModuloType {
argument_type: Type::String,
position: Span(4, 11)
},
source,
})
);
}

View File

@ -1,57 +0,0 @@
use dust_lang::*;
#[test]
fn multiply_floats() {
let source = "2.0 * 2.0";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Float
},
vec![
(
Instruction::multiply(0, Operand::Constant(0), Operand::Constant(0)),
Span(4, 5)
),
(Instruction::r#return(true), Span(9, 9)),
],
vec![ConcreteValue::Float(2.0)],
vec![]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Float(4.0))));
}
#[test]
fn multiply_integers() {
let source = "1 * 2";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer,
},
vec![
(
Instruction::multiply(0, Operand::Constant(0), Operand::Constant(1)),
Span(2, 3)
),
(Instruction::r#return(true), Span(5, 5)),
],
vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)],
vec![]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(2))));
}

View File

@ -1,35 +0,0 @@
use dust_lang::*;
#[test]
fn multiply_assign() {
let source = "let mut a = 2; a *= 3 a";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer,
},
vec![
(Instruction::load_constant(0, 0, false), Span(12, 13)),
(
Instruction::multiply(0, Operand::Register(0), Operand::Constant(2)),
Span(17, 19)
),
(Instruction::get_local(1, 0), Span(22, 23)),
(Instruction::r#return(true), Span(23, 23))
],
vec![
ConcreteValue::Integer(2),
ConcreteValue::string("a"),
ConcreteValue::Integer(3)
],
vec![Local::new(1, 0, true, Scope::default())]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(6))));
}

View File

@ -1,229 +0,0 @@
use dust_lang::*;
#[test]
fn multiply_boolean_left() {
let source = "true * 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotMultiplyType {
argument_type: Type::Boolean,
position: Span(0, 4)
},
source,
})
);
}
#[test]
fn multiply_boolean_right() {
let source = "1 * true";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotMultiplyType {
argument_type: Type::Boolean,
position: Span(4, 8)
},
source,
})
);
}
#[test]
fn multiply_character_left() {
let source = "'a' * 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotMultiplyType {
argument_type: Type::Character,
position: Span(0, 3)
},
source,
})
);
}
#[test]
fn multiply_character_right() {
let source = "1 * 'a'";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotMultiplyType {
argument_type: Type::Character,
position: Span(4, 7)
},
source,
})
);
}
#[test]
fn multiply_float_and_character() {
let source = "1.0 * 'a'";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotMultiplyType {
argument_type: Type::Character,
position: Span(6, 9)
},
source,
})
);
}
#[test]
fn multiply_float_and_integer() {
let source = "1.0 * 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotMultiplyArguments {
left_type: Type::Float,
right_type: Type::Integer,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn multiply_function_left() {
let source = "fn(){} * 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotMultiplyType {
argument_type: Type::function(FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None
}),
position: Span(0, 6)
},
source,
})
);
}
#[test]
fn multiply_function_right() {
let source = "1 * fn(){}";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotMultiplyType {
argument_type: Type::function(FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None
}),
position: Span(4, 10)
},
source,
})
);
}
#[test]
fn multiply_integer_and_float() {
let source = "1 * 1.0";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotMultiplyArguments {
left_type: Type::Integer,
right_type: Type::Float,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn multiply_list_left() {
let source = "[1, 2] * 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotMultiplyType {
argument_type: Type::List(Box::new(Type::Integer)),
position: Span(0, 6)
},
source,
})
);
}
#[test]
fn multiply_list_right() {
let source = "1 * [1, 2]";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotMultiplyType {
argument_type: Type::List(Box::new(Type::Integer)),
position: Span(4, 10)
},
source,
})
);
}
// #[test]
// fn add_range_left() {
// todo!("Add ranges")
// }
// #[test]
// fn add_range_right() {
// todo!("Add ranges")
// }
#[test]
fn multiply_string_left() {
let source = "\"hello\" * 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotMultiplyType {
argument_type: Type::String,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn multiply_string_right() {
let source = "1 * \"hello\"";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotMultiplyType {
argument_type: Type::String,
position: Span(4, 11)
},
source,
})
);
}

View File

@ -1,116 +0,0 @@
use dust_lang::*;
#[test]
fn subtract_floats() {
let source = "2.0 - 2.0";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Float,
},
vec![
(
Instruction::subtract(0, Operand::Constant(0), Operand::Constant(0)),
Span(4, 5)
),
(Instruction::r#return(true), Span(9, 9)),
],
vec![ConcreteValue::Float(2.0)],
vec![]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Float(0.0))));
}
#[test]
fn subtract_floats_saturate() {
let source = "-1.7976931348623157E+308 - 0.0000001";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Float,
},
vec![
(
Instruction::subtract(0, Operand::Constant(0), Operand::Constant(1)),
Span(25, 26)
),
(Instruction::r#return(true), Span(36, 36)),
],
vec![
ConcreteValue::Float(f64::MIN),
ConcreteValue::Float(0.0000001),
],
vec![]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Float(f64::MIN))));
}
#[test]
fn subtract_integers() {
let source = "1 - 2";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer,
},
vec![
(
Instruction::subtract(0, Operand::Constant(0), Operand::Constant(1)),
Span(2, 3)
),
(Instruction::r#return(true), Span(5, 5)),
],
vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)],
vec![]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(-1))));
}
#[test]
fn subtract_integers_saturate() {
let source = "-9223372036854775808 - 1";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer,
},
vec![
(
Instruction::subtract(0, Operand::Constant(0), Operand::Constant(1)),
Span(21, 22)
),
(Instruction::r#return(true), Span(24, 24)),
],
vec![ConcreteValue::Integer(i64::MIN), ConcreteValue::Integer(1),],
vec![]
))
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(i64::MIN))));
}

View File

@ -1,35 +0,0 @@
use dust_lang::*;
#[test]
fn subtract_assign() {
let source = "let mut x = 42; x -= 2; x";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer,
},
vec![
(Instruction::load_constant(0, 0, false), Span(12, 14)),
(
Instruction::subtract(0, Operand::Register(0), Operand::Constant(2)),
Span(18, 20)
),
(Instruction::get_local(1, 0), Span(24, 25)),
(Instruction::r#return(true), Span(25, 25)),
],
vec![
ConcreteValue::Integer(42),
ConcreteValue::string("x"),
ConcreteValue::Integer(2)
],
vec![Local::new(1, 0, true, Scope::default())]
)),
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(40))));
}

View File

@ -1,229 +0,0 @@
use dust_lang::*;
#[test]
fn subtract_boolean_left() {
let source = "true - 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotSubtractType {
argument_type: Type::Boolean,
position: Span(0, 4)
},
source,
})
);
}
#[test]
fn subtract_boolean_right() {
let source = "1 - true";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotSubtractType {
argument_type: Type::Boolean,
position: Span(4, 8)
},
source,
})
);
}
#[test]
fn subtract_character_left() {
let source = "'a' - 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotSubtractType {
argument_type: Type::Character,
position: Span(0, 3)
},
source,
})
);
}
#[test]
fn subtract_character_right() {
let source = "1 - 'a'";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotSubtractType {
argument_type: Type::Character,
position: Span(4, 7)
},
source,
})
);
}
#[test]
fn subtract_float_and_character() {
let source = "1.0 - 'a'";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotSubtractType {
argument_type: Type::Character,
position: Span(6, 9)
},
source,
})
);
}
#[test]
fn subtract_float_and_integer() {
let source = "1.0 - 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotSubtractArguments {
left_type: Type::Float,
right_type: Type::Integer,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn subtract_function_left() {
let source = "fn(){} - 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotSubtractType {
argument_type: Type::function(FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None
}),
position: Span(0, 6)
},
source,
})
);
}
#[test]
fn subtract_function_right() {
let source = "1 - fn(){}";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotSubtractType {
argument_type: Type::function(FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None
}),
position: Span(4, 10)
},
source,
})
);
}
#[test]
fn subtract_integer_and_float() {
let source = "1 - 1.0";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotSubtractArguments {
left_type: Type::Integer,
right_type: Type::Float,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn subtract_list_left() {
let source = "[1, 2] - 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotSubtractType {
argument_type: Type::List(Box::new(Type::Integer)),
position: Span(0, 6)
},
source,
})
);
}
#[test]
fn subtract_list_right() {
let source = "1 - [1, 2]";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotSubtractType {
argument_type: Type::List(Box::new(Type::Integer)),
position: Span(4, 10)
},
source,
})
);
}
// #[test]
// fn add_range_left() {
// todo!("Add ranges")
// }
// #[test]
// fn add_range_right() {
// todo!("Add ranges")
// }
#[test]
fn subtract_string_left() {
let source = "\"hello\" - 1";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotSubtractType {
argument_type: Type::String,
position: Span(0, 7)
},
source,
})
);
}
#[test]
fn subtract_string_right() {
let source = "1 - \"hello\"";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::CannotSubtractType {
argument_type: Type::String,
position: Span(4, 11)
},
source,
})
);
}

View File

@ -1,72 +0,0 @@
use dust_lang::*;
#[test]
fn panic() {
let source = "panic(\"Goodbye world!\", 42)";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None,
},
vec![
(Instruction::load_constant(0, 0, false), Span(6, 22)),
(Instruction::load_constant(1, 1, false), Span(24, 26)),
(
Instruction::call_native(2, NativeFunction::Panic, 2),
Span(0, 27)
),
(Instruction::r#return(false), Span(27, 27))
],
vec![
ConcreteValue::string("Goodbye world!"),
ConcreteValue::Integer(42)
],
vec![]
)),
);
assert_eq!(
run(source),
Err(DustError::Runtime {
error: VmError::NativeFunction(NativeFunctionError::Panic {
message: Some("Goodbye world! 42".to_string()),
position: Span(0, 27)
}),
source
})
)
}
#[test]
fn to_string() {
let source = "to_string(42)";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::String,
},
vec![
(Instruction::load_constant(0, 0, false), Span(10, 12)),
(
Instruction::call_native(1, NativeFunction::ToString, 1),
Span(0, 13)
),
(Instruction::r#return(true), Span(13, 13))
],
vec![ConcreteValue::Integer(42)],
vec![]
)),
);
assert_eq!(run(source), Ok(Some(ConcreteValue::string("42"))))
}

View File

@ -1,239 +0,0 @@
use dust_lang::*;
#[test]
fn allow_access_to_parent_scope() {
let source = r#"
let x = 1;
{
x
}
"#;
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(1))));
}
#[test]
fn block_scope() {
let source = "
let a = 0;
{
let b = 42;
{
let c = 1;
}
let d = 2;
}
let e = 1;
";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None,
},
vec![
(Instruction::load_constant(0, 0, false), Span(17, 18)),
(Instruction::load_constant(1, 2, false), Span(50, 52)),
(Instruction::load_constant(2, 4, false), Span(92, 93)),
(Instruction::load_constant(3, 6, false), Span(129, 130)),
(Instruction::load_constant(4, 4, false), Span(158, 159)),
(Instruction::r#return(false), Span(165, 165))
],
vec![
ConcreteValue::Integer(0),
ConcreteValue::string("a"),
ConcreteValue::Integer(42),
ConcreteValue::string("b"),
ConcreteValue::Integer(1),
ConcreteValue::string("c"),
ConcreteValue::Integer(2),
ConcreteValue::string("d"),
ConcreteValue::string("e"),
],
vec![
Local::new(1, 0, false, Scope::new(0, 0)),
Local::new(3, 2, false, Scope::new(1, 1)),
Local::new(5, 4, false, Scope::new(2, 2)),
Local::new(7, 6, false, Scope::new(1, 1)),
Local::new(8, 7, false, Scope::new(0, 0)),
]
)),
);
assert_eq!(run(source), Ok(None));
}
#[test]
fn multiple_block_scopes() {
let source = "
let a = 0;
{
let b = 42;
{
let c = 1;
}
let d = b;
}
let q = a;
{
let b = 42;
{
let c = 1;
}
let d = b;
}
let e = a;
";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None,
},
vec![
(Instruction::load_constant(0, 0, false), Span(17, 18)),
(Instruction::load_constant(1, 2, false), Span(50, 52)),
(Instruction::load_constant(2, 4, false), Span(92, 93)),
(Instruction::get_local(3, 1), Span(129, 130)),
(Instruction::get_local(4, 0), Span(158, 159)),
(Instruction::load_constant(5, 2, false), Span(191, 193)),
(Instruction::load_constant(4, 4, false), Span(233, 234)),
(Instruction::get_local(7, 5), Span(270, 271)),
(Instruction::get_local(8, 0), Span(299, 300)),
(Instruction::r#return(false), Span(306, 306))
],
vec![
ConcreteValue::Integer(0),
ConcreteValue::string("a"),
ConcreteValue::Integer(42),
ConcreteValue::string("b"),
ConcreteValue::Integer(1),
ConcreteValue::string("c"),
ConcreteValue::string("d"),
ConcreteValue::string("q"),
ConcreteValue::string("e"),
],
vec![
Local::new(1, 0, false, Scope::new(0, 0)),
Local::new(3, 2, false, Scope::new(1, 1)),
Local::new(5, 4, false, Scope::new(2, 2)),
Local::new(6, 5, false, Scope::new(1, 1)),
Local::new(7, 6, false, Scope::new(0, 0)),
Local::new(3, 1, false, Scope::new(1, 3)),
Local::new(5, 1, false, Scope::new(2, 4)),
Local::new(6, 1, false, Scope::new(1, 3)),
Local::new(8, 1, false, Scope::new(0, 0)),
]
)),
);
assert_eq!(run(source), Ok(None));
}
#[test]
fn disallow_access_to_child_scope() {
let source = r#"
{
let x = 1;
}
x
"#;
assert_eq!(
run(source),
Err(DustError::Compile {
error: CompileError::VariableOutOfScope {
identifier: "x".to_string(),
position: Span(52, 53),
variable_scope: Scope::new(1, 1),
access_scope: Scope::new(0, 0),
},
source
})
);
}
#[test]
fn disallow_access_to_child_scope_nested() {
let source = r#"
{
{
let x = 1;
}
x
}
"#;
assert_eq!(
run(source),
Err(DustError::Compile {
error: CompileError::VariableOutOfScope {
identifier: "x".to_string(),
position: Span(78, 79),
variable_scope: Scope::new(2, 2),
access_scope: Scope::new(1, 1),
},
source
})
);
}
#[test]
fn disallow_access_to_sibling_scope() {
let source = r#"
{
let x = 1;
}
{
x
}
"#;
assert_eq!(
run(source),
Err(DustError::Compile {
error: CompileError::VariableOutOfScope {
identifier: "x".to_string(),
variable_scope: Scope::new(1, 1),
access_scope: Scope::new(1, 2),
position: Span(66, 67),
},
source
})
);
}
#[test]
fn disallow_access_to_sibling_scope_nested() {
let source = r#"
{
{
let x = 1;
}
{
x
}
}
"#;
assert_eq!(
run(source),
Err(DustError::Compile {
error: CompileError::VariableOutOfScope {
identifier: "x".to_string(),
variable_scope: Scope::new(2, 2),
access_scope: Scope::new(2, 3),
position: Span(96, 97),
},
source
})
);
}

View File

@ -1,52 +0,0 @@
use dust_lang::*;
#[test]
fn negate() {
let source = "-(42)";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer,
},
vec![
(Instruction::negate(0, Operand::Constant(0)), Span(0, 1)),
(Instruction::r#return(true), Span(5, 5)),
],
vec![ConcreteValue::Integer(42)],
vec![]
)),
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(-42))));
}
#[test]
fn not() {
let source = "!true";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Boolean,
},
vec![
(Instruction::load_encoded(0, true, false), Span(1, 5)),
(Instruction::not(1, Operand::Register(0)), Span(0, 1)),
(Instruction::r#return(true), Span(5, 5)),
],
vec![],
vec![]
)),
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false))));
}

View File

@ -0,0 +1,369 @@
use dust_lang::{
AbstractList, Chunk, ConcreteValue, DustString, FunctionType, Instruction, Span, Type, Value,
compile, instruction::TypeCode, run, vm::Pointer,
};
#[test]
fn load_boolean_true() {
let source = "true";
let chunk = Chunk {
r#type: FunctionType {
return_type: Type::Boolean,
..FunctionType::default()
},
instructions: vec![
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
],
positions: vec![Span(0, 4), Span(4, 4)],
..Chunk::default()
};
let return_value = Some(Value::boolean(true));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn load_boolean_false() {
let source = "false";
let chunk = Chunk {
r#type: FunctionType {
return_type: Type::Boolean,
..FunctionType::default()
},
instructions: vec![
Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false),
Instruction::r#return(true, 0, TypeCode::BOOLEAN),
],
positions: vec![Span(0, 5), Span(5, 5)],
..Chunk::default()
};
let return_value = Some(Value::boolean(false));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn load_byte() {
let source = "0x2a";
let chunk = Chunk {
r#type: FunctionType {
return_type: Type::Byte,
..FunctionType::default()
},
instructions: vec![
Instruction::load_encoded(0, 0x2a, TypeCode::BYTE, false),
Instruction::r#return(true, 0, TypeCode::BYTE),
],
positions: vec![Span(0, 6), Span(6, 6)],
..Chunk::default()
};
let return_value = Some(Value::byte(0x2a));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn load_character() {
let source = "'a'";
let chunk = Chunk {
r#type: FunctionType {
return_type: Type::Character,
..FunctionType::default()
},
instructions: vec![
Instruction::load_constant(0, 0, TypeCode::CHARACTER, false),
Instruction::r#return(true, 0, TypeCode::CHARACTER),
],
positions: vec![Span(0, 3), Span(3, 3)],
constants: vec![ConcreteValue::Character('a')],
..Chunk::default()
};
let return_value = Some(Value::character('a'));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn load_float() {
let source = "42.42";
let chunk = Chunk {
r#type: FunctionType {
return_type: Type::Float,
..FunctionType::default()
},
instructions: vec![
Instruction::load_constant(0, 0, TypeCode::FLOAT, false),
Instruction::r#return(true, 0, TypeCode::FLOAT),
],
positions: vec![Span(0, 4), Span(4, 4)],
constants: vec![ConcreteValue::Float(42.42)],
..Chunk::default()
};
let return_value = Some(Value::float(42.42));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn load_integer() {
let source = "42";
let chunk = Chunk {
r#type: FunctionType {
return_type: Type::Integer,
..FunctionType::default()
},
instructions: vec![
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
Instruction::r#return(true, 0, TypeCode::INTEGER),
],
positions: vec![Span(0, 2), Span(2, 2)],
constants: vec![ConcreteValue::Integer(42)],
..Chunk::default()
};
let return_value = Some(Value::integer(42));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn load_string() {
let source = "\"Hello, World!\"";
let chunk = Chunk {
r#type: FunctionType {
return_type: Type::String,
..FunctionType::default()
},
instructions: vec![
Instruction::load_constant(0, 0, TypeCode::STRING, false),
Instruction::r#return(true, 0, TypeCode::STRING),
],
positions: vec![Span(0, 15), Span(15, 15)],
constants: vec![ConcreteValue::String(DustString::from("Hello, World!"))],
..Chunk::default()
};
let return_value = Some(Value::string("Hello, World!"));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn load_boolean_list() {
let source = "[true, false]";
let chunk = Chunk {
r#type: FunctionType {
return_type: Type::List(TypeCode::BOOLEAN),
..FunctionType::default()
},
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::r#return(true, 0, TypeCode::LIST),
],
positions: vec![Span(0, 13), Span(13, 13)],
..Chunk::default()
};
let return_value = Some(Value::Concrete(ConcreteValue::List(vec![
ConcreteValue::Boolean(true),
ConcreteValue::Boolean(false),
])));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn load_byte_list() {
let source = "[0x2a, 0x42]";
let chunk = Chunk {
r#type: FunctionType {
return_type: Type::List(TypeCode::BYTE),
..FunctionType::default()
},
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::r#return(true, 0, TypeCode::LIST),
],
positions: vec![Span(0, 15), Span(15, 15)],
..Chunk::default()
};
let return_value = Some(Value::Concrete(ConcreteValue::List(vec![
ConcreteValue::Byte(0x2a),
ConcreteValue::Byte(0x42),
])));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn load_character_list() {
let source = "['a', 'b']";
let chunk = Chunk {
r#type: FunctionType {
return_type: Type::List(TypeCode::CHARACTER),
..FunctionType::default()
},
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::r#return(true, 0, TypeCode::LIST),
],
positions: vec![Span(0, 9), Span(9, 9)],
constants: vec![ConcreteValue::Character('a'), ConcreteValue::Character('b')],
..Chunk::default()
};
let return_value = Some(Value::Concrete(ConcreteValue::List(vec![
ConcreteValue::Character('a'),
ConcreteValue::Character('b'),
])));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn load_float_list() {
let source = "[42.42, 24.24]";
let chunk = Chunk {
r#type: FunctionType {
return_type: Type::List(TypeCode::FLOAT),
..FunctionType::default()
},
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::r#return(true, 0, TypeCode::LIST),
],
positions: vec![Span(0, 15), Span(15, 15)],
constants: vec![ConcreteValue::Float(42.42), ConcreteValue::Float(24.24)],
..Chunk::default()
};
let return_value = Some(Value::Concrete(ConcreteValue::List(vec![
ConcreteValue::Float(42.42),
ConcreteValue::Float(24.24),
])));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn load_integer_list() {
let source = "[1, 2, 3]";
let chunk = Chunk {
r#type: FunctionType {
return_type: Type::List(TypeCode::INTEGER),
..FunctionType::default()
},
instructions: vec![
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::r#return(true, 0, TypeCode::LIST),
],
positions: vec![Span(0, 9), Span(9, 9)],
constants: vec![
ConcreteValue::Integer(1),
ConcreteValue::Integer(2),
ConcreteValue::Integer(3),
],
..Chunk::default()
};
let return_value = Some(Value::Concrete(ConcreteValue::List(vec![
ConcreteValue::Integer(1),
ConcreteValue::Integer(2),
ConcreteValue::Integer(3),
])));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn load_string_list() {
let source = "[\"Hello\", \"World\"]";
let chunk = Chunk {
r#type: FunctionType {
return_type: Type::List(TypeCode::STRING),
..FunctionType::default()
},
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::r#return(true, 0, TypeCode::LIST),
],
positions: vec![Span(0, 19), Span(19, 19)],
constants: vec![
ConcreteValue::String(DustString::from("Hello")),
ConcreteValue::String(DustString::from("World")),
],
..Chunk::default()
};
let return_value = Some(Value::Concrete(ConcreteValue::List(vec![
ConcreteValue::String(DustString::from("Hello")),
ConcreteValue::String(DustString::from("World")),
])));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn load_nested_list() {
let source = "[[1, 2], [3, 4]]";
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, 2, 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::r#return(true, 2, TypeCode::LIST),
],
positions: vec![
Span(2, 3),
Span(5, 6),
Span(1, 7),
Span(10, 11),
Span(13, 14),
Span(9, 15),
Span(0, 16),
Span(16, 16),
],
constants: vec![
ConcreteValue::Integer(1),
ConcreteValue::Integer(2),
ConcreteValue::Integer(3),
ConcreteValue::Integer(4),
],
..Chunk::default()
};
let return_value = Some(Value::Concrete(ConcreteValue::List(vec![
ConcreteValue::List(vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)]),
ConcreteValue::List(vec![ConcreteValue::Integer(3), ConcreteValue::Integer(4)]),
])));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}

View File

@ -1,75 +0,0 @@
use dust_lang::*;
#[test]
fn define_local() {
let source = "let x = 42;";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None,
},
vec![
(Instruction::load_constant(0, 0, false), Span(8, 10)),
(Instruction::r#return(false), Span(11, 11))
],
vec![ConcreteValue::Integer(42), ConcreteValue::string("x")],
vec![Local::new(1, 0, false, Scope::default())]
)),
);
assert_eq!(run(source), Ok(None));
}
#[test]
fn let_statement_expects_identifier() {
let source = "let 1 = 2";
assert_eq!(
compile(source),
Err(DustError::Compile {
error: CompileError::ExpectedToken {
expected: TokenKind::Identifier,
found: Token::Integer("1").to_owned(),
position: Span(4, 5)
},
source
})
);
}
#[test]
fn set_local() {
let source = "let mut x = 41; x = 42; x";
assert_eq!(
compile(source),
Ok(Chunk::with_data(
None,
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::Integer,
},
vec![
(Instruction::load_constant(0, 0, false), Span(12, 14)),
(Instruction::load_constant(1, 2, false), Span(20, 22)),
(Instruction::set_local(1, 0), Span(16, 17)),
(Instruction::get_local(2, 0), Span(24, 25)),
(Instruction::r#return(true), Span(25, 25)),
],
vec![
ConcreteValue::Integer(41),
ConcreteValue::string("x"),
ConcreteValue::Integer(42)
],
vec![Local::new(1, 0, true, Scope::default())]
)),
);
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(42))));
}