1
0

Major overhaul to VM

This commit is contained in:
Jeff 2024-11-10 19:28:21 -05:00
parent 8af8e48ebd
commit 73247446c7
24 changed files with 1729 additions and 1379 deletions

View File

@ -4,29 +4,23 @@
//! list of locals that can be executed by the Dust virtual machine. Chunks have a name when they //! list of locals that can be executed by the Dust virtual machine. Chunks have a name when they
//! belong to a named function. //! belong to a named function.
use std::{ use std::fmt::{self, Debug, Display, Formatter};
cmp::Ordering,
fmt::{self, Debug, Display, Formatter},
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{Disassembler, Instruction, Span, Type, Value}; use crate::{value::ConcreteValue, Disassembler, FunctionType, Instruction, Scope, Span, Type};
/// In-memory representation of a Dust program or function. /// In-memory representation of a Dust program or function.
/// ///
/// See the [module-level documentation](index.html) for more information. /// See the [module-level documentation](index.html) for more information.
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Chunk { pub struct Chunk {
name: Option<String>, name: Option<String>, // TODO: Use a bool indicating whether the chunk is named. If it is, get
r#type: FunctionType, // the name from C0.
instructions: Vec<(Instruction, Span)>, instructions: Vec<(Instruction, Span)>,
constants: Vec<Value>, constants: Vec<ConcreteValue>,
locals: Vec<Local>, locals: Vec<Local>,
return_type: Type,
current_scope: Scope,
block_index: u8,
} }
impl Chunk { impl Chunk {
@ -36,16 +30,18 @@ impl Chunk {
instructions: Vec::new(), instructions: Vec::new(),
constants: Vec::new(), constants: Vec::new(),
locals: Vec::new(), locals: Vec::new(),
return_type: Type::None, r#type: FunctionType {
current_scope: Scope::default(), type_parameters: None,
block_index: 0, value_parameters: None,
return_type: Box::new(Type::None),
},
} }
} }
pub fn with_data( pub fn with_data(
name: Option<String>, name: Option<String>,
instructions: Vec<(Instruction, Span)>, instructions: Vec<(Instruction, Span)>,
constants: Vec<Value>, constants: Vec<ConcreteValue>,
locals: Vec<Local>, locals: Vec<Local>,
) -> Self { ) -> Self {
Self { Self {
@ -53,9 +49,11 @@ impl Chunk {
instructions, instructions,
constants, constants,
locals, locals,
return_type: Type::None, r#type: FunctionType {
current_scope: Scope::default(), type_parameters: None,
block_index: 0, value_parameters: None,
return_type: Box::new(Type::None),
},
} }
} }
@ -63,6 +61,10 @@ impl Chunk {
self.name.as_ref() self.name.as_ref()
} }
pub fn r#type(&self) -> &FunctionType {
&self.r#type
}
pub fn set_name(&mut self, name: String) { pub fn set_name(&mut self, name: String) {
self.name = Some(name); self.name = Some(name);
} }
@ -75,16 +77,36 @@ impl Chunk {
self.instructions.is_empty() self.instructions.is_empty()
} }
pub fn constants(&self) -> &Vec<Value> { pub fn constants(&self) -> &Vec<ConcreteValue> {
&self.constants &self.constants
} }
pub fn constants_mut(&mut self) -> &mut Vec<Value> { pub fn constants_mut(&mut self) -> &mut Vec<ConcreteValue> {
&mut self.constants &mut self.constants
} }
pub fn take_constants(self) -> Vec<Value> { pub fn get_constant(&self, index: u8) -> Result<&ConcreteValue, ChunkError> {
self.constants self.constants
.get(index as usize)
.ok_or(ChunkError::ConstantIndexOutOfBounds {
index: index as usize,
})
}
pub fn push_or_get_constant(&mut self, value: ConcreteValue) -> u8 {
if let Some(index) = self
.constants
.iter()
.position(|constant| *constant == value)
{
index as u8
} else {
let index = self.constants.len() as u8;
self.constants.push(value);
index
}
} }
pub fn instructions(&self) -> &Vec<(Instruction, Span)> { pub fn instructions(&self) -> &Vec<(Instruction, Span)> {
@ -125,32 +147,6 @@ impl Chunk {
}) })
} }
pub fn current_scope(&self) -> Scope {
self.current_scope
}
pub fn get_constant(&self, index: u8) -> Result<&Value, ChunkError> {
self.constants
.get(index as usize)
.ok_or(ChunkError::ConstantIndexOutOfBounds {
index: index as usize,
})
}
pub fn push_or_get_constant(&mut self, value: Value) -> u8 {
if let Some(index) = self
.constants
.iter()
.position(|constant| constant == &value)
{
return index as u8;
}
self.constants.push(value);
(self.constants.len() - 1) as u8
}
pub fn get_identifier(&self, local_index: u8) -> Option<String> { pub fn get_identifier(&self, local_index: u8) -> Option<String> {
self.locals.get(local_index as usize).and_then(|local| { self.locals.get(local_index as usize).and_then(|local| {
self.constants self.constants
@ -159,22 +155,6 @@ impl Chunk {
}) })
} }
pub fn begin_scope(&mut self) {
self.block_index += 1;
self.current_scope.block_index = self.block_index;
self.current_scope.depth += 1;
}
pub fn end_scope(&mut self) {
self.current_scope.depth -= 1;
if self.current_scope.depth == 0 {
self.current_scope.block_index = 0;
} else {
self.current_scope.block_index -= 1;
}
}
pub fn get_constant_type(&self, constant_index: u8) -> Result<Type, ChunkError> { pub fn get_constant_type(&self, constant_index: u8) -> Result<Type, ChunkError> {
self.constants self.constants
.get(constant_index as usize) .get(constant_index as usize)
@ -193,10 +173,6 @@ impl Chunk {
}) })
} }
pub fn return_type(&self) -> &Type {
&self.return_type
}
pub fn disassembler(&self) -> Disassembler { pub fn disassembler(&self) -> Disassembler {
Disassembler::new(self) Disassembler::new(self)
} }
@ -260,45 +236,11 @@ impl Local {
} }
} }
/// Variable locality, as defined by its depth and block index.
///
/// The `block index` is a unique identifier for a block within a chunk. It is used to differentiate
/// between blocks that are not nested together but have the same depth, i.e. sibling scopes. If the
/// `block_index` is 0, then the scope is the root scope of the chunk. The `block_index` is always 0
/// when the `depth` is 0. See [Chunk::begin_scope][] and [Chunk::end_scope][] to see how scopes are
/// incremented and decremented.
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Scope {
/// Level of block nesting.
pub depth: u8,
/// Index of the block in the chunk.
pub block_index: u8,
}
impl Scope {
pub fn new(depth: u8, block_index: u8) -> Self {
Self { depth, block_index }
}
pub fn contains(&self, other: &Self) -> bool {
match self.depth.cmp(&other.depth) {
Ordering::Less => false,
Ordering::Greater => self.block_index >= other.block_index,
Ordering::Equal => self.block_index == other.block_index,
}
}
}
impl Display for Scope {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.depth, self.block_index)
}
}
/// Errors that can occur when using a [`Chunk`]. /// Errors that can occur when using a [`Chunk`].
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum ChunkError { pub enum ChunkError {
ConstantIndexOutOfBounds { index: usize }, ConstantIndexOutOfBounds { index: usize },
FunctionIndexOutOfBounds { index: usize },
InstructionIndexOutOfBounds { index: usize }, InstructionIndexOutOfBounds { index: usize },
LocalIndexOutOfBounds { index: usize }, LocalIndexOutOfBounds { index: usize },
} }
@ -309,6 +251,9 @@ impl Display for ChunkError {
ChunkError::ConstantIndexOutOfBounds { index } => { ChunkError::ConstantIndexOutOfBounds { index } => {
write!(f, "Constant index {} out of bounds", index) write!(f, "Constant index {} out of bounds", index)
} }
ChunkError::FunctionIndexOutOfBounds { index } => {
write!(f, "Function index {} out of bounds", index)
}
ChunkError::InstructionIndexOutOfBounds { index } => { ChunkError::InstructionIndexOutOfBounds { index } => {
write!(f, "Instruction index {} out of bounds", index) write!(f, "Instruction index {} out of bounds", index)
} }

View File

@ -13,9 +13,9 @@ use std::{
use colored::Colorize; use colored::Colorize;
use crate::{ use crate::{
AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Instruction, LexError, Lexer, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Instruction,
Local, NativeFunction, Operation, Optimizer, Scope, Span, Token, TokenKind, TokenOwned, Type, LexError, Lexer, Local, NativeFunction, Operation, Optimizer, Scope, Span, Token, TokenKind,
TypeConflict, Value, TokenOwned, Type, TypeConflict,
}; };
/// Compiles the input and returns a chunk. /// Compiles the input and returns a chunk.
@ -59,6 +59,9 @@ pub struct Compiler<'src> {
previous_token: Token<'src>, previous_token: Token<'src>,
previous_position: Span, previous_position: Span,
block_index: u8,
current_scope: Scope,
} }
impl<'src> Compiler<'src> { impl<'src> Compiler<'src> {
@ -83,6 +86,8 @@ impl<'src> Compiler<'src> {
current_position, current_position,
previous_token: Token::Eof, previous_token: Token::Eof,
previous_position: Span(0, 0), previous_position: Span(0, 0),
block_index: 0,
current_scope: Scope::default(),
}) })
} }
@ -149,11 +154,15 @@ impl<'src> Compiler<'src> {
.enumerate() .enumerate()
.rev() .rev()
.find_map(|(index, local)| { .find_map(|(index, local)| {
let identifier = self let constant = self
.chunk .chunk
.constants() .constants()
.get(local.identifier_index as usize)? .get(local.identifier_index as usize)?;
.as_string()?; let identifier = if let ConcreteValue::String(identifier) = constant {
identifier
} else {
return None;
};
if identifier == identifier_text { if identifier == identifier_text {
Some(index as u8) Some(index as u8)
@ -177,7 +186,7 @@ impl<'src> Compiler<'src> {
) -> (u8, u8) { ) -> (u8, u8) {
log::debug!("Declare local {identifier}"); log::debug!("Declare local {identifier}");
let identifier = Value::string(identifier); let identifier = ConcreteValue::string(identifier);
let identifier_index = self.chunk.push_or_get_constant(identifier); let identifier_index = self.chunk.push_or_get_constant(identifier);
self.chunk self.chunk
@ -210,16 +219,6 @@ impl<'src> Compiler<'src> {
} }
} }
fn emit_instruction(&mut self, instruction: Instruction, position: Span) {
log::debug!(
"Emitting {} at {}",
instruction.operation().to_string().bold(),
position.to_string()
);
self.chunk.instructions_mut().push((instruction, position));
}
fn pop_last_instruction(&mut self) -> Result<(Instruction, Span), CompileError> { fn pop_last_instruction(&mut self) -> Result<(Instruction, Span), CompileError> {
self.chunk self.chunk
.instructions_mut() .instructions_mut()
@ -316,8 +315,8 @@ impl<'src> Compiler<'src> {
pub fn get_register_type(&self, register_index: u8) -> Result<Type, CompileError> { pub fn get_register_type(&self, register_index: u8) -> Result<Type, CompileError> {
for (index, (instruction, _)) in self.chunk.instructions().iter().enumerate() { for (index, (instruction, _)) in self.chunk.instructions().iter().enumerate() {
if let Operation::LoadList = instruction.operation() { if instruction.a() == register_index {
if instruction.a() == register_index { if let Operation::LoadList = instruction.operation() {
let mut length = (instruction.c() - instruction.b() + 1) as usize; let mut length = (instruction.c() - instruction.b() + 1) as usize;
let mut item_type = Type::Any; let mut item_type = Type::Any;
let distance_to_end = self.chunk.len() - index; let distance_to_end = self.chunk.len() - index;
@ -342,10 +341,14 @@ impl<'src> Compiler<'src> {
length, length,
}); });
} }
}
if instruction.yields_value() && instruction.a() == register_index { if let Operation::LoadSelf = instruction.operation() {
return self.get_instruction_type(instruction); return Ok(Type::SelfChunk);
}
if instruction.yields_value() {
return self.get_instruction_type(instruction);
}
} }
} }
@ -355,8 +358,22 @@ impl<'src> Compiler<'src> {
}) })
} }
fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), CompileError> { fn emit_instruction(&mut self, instruction: Instruction, position: Span) {
let constant_index = self.chunk.push_or_get_constant(value); log::debug!(
"Emitting {} at {}",
instruction.operation().to_string().bold(),
position.to_string()
);
self.chunk.instructions_mut().push((instruction, position));
}
fn emit_constant(
&mut self,
constant: ConcreteValue,
position: Span,
) -> Result<(), CompileError> {
let constant_index = self.chunk.push_or_get_constant(constant);
let register = self.next_register(); let register = self.next_register();
self.emit_instruction( self.emit_instruction(
@ -401,7 +418,7 @@ impl<'src> Compiler<'src> {
let byte = u8::from_str_radix(&text[2..], 16) let byte = u8::from_str_radix(&text[2..], 16)
.map_err(|error| CompileError::ParseIntError { error, position })?; .map_err(|error| CompileError::ParseIntError { error, position })?;
let value = Value::byte(byte); let value = ConcreteValue::byte(byte);
self.emit_constant(value, position)?; self.emit_constant(value, position)?;
@ -423,7 +440,7 @@ impl<'src> Compiler<'src> {
if let Token::Character(character) = self.current_token { if let Token::Character(character) = self.current_token {
self.advance()?; self.advance()?;
let value = Value::character(character); let value = ConcreteValue::character(character);
self.emit_constant(value, position)?; self.emit_constant(value, position)?;
@ -451,7 +468,7 @@ impl<'src> Compiler<'src> {
error, error,
position: self.previous_position, position: self.previous_position,
})?; })?;
let value = Value::float(float); let value = ConcreteValue::float(float);
self.emit_constant(value, position)?; self.emit_constant(value, position)?;
@ -479,7 +496,7 @@ impl<'src> Compiler<'src> {
error, error,
position: self.previous_position, position: self.previous_position,
})?; })?;
let value = Value::integer(integer); let value = ConcreteValue::integer(integer);
self.emit_constant(value, position)?; self.emit_constant(value, position)?;
@ -501,7 +518,7 @@ impl<'src> Compiler<'src> {
if let Token::String(text) = self.current_token { if let Token::String(text) = self.current_token {
self.advance()?; self.advance()?;
let value = Value::string(text); let value = ConcreteValue::string(text);
self.emit_constant(value, position)?; self.emit_constant(value, position)?;
@ -882,10 +899,15 @@ impl<'src> Compiler<'src> {
return self.parse_native_call(native_function); return self.parse_native_call(native_function);
} else if Some(identifier) == self.chunk.name().map(|string| string.as_str()) { } else if Some(identifier) == self.chunk.name().map(|string| string.as_str()) {
let register = self.next_register(); let register = self.next_register();
let scope = self.chunk.current_scope();
self.emit_instruction(Instruction::load_self(register), start_position); self.emit_instruction(Instruction::load_self(register), start_position);
self.declare_local(identifier, Type::SelfChunk, false, scope, register); self.declare_local(
identifier,
Type::SelfChunk,
false,
self.current_scope,
register,
);
self.previous_expression_type = Type::SelfChunk; self.previous_expression_type = Type::SelfChunk;
@ -902,14 +924,13 @@ impl<'src> Compiler<'src> {
(local.is_mutable, local.scope) (local.is_mutable, local.scope)
}; };
let current_scope = self.chunk.current_scope();
if !current_scope.contains(&local_scope) { if !self.current_scope.contains(&local_scope) {
return Err(CompileError::VariableOutOfScope { return Err(CompileError::VariableOutOfScope {
identifier: self.chunk.get_identifier(local_index).unwrap(), identifier: self.chunk.get_identifier(local_index).unwrap(),
position: start_position, position: start_position,
variable_scope: local_scope, variable_scope: local_scope,
access_scope: current_scope, access_scope: self.current_scope,
}); });
} }
@ -977,13 +998,16 @@ impl<'src> Compiler<'src> {
fn parse_block(&mut self) -> Result<(), CompileError> { fn parse_block(&mut self) -> Result<(), CompileError> {
self.advance()?; self.advance()?;
self.chunk.begin_scope();
self.block_index += 1;
self.current_scope.begin(self.block_index);
while !self.allow(Token::RightBrace)? && !self.is_eof() { while !self.allow(Token::RightBrace)? && !self.is_eof() {
self.parse(Precedence::None)?; self.parse(Precedence::None)?;
} }
self.chunk.end_scope(); self.current_scope.end();
Ok(()) Ok(())
} }
@ -1317,7 +1341,6 @@ impl<'src> Compiler<'src> {
fn parse_let_statement(&mut self) -> Result<(), CompileError> { fn parse_let_statement(&mut self) -> Result<(), CompileError> {
self.advance()?; self.advance()?;
let scope = self.chunk.current_scope();
let is_mutable = self.allow(Token::Mut)?; let is_mutable = self.allow(Token::Mut)?;
let position = self.current_position; let position = self.current_position;
let identifier = if let Token::Identifier(text) = self.current_token { let identifier = if let Token::Identifier(text) = self.current_token {
@ -1350,7 +1373,8 @@ impl<'src> Compiler<'src> {
} else { } else {
self.get_register_type(register)? self.get_register_type(register)?
}; };
let (local_index, _) = self.declare_local(identifier, r#type, is_mutable, scope, register); let (local_index, _) =
self.declare_local(identifier, r#type, is_mutable, self.current_scope, register);
self.emit_instruction( self.emit_instruction(
Instruction::define_local(register, local_index, is_mutable), Instruction::define_local(register, local_index, is_mutable),
@ -1405,12 +1429,11 @@ impl<'src> Compiler<'src> {
let register = function_compiler.next_register(); let register = function_compiler.next_register();
let scope = function_compiler.chunk.current_scope();
let (_, identifier_index) = function_compiler.declare_local( let (_, identifier_index) = function_compiler.declare_local(
parameter, parameter,
r#type.clone(), r#type.clone(),
is_mutable, is_mutable,
scope, function_compiler.current_scope,
register, register,
); );
@ -1453,23 +1476,26 @@ impl<'src> Compiler<'src> {
value_parameters, value_parameters,
return_type, return_type,
}; };
let function = Value::function(function_compiler.finish(), function_type.clone()); let function = ConcreteValue::function(function_compiler.finish());
let constant_index = self.chunk.push_or_get_constant(function);
let function_end = self.current_position.1; let function_end = self.current_position.1;
let register = self.next_register();
self.lexer.skip_to(function_end); self.lexer.skip_to(function_end);
if let Some((identifier, identifier_position)) = identifier { if let Some((identifier, identifier_position)) = identifier {
let register = self.next_register();
let scope = self.chunk.current_scope();
let (local_index, _) = self.declare_local( let (local_index, _) = self.declare_local(
identifier, identifier,
Type::Function(function_type), Type::Function(function_type),
false, false,
scope, self.current_scope,
register, register,
); );
self.emit_constant(function, Span(function_start, function_end))?; self.emit_instruction(
Instruction::load_constant(register, constant_index, false),
Span(function_start, function_end),
);
self.emit_instruction( self.emit_instruction(
Instruction::define_local(register, local_index, false), Instruction::define_local(register, local_index, false),
identifier_position, identifier_position,
@ -1477,7 +1503,10 @@ impl<'src> Compiler<'src> {
self.previous_expression_type = Type::None; self.previous_expression_type = Type::None;
} else { } else {
self.emit_constant(function, Span(function_start, function_end))?; self.emit_instruction(
Instruction::load_constant(register, constant_index, false),
Span(function_start, function_end),
);
self.previous_expression_type = Type::Function(function_type); self.previous_expression_type = Type::Function(function_type);
} }
@ -1503,15 +1532,18 @@ impl<'src> Compiler<'src> {
} }
let function_register = last_instruction.a(); let function_register = last_instruction.a();
let function_return_type = let register_type = self.get_register_type(function_register)?;
if let Type::Function(function_type) = self.get_register_type(function_register)? { let function_return_type = match register_type {
*function_type.return_type Type::Function(function_type) => *function_type.return_type,
} else { Type::SelfChunk => (*self.chunk.r#type().return_type).clone(),
_ => {
return Err(CompileError::ExpectedFunction { return Err(CompileError::ExpectedFunction {
found: self.previous_token.to_owned(), found: self.previous_token.to_owned(),
actual_type: register_type,
position: self.previous_position, position: self.previous_position,
}); });
}; }
};
let start = self.current_position.0; let start = self.current_position.0;
self.advance()?; self.advance()?;
@ -1944,6 +1976,7 @@ pub enum CompileError {
}, },
ExpectedFunction { ExpectedFunction {
found: TokenOwned, found: TokenOwned,
actual_type: Type,
position: Span, position: Span,
}, },
InvalidAssignmentTarget { InvalidAssignmentTarget {
@ -2048,8 +2081,8 @@ impl AnnotatedError for CompileError {
} }
Self::Chunk { error, .. } => Some(error.to_string()), Self::Chunk { error, .. } => Some(error.to_string()),
Self::ExpectedExpression { found, .. } => Some(format!("Found {found}")), Self::ExpectedExpression { found, .. } => Some(format!("Found {found}")),
Self::ExpectedFunction { found, .. } => { Self::ExpectedFunction { found, actual_type, .. } => {
Some(format!("Expected \"{found}\" to be a function")) Some(format!("Expected \"{found}\" to be a function but it has type {actual_type}"))
} }
Self::ExpectedToken { Self::ExpectedToken {
expected, found, .. expected, found, ..

View File

@ -45,13 +45,13 @@ use std::env::current_exe;
use colored::Colorize; use colored::Colorize;
use crate::{Chunk, ConcreteValue, Local, Value}; use crate::{value::ConcreteValue, Chunk, Local};
const INSTRUCTION_HEADER: [&str; 4] = [ const INSTRUCTION_HEADER: [&str; 4] = [
"Instructions", "Instructions",
"------------", "------------",
" i BYTECODE OPERATION INFO TYPE POSITION ", " i BYTECODE OPERATION INFO POSITION ",
"--- -------- ------------- -------------------- ---------------- ----------", "--- -------- ------------- ------------------------- ----------",
]; ];
const CONSTANT_HEADER: [&str; 4] = [ const CONSTANT_HEADER: [&str; 4] = [
@ -254,7 +254,7 @@ impl<'a> Disassembler<'a> {
self.chunk.len(), self.chunk.len(),
self.chunk.constants().len(), self.chunk.constants().len(),
self.chunk.locals().len(), self.chunk.locals().len(),
self.chunk.return_type() self.chunk.r#type().return_type
); );
self.push(&info_line, true, false, true, true); self.push(&info_line, true, false, true, true);
@ -271,7 +271,7 @@ impl<'a> Disassembler<'a> {
let position = position.to_string(); let position = position.to_string();
let instruction_display = let instruction_display =
format!("{index:^3} {bytecode:>8} {operation:13} {info:^20} {position:10}"); format!("{index:^3} {bytecode:>8} {operation:13} {info:<25} {position:10}");
self.push_details(&instruction_display); self.push_details(&instruction_display);
} }
@ -313,6 +313,19 @@ impl<'a> Disassembler<'a> {
} }
for (index, value) in self.chunk.constants().iter().enumerate() { for (index, value) in self.chunk.constants().iter().enumerate() {
if let ConcreteValue::Function(function) = value {
let function_disassembly = function
.chunk()
.disassembler()
.styled(self.styled)
.indent(self.indent + 1)
.disassemble();
self.output.push_str(&function_disassembly);
continue;
}
let value_display = { let value_display = {
let value_string = value.to_string(); let value_string = value.to_string();
@ -325,17 +338,6 @@ impl<'a> Disassembler<'a> {
let constant_display = format!("{index:^3} {value_display:^15}"); let constant_display = format!("{index:^3} {value_display:^15}");
self.push_details(&constant_display); self.push_details(&constant_display);
if let Value::Concrete(ConcreteValue::Function(function)) = value {
let function_disassembly = function
.chunk()
.disassembler()
.styled(self.styled)
.indent(self.indent + 1)
.disassemble();
self.output.push_str(&function_disassembly);
}
} }
self.push_border(&bottom_border); self.push_border(&bottom_border);

63
dust-lang/src/function.rs Normal file
View File

@ -0,0 +1,63 @@
use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
use crate::{Chunk, Type};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Function {
chunk: Chunk,
}
impl Function {
pub fn new(chunk: Chunk) -> Self {
Self { chunk }
}
pub fn chunk(&self) -> &Chunk {
&self.chunk
}
pub fn r#type(&self) -> Type {
Type::Function(self.chunk.r#type().clone())
}
pub fn as_borrowed(&self) -> FunctionBorrowed {
FunctionBorrowed::new(&self.chunk)
}
}
impl Display for Function {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.chunk.r#type())
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct FunctionBorrowed<'a> {
chunk: &'a Chunk,
}
impl<'a> FunctionBorrowed<'a> {
pub fn new(chunk: &'a Chunk) -> Self {
Self { chunk }
}
pub fn chunk(&self) -> &Chunk {
self.chunk
}
pub fn r#type(&self) -> Type {
Type::Function(self.chunk.r#type().clone())
}
pub fn to_owned(&self) -> Function {
Function::new(self.chunk.clone())
}
}
impl<'a> Display for FunctionBorrowed<'a> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.chunk.r#type())
}
}

View File

@ -657,13 +657,13 @@ mod tests {
#[test] #[test]
fn r#move() { fn r#move() {
let mut instruction = Instruction::r#move(0, 1); let mut instruction = Instruction::r#move(4, 1);
instruction.set_b_is_constant(); instruction.set_b_is_constant();
instruction.set_c_is_constant(); instruction.set_c_is_constant();
assert_eq!(instruction.operation(), Operation::Move); assert_eq!(instruction.operation(), Operation::Move);
assert_eq!(instruction.a(), 0); assert_eq!(instruction.a(), 4);
assert_eq!(instruction.b(), 1); assert_eq!(instruction.b(), 1);
assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant());
assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant());
@ -690,13 +690,13 @@ mod tests {
#[test] #[test]
fn load_constant() { fn load_constant() {
let mut instruction = Instruction::load_constant(0, 1, true); let mut instruction = Instruction::load_constant(4, 1, true);
instruction.set_b_is_constant(); instruction.set_b_is_constant();
instruction.set_c_is_constant(); instruction.set_c_is_constant();
assert_eq!(instruction.operation(), Operation::LoadConstant); assert_eq!(instruction.operation(), Operation::LoadConstant);
assert_eq!(instruction.a(), 0); assert_eq!(instruction.a(), 4);
assert_eq!(instruction.b(), 1); assert_eq!(instruction.b(), 1);
assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant());
assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant());
@ -705,10 +705,10 @@ mod tests {
#[test] #[test]
fn load_list() { fn load_list() {
let instruction = Instruction::load_list(0, 1); let instruction = Instruction::load_list(4, 1);
assert_eq!(instruction.operation(), Operation::LoadList); assert_eq!(instruction.operation(), Operation::LoadList);
assert_eq!(instruction.a(), 0); assert_eq!(instruction.a(), 4);
assert_eq!(instruction.b(), 1); assert_eq!(instruction.b(), 1);
} }
@ -722,12 +722,12 @@ mod tests {
#[test] #[test]
fn declare_local() { fn declare_local() {
let mut instruction = Instruction::define_local(0, 1, true); let mut instruction = Instruction::define_local(4, 1, true);
instruction.set_b_is_constant(); instruction.set_b_is_constant();
assert_eq!(instruction.operation(), Operation::DefineLocal); assert_eq!(instruction.operation(), Operation::DefineLocal);
assert_eq!(instruction.a(), 0); assert_eq!(instruction.a(), 4);
assert_eq!(instruction.b(), 1); assert_eq!(instruction.b(), 1);
assert_eq!(instruction.c(), true as u8); assert_eq!(instruction.c(), true as u8);
assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant());
@ -735,26 +735,26 @@ mod tests {
#[test] #[test]
fn add() { fn add() {
let mut instruction = Instruction::add(1, 1, 0); let mut instruction = Instruction::add(1, 1, 4);
instruction.set_b_is_constant(); instruction.set_b_is_constant();
assert_eq!(instruction.operation(), Operation::Add); assert_eq!(instruction.operation(), Operation::Add);
assert_eq!(instruction.a(), 1); assert_eq!(instruction.a(), 1);
assert_eq!(instruction.b(), 1); assert_eq!(instruction.b(), 1);
assert_eq!(instruction.c(), 0); assert_eq!(instruction.c(), 4);
assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant());
} }
#[test] #[test]
fn subtract() { fn subtract() {
let mut instruction = Instruction::subtract(0, 1, 2); let mut instruction = Instruction::subtract(4, 1, 2);
instruction.set_b_is_constant(); instruction.set_b_is_constant();
instruction.set_c_is_constant(); instruction.set_c_is_constant();
assert_eq!(instruction.operation(), Operation::Subtract); assert_eq!(instruction.operation(), Operation::Subtract);
assert_eq!(instruction.a(), 0); assert_eq!(instruction.a(), 4);
assert_eq!(instruction.b(), 1); assert_eq!(instruction.b(), 1);
assert_eq!(instruction.c(), 2); assert_eq!(instruction.c(), 2);
assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant());
@ -763,13 +763,13 @@ mod tests {
#[test] #[test]
fn multiply() { fn multiply() {
let mut instruction = Instruction::multiply(0, 1, 2); let mut instruction = Instruction::multiply(4, 1, 2);
instruction.set_b_is_constant(); instruction.set_b_is_constant();
instruction.set_c_is_constant(); instruction.set_c_is_constant();
assert_eq!(instruction.operation(), Operation::Multiply); assert_eq!(instruction.operation(), Operation::Multiply);
assert_eq!(instruction.a(), 0); assert_eq!(instruction.a(), 4);
assert_eq!(instruction.b(), 1); assert_eq!(instruction.b(), 1);
assert_eq!(instruction.c(), 2); assert_eq!(instruction.c(), 2);
assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant());
@ -778,13 +778,13 @@ mod tests {
#[test] #[test]
fn divide() { fn divide() {
let mut instruction = Instruction::divide(0, 1, 2); let mut instruction = Instruction::divide(4, 1, 2);
instruction.set_b_is_constant(); instruction.set_b_is_constant();
instruction.set_c_is_constant(); instruction.set_c_is_constant();
assert_eq!(instruction.operation(), Operation::Divide); assert_eq!(instruction.operation(), Operation::Divide);
assert_eq!(instruction.a(), 0); assert_eq!(instruction.a(), 4);
assert_eq!(instruction.b(), 1); assert_eq!(instruction.b(), 1);
assert_eq!(instruction.c(), 2); assert_eq!(instruction.c(), 2);
assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant());
@ -827,13 +827,13 @@ mod tests {
#[test] #[test]
fn negate() { fn negate() {
let mut instruction = Instruction::negate(0, 1); let mut instruction = Instruction::negate(4, 1);
instruction.set_b_is_constant(); instruction.set_b_is_constant();
instruction.set_c_is_constant(); instruction.set_c_is_constant();
assert_eq!(instruction.operation(), Operation::Negate); assert_eq!(instruction.operation(), Operation::Negate);
assert_eq!(instruction.a(), 0); assert_eq!(instruction.a(), 4);
assert_eq!(instruction.b(), 1); assert_eq!(instruction.b(), 1);
assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant());
assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant());
@ -841,13 +841,13 @@ mod tests {
#[test] #[test]
fn not() { fn not() {
let mut instruction = Instruction::not(0, 1); let mut instruction = Instruction::not(4, 1);
instruction.set_b_is_constant(); instruction.set_b_is_constant();
instruction.set_c_is_constant(); instruction.set_c_is_constant();
assert_eq!(instruction.operation(), Operation::Not); assert_eq!(instruction.operation(), Operation::Not);
assert_eq!(instruction.a(), 0); assert_eq!(instruction.a(), 4);
assert_eq!(instruction.b(), 1); assert_eq!(instruction.b(), 1);
assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant());
assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant());

View File

@ -5,29 +5,33 @@ pub mod compiler;
pub mod disassembler; pub mod disassembler;
pub mod dust_error; pub mod dust_error;
pub mod formatter; pub mod formatter;
pub mod function;
pub mod instruction; pub mod instruction;
pub mod lexer; pub mod lexer;
pub mod native_function; pub mod native_function;
pub mod operation; pub mod operation;
pub mod optimizer; pub mod optimizer;
pub mod scope;
pub mod token; pub mod token;
pub mod r#type; pub mod r#type;
pub mod value; pub mod value;
pub mod vm; pub mod vm;
pub use crate::chunk::{Chunk, ChunkError, Local, Scope}; pub use crate::chunk::{Chunk, ChunkError, Local};
pub use crate::compiler::{compile, CompileError, Compiler}; pub use crate::compiler::{compile, CompileError, Compiler};
pub use crate::disassembler::Disassembler; pub use crate::disassembler::Disassembler;
pub use crate::dust_error::{AnnotatedError, DustError}; pub use crate::dust_error::{AnnotatedError, DustError};
pub use crate::formatter::{format, Formatter}; pub use crate::formatter::{format, Formatter};
pub use crate::function::{Function, FunctionBorrowed};
pub use crate::instruction::Instruction; pub use crate::instruction::Instruction;
pub use crate::lexer::{lex, LexError, Lexer}; pub use crate::lexer::{lex, LexError, Lexer};
pub use crate::native_function::{NativeFunction, NativeFunctionError}; pub use crate::native_function::{NativeFunction, NativeFunctionError};
pub use crate::operation::Operation; pub use crate::operation::Operation;
pub use crate::optimizer::Optimizer; pub use crate::optimizer::Optimizer;
pub use crate::r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict}; pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
pub use crate::scope::Scope;
pub use crate::token::{output_token_list, Token, TokenKind, TokenOwned}; pub use crate::token::{output_token_list, Token, TokenKind, TokenOwned};
pub use crate::value::{ConcreteValue, Function, Value, ValueError}; pub use crate::value::{ConcreteValue, Value, ValueError};
pub use crate::vm::{run, Vm, VmError}; pub use crate::vm::{run, Vm, VmError};
use std::fmt::Display; use std::fmt::Display;

View File

@ -0,0 +1,157 @@
use std::io::{self, stdout, Write};
use crate::{ConcreteValue, Instruction, NativeFunctionError, Span, Value, Vm, VmError};
pub fn panic(
vm: &Vm,
instruction: Instruction,
position: Span,
) -> Result<Option<ConcreteValue>, VmError> {
let argument_count = instruction.c();
let message = if argument_count == 0 {
None
} else {
let mut message = String::new();
for argument_index in 0..argument_count {
if argument_index != 0 {
message.push(' ');
}
let argument = vm.open_register(argument_index, position)?;
message.push_str(&argument.to_string());
}
Some(message)
};
Err(VmError::NativeFunction(NativeFunctionError::Panic {
message,
position,
}))
}
pub fn to_string(
vm: &Vm,
instruction: Instruction,
position: Span,
) -> Result<Option<ConcreteValue>, VmError> {
let argument_count = instruction.c();
if argument_count != 1 {
return Err(VmError::NativeFunction(
NativeFunctionError::ExpectedArgumentCount {
expected: 1,
found: argument_count as usize,
position,
},
));
}
let mut string = String::new();
for argument_index in 0..argument_count {
let argument = vm.open_register(argument_index, position)?;
string.push_str(&argument.to_string());
}
Ok(Some(ConcreteValue::String(string)))
}
pub fn read_line(
_: &Vm,
instruction: Instruction,
position: Span,
) -> Result<Option<ConcreteValue>, VmError> {
let argument_count = instruction.c();
if argument_count != 0 {
return Err(VmError::NativeFunction(
NativeFunctionError::ExpectedArgumentCount {
expected: 0,
found: argument_count as usize,
position,
},
));
}
let mut buffer = String::new();
match io::stdin().read_line(&mut buffer) {
Ok(_) => Ok(Some(ConcreteValue::String(
buffer.trim_end_matches('\n').to_string(),
))),
Err(error) => Err(VmError::NativeFunction(NativeFunctionError::Io {
error: error.kind(),
position,
})),
}
}
pub fn write(
vm: &Vm,
instruction: Instruction,
position: Span,
) -> Result<Option<ConcreteValue>, VmError> {
let to_register = instruction.a();
let argument_count = instruction.c();
let mut stdout = stdout();
let map_err = |io_error: io::Error| {
VmError::NativeFunction(NativeFunctionError::Io {
error: io_error.kind(),
position,
})
};
let first_argument = to_register.saturating_sub(argument_count);
for argument_index in first_argument..to_register {
if argument_index != first_argument {
stdout.write(b" ").map_err(map_err)?;
}
let argument_string = vm.open_register(argument_index, position)?.to_string();
stdout
.write_all(argument_string.as_bytes())
.map_err(map_err)?;
}
Ok(None)
}
pub fn write_line(
vm: &Vm,
instruction: Instruction,
position: Span,
) -> Result<Option<ConcreteValue>, VmError> {
let to_register = instruction.a();
let argument_count = instruction.c();
let mut stdout = stdout();
let map_err = |io_error: io::Error| {
VmError::NativeFunction(NativeFunctionError::Io {
error: io_error.kind(),
position,
})
};
let first_argument = to_register.saturating_sub(argument_count);
for argument_index in first_argument..to_register {
if argument_index != first_argument {
stdout.write(b" ").map_err(map_err)?;
}
let argument_string = vm.open_register(argument_index, position)?.to_string();
stdout
.write_all(argument_string.as_bytes())
.map_err(map_err)?;
}
stdout.write(b"\n").map_err(map_err)?;
Ok(None)
}

View File

@ -2,20 +2,20 @@
//! //!
//! Native functions are used either to implement features that are not possible to implement in //! Native functions are used either to implement features that are not possible to implement in
//! Dust itself or that are more efficient to implement in Rust. //! Dust itself or that are more efficient to implement in Rust.
mod logic;
use std::{ use std::{
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
io::{self, stdin, stdout, Write}, io::{self},
string::{self}, string::{self},
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{AnnotatedError, ConcreteValue, FunctionType, Instruction, Span, Type, Vm, VmError};
AnnotatedError, ConcreteValue, FunctionType, Instruction, Span, Type, Value, Vm, VmError,
};
macro_rules! define_native_function { macro_rules! define_native_function {
($(($name:ident, $byte:literal, $str:expr, $type:expr)),*) => { ($(($name:ident, $byte:literal, $str:expr, $type:expr, $function:expr)),*) => {
/// A dust-native function. /// A dust-native function.
/// ///
/// See the [module-level documentation](index.html) for more information. /// See the [module-level documentation](index.html) for more information.
@ -27,6 +27,19 @@ macro_rules! define_native_function {
} }
impl NativeFunction { impl NativeFunction {
pub fn call(
&self,
vm: &mut Vm,
instruction: Instruction,
span: Span
) -> Result<Option<ConcreteValue>, VmError> {
match self {
$(
NativeFunction::$name => $function(vm, instruction, span),
)*
}
}
pub fn as_str(&self) -> &'static str { pub fn as_str(&self) -> &'static str {
match self { match self {
$( $(
@ -91,18 +104,25 @@ macro_rules! define_native_function {
}; };
} }
impl Display for NativeFunction {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
define_native_function! { define_native_function! {
// Assertion // Assertion
( // (
Assert, // Assert,
0_u8, // 0_u8,
"assert", // "assert",
FunctionType { // FunctionType {
type_parameters: None, // type_parameters: None,
value_parameters: None, // value_parameters: None,
return_type: Box::new(Type::None) // return_type: Box::new(Type::None)
} // },
), // assert
// ),
// (AssertEqual, 1_u8, "assert_equal", false), // (AssertEqual, 1_u8, "assert_equal", false),
// (AssertNotEqual, 2_u8, "assert_not_equal", false), // (AssertNotEqual, 2_u8, "assert_not_equal", false),
( (
@ -113,7 +133,8 @@ define_native_function! {
type_parameters: None, type_parameters: None,
value_parameters: None, value_parameters: None,
return_type: Box::new(Type::None) return_type: Box::new(Type::None)
} },
logic::panic
), ),
// // Type conversion // // Type conversion
@ -129,7 +150,8 @@ define_native_function! {
type_parameters: None, type_parameters: None,
value_parameters: Some(vec![(0, Type::Any)]), value_parameters: Some(vec![(0, Type::Any)]),
return_type: Box::new(Type::String { length: None }) return_type: Box::new(Type::String { length: None })
} },
logic::to_string
), ),
// // List and string // // List and string
@ -189,7 +211,8 @@ define_native_function! {
type_parameters: None, type_parameters: None,
value_parameters: None, value_parameters: None,
return_type: Box::new(Type::String { length: None }) return_type: Box::new(Type::String { length: None })
} },
logic::read_line
), ),
// (ReadTo, 51_u8, "read_to", false), // (ReadTo, 51_u8, "read_to", false),
// (ReadUntil, 52_u8, "read_until", true), // (ReadUntil, 52_u8, "read_until", true),
@ -204,7 +227,8 @@ define_native_function! {
type_parameters: None, type_parameters: None,
value_parameters: Some(vec![(0, Type::String { length: None })]), value_parameters: Some(vec![(0, Type::String { length: None })]),
return_type: Box::new(Type::None) return_type: Box::new(Type::None)
} },
logic::write
), ),
// (WriteFile, 56_u8, "write_file", false), // (WriteFile, 56_u8, "write_file", false),
( (
@ -215,7 +239,8 @@ define_native_function! {
type_parameters: None, type_parameters: None,
value_parameters: Some(vec![(0, Type::String { length: None })]), value_parameters: Some(vec![(0, Type::String { length: None })]),
return_type: Box::new(Type::None) return_type: Box::new(Type::None)
} },
logic::write_line
) )
// // Random // // Random
@ -223,143 +248,6 @@ define_native_function! {
// (RandomInRange, 59_u8, "random_in_range", true) // (RandomInRange, 59_u8, "random_in_range", true)
} }
impl NativeFunction {
pub fn call(
&self,
instruction: Instruction,
vm: &Vm,
position: Span,
) -> Result<Option<Value>, VmError> {
let to_register = instruction.a();
let argument_count = instruction.c();
let return_value = match self {
NativeFunction::Panic => {
let message = if argument_count == 0 {
None
} else {
let mut message = String::new();
for argument_index in 0..argument_count {
if argument_index != 0 {
message.push(' ');
}
let argument = vm.open_register(argument_index, position)?;
message.push_str(&argument.to_string());
}
Some(message)
};
return Err(VmError::NativeFunction(NativeFunctionError::Panic {
message,
position,
}));
}
// Type conversion
NativeFunction::ToString => {
let mut string = String::new();
for argument_index in 0..argument_count {
let argument = vm.open_register(argument_index, position)?;
string.push_str(&argument.to_string());
}
Some(Value::Concrete(ConcreteValue::String(string)))
}
// I/O
NativeFunction::ReadLine => {
let mut buffer = String::new();
stdin().read_line(&mut buffer).map_err(|io_error| {
VmError::NativeFunction(NativeFunctionError::Io {
error: io_error.kind(),
position,
})
})?;
buffer = buffer.trim_end_matches('\n').to_string();
Some(Value::Concrete(ConcreteValue::String(buffer)))
}
NativeFunction::Write => {
let to_register = instruction.a();
let mut stdout = stdout();
let map_err = |io_error: io::Error| {
VmError::NativeFunction(NativeFunctionError::Io {
error: io_error.kind(),
position,
})
};
let first_argument = to_register.saturating_sub(argument_count);
let last_argument = to_register.saturating_sub(1);
for argument_index in first_argument..=last_argument {
if argument_index != first_argument {
stdout.write(b" ").map_err(map_err)?;
}
let argument_string = vm.open_register(argument_index, position)?.to_string();
stdout
.write_all(argument_string.as_bytes())
.map_err(map_err)?;
}
None
}
NativeFunction::WriteLine => {
let mut stdout = stdout();
let map_err = |io_error: io::Error| {
VmError::NativeFunction(NativeFunctionError::Io {
error: io_error.kind(),
position,
})
};
let first_index = to_register.saturating_sub(argument_count);
for (index, register) in (first_index..to_register).enumerate() {
if index != 0 {
stdout.write(b" ").map_err(map_err)?;
}
let argument = vm.open_register(register, position)?;
if let Value::Concrete(ConcreteValue::String(string)) = argument {
let bytes = string.as_bytes();
stdout.write_all(bytes).map_err(map_err)?;
} else {
let bytes = argument.to_string().into_bytes();
stdout.write_all(&bytes).map_err(map_err)?;
}
}
stdout.write(b"\n").map_err(map_err)?;
None
}
_ => todo!(),
};
Ok(return_value)
}
}
impl Display for NativeFunction {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum NativeFunctionError { pub enum NativeFunctionError {
ExpectedArgumentCount { ExpectedArgumentCount {

59
dust-lang/src/scope.rs Normal file
View File

@ -0,0 +1,59 @@
//! Scope, a type that represents the locality of a variable.
use std::{
cmp::Ordering,
fmt::{self, Display, Formatter},
};
use serde::{Deserialize, Serialize};
/// Variable locality, as defined by its depth and block index.
///
/// The `block index` is a unique identifier for a block within a chunk. It is used to differentiate
/// between blocks that are not nested together but have the same depth, i.e. sibling scopes. If the
/// `block_index` is 0, then the scope is the root scope of the chunk. The `block_index` is always 0
/// when the `depth` is 0. See [Chunk::begin_scope][] and [Chunk::end_scope][] to see how scopes are
/// incremented and decremented.
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Scope {
/// Level of block nesting.
pub depth: u8,
/// Index of the block in the chunk.
pub block_index: u8,
}
impl Scope {
pub fn new(depth: u8, block_index: u8) -> Self {
Self { depth, block_index }
}
pub fn contains(&self, other: &Self) -> bool {
match self.depth.cmp(&other.depth) {
Ordering::Less => false,
Ordering::Greater => self.block_index >= other.block_index,
Ordering::Equal => self.block_index == other.block_index,
}
}
#[inline]
pub fn begin(&mut self, block_index: u8) {
self.block_index = block_index;
self.depth += 1;
}
#[inline]
pub fn end(&mut self) {
self.depth -= 1;
if self.depth == 0 {
self.block_index = 0;
} else {
self.block_index -= 1;
}
}
}
impl Display for Scope {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "({}, {})", self.depth, self.block_index)
}
}

View File

@ -36,7 +36,7 @@ pub enum Type {
None, None,
Number, Number,
Range { Range {
r#type: RangeableType, r#type: Box<Type>,
}, },
SelfChunk, SelfChunk,
String { String {
@ -72,6 +72,7 @@ impl Type {
| (Type::Character, Type::Character) | (Type::Character, Type::Character)
| (Type::Float, Type::Float) | (Type::Float, Type::Float)
| (Type::Integer, Type::Integer) | (Type::Integer, Type::Integer)
| (Type::None, Type::None)
| (Type::String { .. }, Type::String { .. }) => return Ok(()), | (Type::String { .. }, Type::String { .. }) => return Ok(()),
( (
Type::Generic { Type::Generic {
@ -553,115 +554,8 @@ impl Display for EnumType {
} }
} }
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum RangeableType {
Byte,
Character,
Float,
Integer,
}
impl Display for RangeableType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
RangeableType::Byte => Type::Byte.fmt(f),
RangeableType::Character => Type::Character.fmt(f),
RangeableType::Float => Type::Float.fmt(f),
RangeableType::Integer => Type::Integer.fmt(f),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct TypeConflict { pub struct TypeConflict {
pub expected: Type, pub expected: Type,
pub actual: Type, pub actual: Type,
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_type_any() {
let foo = Type::Any;
let bar = Type::Any;
foo.check(&bar).unwrap();
}
#[test]
fn check_type_boolean() {
let foo = Type::Boolean;
let bar = Type::Boolean;
foo.check(&bar).unwrap();
}
#[test]
fn check_type_byte() {
let foo = Type::Byte;
let bar = Type::Byte;
foo.check(&bar).unwrap();
}
#[test]
fn check_type_character() {
let foo = Type::Character;
let bar = Type::Character;
foo.check(&bar).unwrap();
}
#[test]
fn errors() {
let foo = Type::Integer;
let bar = Type::String { length: None };
assert_eq!(
foo.check(&bar),
Err(TypeConflict {
actual: bar.clone(),
expected: foo.clone()
})
);
assert_eq!(
bar.check(&foo),
Err(TypeConflict {
actual: foo.clone(),
expected: bar.clone()
})
);
let types = [
Type::Boolean,
Type::Float,
Type::Integer,
Type::List {
item_type: Box::new(Type::Integer),
length: 42,
},
Type::Range {
r#type: RangeableType::Integer,
},
Type::String { length: None },
];
for left in types.clone() {
for right in types.clone() {
if left == right {
continue;
}
assert_eq!(
left.check(&right),
Err(TypeConflict {
actual: right.clone(),
expected: left.clone()
})
);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,19 +3,20 @@ use std::{
cmp::Ordering, cmp::Ordering,
collections::HashMap, collections::HashMap,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
mem::replace,
rc::Weak,
}; };
use crate::{ use crate::{
compile, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, compile, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionBorrowed,
Instruction, NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError, Instruction, NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError,
}; };
pub fn run(source: &str) -> Result<Option<Value>, DustError> { pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
let chunk = compile(source)?; let chunk = compile(source)?;
let mut vm = Vm::new(&chunk, None); let mut vm = Vm::new(&chunk, None);
vm.run() vm.run()
.map(|option| option.cloned())
.map_err(|error| DustError::Runtime { error, source }) .map_err(|error| DustError::Runtime { error, source })
} }
@ -31,19 +32,19 @@ pub fn run_and_display_output(source: &str) {
/// ///
/// See the [module-level documentation](index.html) for more information. /// See the [module-level documentation](index.html) for more information.
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub struct Vm<'chunk, 'parent> { pub struct Vm<'chunk, 'parent, 'stack> {
ip: usize, ip: usize,
chunk: &'chunk Chunk, chunk: &'chunk Chunk,
stack: Vec<Register>, stack: Vec<Register<'stack>>,
local_definitions: HashMap<u8, u8>, local_definitions: HashMap<u8, u8>,
last_assigned_register: Option<u8>, last_assigned_register: Option<u8>,
parent: Option<&'parent Vm<'chunk, 'parent>>, parent: Option<&'parent Vm<'chunk, 'stack, 'parent>>,
} }
impl<'chunk, 'parent> Vm<'chunk, 'parent> { impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
const STACK_LIMIT: usize = u16::MAX as usize; const STACK_LIMIT: usize = u16::MAX as usize;
pub fn new(chunk: &'chunk Chunk, parent: Option<&'parent Vm<'chunk, 'parent>>) -> Self { pub fn new(chunk: &'chunk Chunk, parent: Option<&'parent Vm<'chunk, 'parent, 'stack>>) -> Self {
Self { Self {
ip: 0, ip: 0,
chunk, chunk,
@ -54,27 +55,7 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
} }
} }
pub fn run(&mut self) -> Result<Option<&Value>, VmError> { pub fn run(&'stack mut self) -> Result<Option<ConcreteValue>, VmError> {
// DRY helper to get constant or register values for binary operations
fn get_arguments<'a>(
vm: &'a mut Vm,
instruction: Instruction,
position: Span,
) -> Result<(&'a Value, &'a Value), VmError> {
let left = if instruction.b_is_constant() {
vm.get_constant(instruction.b(), position)?
} else {
vm.open_register(instruction.b(), position)?
};
let right = if instruction.c_is_constant() {
vm.get_constant(instruction.c(), position)?
} else {
vm.open_register(instruction.c(), position)?
};
Ok((left, right))
}
while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() { while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() {
log::info!( log::info!(
"{} | {} | {} | {}", "{} | {} | {} | {}",
@ -117,9 +98,9 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
let to_register = instruction.a(); let to_register = instruction.a();
let boolean = instruction.b_as_boolean(); let boolean = instruction.b_as_boolean();
let jump = instruction.c_as_boolean(); let jump = instruction.c_as_boolean();
let value = Value::boolean(boolean); let boolean = ConcreteValue::boolean(boolean);
self.set_register(to_register, Register::Value(value), position)?; self.set_register(to_register, Register::Value(boolean), position)?;
if jump { if jump {
self.ip += 1; self.ip += 1;
@ -143,31 +124,23 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
Operation::LoadList => { Operation::LoadList => {
let to_register = instruction.a(); let to_register = instruction.a();
let start_register = instruction.b(); let start_register = instruction.b();
let item_type = (start_register..to_register) let mut list = Vec::new();
.find_map(|register_index| {
if let Ok(value) = self.open_register(register_index, position) {
Some(value.r#type())
} else {
None
}
})
.unwrap_or(Type::Any);
let value = Value::abstract_list(start_register, to_register, item_type);
self.set_register(to_register, Register::Value(value), position)?; for register_index in start_register..to_register {
let value = self.open_register(register_index, position)?;
list.push(value);
}
self.set_register(to_register, Register::List(list), position)?;
} }
Operation::LoadSelf => { Operation::LoadSelf => {
let to_register = instruction.a(); let to_register = instruction.a();
let value = Value::function( let function = Value::FunctionBorrowed(FunctionBorrowed::new(self.chunk));
self.chunk.clone(),
FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Box::new(Type::None),
},
);
self.set_register(to_register, Register::Value(value), position)?; // self.set_register(to_register, Register::Value(function), position)?;
todo!()
} }
Operation::DefineLocal => { Operation::DefineLocal => {
let from_register = instruction.a(); let from_register = instruction.a();
@ -209,45 +182,45 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
} }
Operation::Add => { Operation::Add => {
let to_register = instruction.a(); let to_register = instruction.a();
let (left, right) = get_arguments(self, instruction, position)?; let (left, right) = self.get_arguments(instruction, position)?;
let sum = left let sum = left
.add(right) .add(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.set_register(to_register, Register::Value(sum), position)?; self.set_register(to_register, Register::Value(sum), position)?;
} }
Operation::Subtract => { Operation::Subtract => {
let to_register = instruction.a(); let to_register = instruction.a();
let (left, right) = get_arguments(self, instruction, position)?; let (left, right) = self.get_arguments(instruction, position)?;
let difference = left let difference = left
.subtract(right) .subtract(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.set_register(to_register, Register::Value(difference), position)?; self.set_register(to_register, Register::Value(difference), position)?;
} }
Operation::Multiply => { Operation::Multiply => {
let to_register = instruction.a(); let to_register = instruction.a();
let (left, right) = get_arguments(self, instruction, position)?; let (left, right) = self.get_arguments(instruction, position)?;
let product = left let product = left
.multiply(right) .multiply(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.set_register(to_register, Register::Value(product), position)?; self.set_register(to_register, Register::Value(product), position)?;
} }
Operation::Divide => { Operation::Divide => {
let to_register = instruction.a(); let to_register = instruction.a();
let (left, right) = get_arguments(self, instruction, position)?; let (left, right) = self.get_arguments(instruction, position)?;
let quotient = left let quotient = left
.divide(right) .divide(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.set_register(to_register, Register::Value(quotient), position)?; self.set_register(to_register, Register::Value(quotient), position)?;
} }
Operation::Modulo => { Operation::Modulo => {
let to_register = instruction.a(); let to_register = instruction.a();
let (left, right) = get_arguments(self, instruction, position)?; let (left, right) = self.get_arguments(instruction, position)?;
let remainder = left let remainder = left
.modulo(right) .modulo(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.set_register(to_register, Register::Value(remainder), position)?; self.set_register(to_register, Register::Value(remainder), position)?;
@ -256,11 +229,11 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
let register = instruction.a(); let register = instruction.a();
let test_value = instruction.c_as_boolean(); let test_value = instruction.c_as_boolean();
let value = self.open_register(register, position)?; let value = self.open_register(register, position)?;
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value { let boolean = if let Some(boolean) = value.as_boolean() {
*boolean boolean
} else { } else {
return Err(VmError::ExpectedBoolean { return Err(VmError::ExpectedBoolean {
found: value.clone(), found: value.into_concrete(),
position, position,
}); });
}; };
@ -276,34 +249,26 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
Operation::Jump Operation::Jump
); );
let (left, right) = get_arguments(self, instruction, position)?;
let equal_result = left
.equal(right)
.map_err(|error| VmError::Value { error, position })?;
let boolean =
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = equal_result {
boolean
} else {
return Err(VmError::ExpectedBoolean {
found: equal_result.clone(),
position,
});
};
let compare_to = instruction.a_as_boolean(); let compare_to = instruction.a_as_boolean();
let (left, right) = self.get_arguments(instruction, position)?;
let equal_result = left
.equal(&right)
.map_err(|error| VmError::Value { error, position })?;
let is_equal = if let ConcreteValue::Boolean(boolean) = equal_result {
boolean
} else {
return Err(VmError::ExpectedBoolean {
found: equal_result.clone(),
position,
});
};
if boolean == compare_to { if is_equal == compare_to {
self.ip += 1; self.ip += 1;
} else { } else {
let (jump, _) = self.get_instruction(self.ip, position)?; let jump = self.get_instruction(self.ip, position)?.0;
let jump_distance = jump.a();
let is_positive = jump.b_as_boolean();
let new_ip = if is_positive {
self.ip + jump_distance as usize
} else {
self.ip - jump_distance as usize
};
self.ip = new_ip; self.jump(jump);
} }
} }
Operation::Less => { Operation::Less => {
@ -312,34 +277,26 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
Operation::Jump Operation::Jump
); );
let (left, right) = get_arguments(self, instruction, position)?;
let less_result = left
.less_than(right)
.map_err(|error| VmError::Value { error, position })?;
let boolean =
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = less_result {
boolean
} else {
return Err(VmError::ExpectedBoolean {
found: less_result.clone(),
position,
});
};
let compare_to = instruction.a_as_boolean(); let compare_to = instruction.a_as_boolean();
let (left, right) = self.get_arguments(instruction, position)?;
let less_result = left
.less_than(&right)
.map_err(|error| VmError::Value { error, position })?;
let is_less_than = if let ConcreteValue::Boolean(boolean) = less_result {
boolean
} else {
return Err(VmError::ExpectedBoolean {
found: less_result.clone(),
position,
});
};
if boolean == compare_to { if is_less_than == compare_to {
self.ip += 1; self.ip += 1;
} else { } else {
let jump = self.get_instruction(self.ip, position)?.0; let jump = self.get_instruction(self.ip, position)?.0;
let jump_distance = jump.a();
let is_positive = jump.b_as_boolean();
let new_ip = if is_positive {
self.ip + jump_distance as usize
} else {
self.ip - jump_distance as usize
};
self.ip = new_ip; self.jump(jump);
} }
} }
Operation::LessEqual => { Operation::LessEqual => {
@ -348,43 +305,32 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
Operation::Jump Operation::Jump
); );
let (left, right) = get_arguments(self, instruction, position)?;
let less_or_equal_result = left
.less_than_or_equal(right)
.map_err(|error| VmError::Value { error, position })?;
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) =
less_or_equal_result
{
boolean
} else {
return Err(VmError::ExpectedBoolean {
found: less_or_equal_result.clone(),
position,
});
};
let compare_to = instruction.a_as_boolean(); let compare_to = instruction.a_as_boolean();
let (left, right) = self.get_arguments(instruction, position)?;
let less_or_equal_result = left
.less_than_or_equal(&right)
.map_err(|error| VmError::Value { error, position })?;
let is_less_than_or_equal =
if let ConcreteValue::Boolean(boolean) = less_or_equal_result {
boolean
} else {
return Err(VmError::ExpectedBoolean {
found: less_or_equal_result.clone(),
position,
});
};
if boolean == compare_to { if is_less_than_or_equal == compare_to {
self.ip += 1; self.ip += 1;
} else { } else {
let jump = self.get_instruction(self.ip, position)?.0; let jump = self.get_instruction(self.ip, position)?.0;
let jump_distance = jump.a();
let is_positive = jump.b_as_boolean();
let new_ip = if is_positive {
self.ip + jump_distance as usize
} else {
self.ip - jump_distance as usize
};
self.ip = new_ip; self.jump(jump);
} }
} }
Operation::Negate => { Operation::Negate => {
let value = if instruction.b_is_constant() { let value =
self.get_constant(instruction.b(), position)? self.get_argument(instruction.b(), instruction.b_is_constant(), position)?;
} else {
self.open_register(instruction.b(), position)?
};
let negated = value let negated = value
.negate() .negate()
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
@ -392,42 +338,30 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
self.set_register(instruction.a(), Register::Value(negated), position)?; self.set_register(instruction.a(), Register::Value(negated), position)?;
} }
Operation::Not => { Operation::Not => {
let value = if instruction.b_is_constant() { let value =
self.get_constant(instruction.b(), position)? self.get_argument(instruction.b(), instruction.b_is_constant(), position)?;
} else {
self.open_register(instruction.b(), position)?
};
let not = value let not = value
.not() .not()
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.set_register(instruction.a(), Register::Value(not), position)?; self.set_register(instruction.a(), Register::Value(not), position)?;
} }
Operation::Jump => { Operation::Jump => self.jump(instruction),
let jump_distance = instruction.b();
let is_positive = instruction.c_as_boolean();
let new_ip = if is_positive {
self.ip + jump_distance as usize
} else {
self.ip - jump_distance as usize - 1
};
self.ip = new_ip;
}
Operation::Call => { Operation::Call => {
let to_register = instruction.a(); let to_register = instruction.a();
let function_register = instruction.b(); let function_register = instruction.b();
let argument_count = instruction.c(); let argument_count = instruction.c();
let value = self.open_register(function_register, position)?; let value = self.open_register(function_register, position)?;
let function = if let Value::Concrete(ConcreteValue::Function(function)) = value let function = if let Some(function) = value.as_function() {
{
function function
} else { } else {
return Err(VmError::ExpectedFunction { return Err(VmError::ExpectedFunction {
found: value.clone(), found: value.into_concrete(),
position, position,
}); });
}; };
let mut function_vm = Vm::new(function.chunk(), Some(self)); let mut function_vm = Vm::new(function.chunk(), Some(self));
let first_argument_index = function_register + 1; let first_argument_index = function_register + 1;
for argument_index in for argument_index in
@ -442,7 +376,7 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
)? )?
} }
let return_value = function_vm.run()?.cloned(); let return_value = function_vm.run()?;
if let Some(value) = return_value { if let Some(value) = return_value {
self.set_register(to_register, Register::Value(value), position)?; self.set_register(to_register, Register::Value(value), position)?;
@ -450,12 +384,12 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
} }
Operation::CallNative => { Operation::CallNative => {
let native_function = NativeFunction::from(instruction.b()); let native_function = NativeFunction::from(instruction.b());
let return_value = native_function.call(instruction, self, position)?; let return_value = native_function.call(self, instruction, position)?;
if let Some(value) = return_value { if let Some(concrete_value) = return_value {
let to_register = instruction.a(); let to_register = instruction.a();
self.set_register(to_register, Register::Value(value), position)?; self.set_register(to_register, Register::Value(concrete_value), position)?;
} }
} }
Operation::Return => { Operation::Return => {
@ -465,13 +399,12 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
return Ok(None); return Ok(None);
} }
let return_value = if let Some(register_index) = self.last_assigned_register { return if let Some(register_index) = self.last_assigned_register {
self.open_register(register_index, position)? self.open_register(register_index, position)
.map(|value| Some(value.into_concrete()))
} else { } else {
return Err(VmError::StackUnderflow { position }); Err(VmError::StackUnderflow { position })
}; };
return Ok(Some(return_value));
} }
} }
} }
@ -479,10 +412,149 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
Ok(None) Ok(None)
} }
pub(crate) fn open_register(
&'stack self,
register_index: u8,
position: Span,
) -> Result<&'stack ConcreteValue, VmError> {
let register_index = register_index as usize;
let register =
self.stack
.get(register_index)
.ok_or_else(|| VmError::RegisterIndexOutOfBounds {
index: register_index,
position,
})?;
log::trace!("Open R{register_index} to {register}");
match register {
Register::Value(value) => Ok(value),
Register::List(list) => Ok(ConcreteValue::List(
list.into_iter()
.map(|concrete_value| (*concrete_value).clone())
.collect(),
)),
Register::StackPointer(register_index) => self.open_register(*register_index, position),
Register::ConstantPointer(constant_index) => {
self.get_constant(*constant_index, position)
}
Register::ParentStackPointer(register_index) => {
let parent = self.parent.ok_or(VmError::ExpectedParent { position })?;
parent.open_register(*register_index, position)
}
Register::ParentConstantPointer(constant_index) => {
let parent = self
.parent
.as_ref()
.ok_or(VmError::ExpectedParent { position })?;
let constant = parent.get_constant(*constant_index, position)?;
Ok(constant)
}
Register::Empty => Err(VmError::EmptyRegister {
index: register_index,
position,
}),
}
}
fn get_concrete_from_register(
&'stack self,
register_index: u8,
position: Span,
) -> Result<ConcreteValue, VmError> {
let register_index = register_index as usize;
let register =
self.stack
.get(register_index)
.ok_or_else(|| VmError::RegisterIndexOutOfBounds {
index: register_index,
position,
})?;
let value = match register {
Register::Value(concrete_value) => concrete_value.clone(),
Register::List(list) => {
let items = list.into_iter().map(|value| (*value).clone()).collect();
ConcreteValue::List(items)
}
Register::StackPointer(register_index) => {
self.get_concrete_from_register(*register_index, position)?
}
Register::ConstantPointer(constant_pointer) => {
self.get_constant(*constant_pointer, position)?.clone()
}
Register::ParentStackPointer(register_index) => {
let parent = self.parent.ok_or(VmError::ExpectedParent { position })?;
parent.get_concrete_from_register(*register_index, position)?
}
Register::ParentConstantPointer(constant_index) => {
let parent = self
.parent
.as_ref()
.ok_or(VmError::ExpectedParent { position })?;
parent.get_constant(*constant_index, position)?.clone()
}
Register::Empty => {
return Err(VmError::EmptyRegister {
index: register_index,
position,
})
}
};
Ok(value)
}
/// DRY helper for handling JUMP instructions
fn jump(&mut self, jump: Instruction) {
let jump_distance = jump.b();
let is_positive = jump.c_as_boolean();
let new_ip = if is_positive {
self.ip + jump_distance as usize
} else {
self.ip - jump_distance as usize - 1
};
self.ip = new_ip;
}
/// DRY helper to get a constant or register values
fn get_argument(
&'stack self,
index: u8,
is_constant: bool,
position: Span,
) -> Result<Value<'stack>, VmError> {
let argument = if is_constant {
self.get_constant(index, position)?.as_reference_value()
} else {
self.open_register(index, position)?
};
Ok(argument)
}
/// DRY helper to get two arguments for binary operations
fn get_arguments(
&'stack self,
instruction: Instruction,
position: Span,
) -> Result<(Value<'stack>, Value<'stack>), VmError> {
let left = self.get_argument(instruction.b(), instruction.b_is_constant(), position)?;
let right = self.get_argument(instruction.c(), instruction.c_is_constant(), position)?;
Ok((left, right))
}
fn set_register( fn set_register(
&mut self, &mut self,
to_register: u8, to_register: u8,
register: Register, register: Register<'stack>,
position: Span, position: Span,
) -> Result<(), VmError> { ) -> Result<(), VmError> {
self.last_assigned_register = Some(to_register); self.last_assigned_register = Some(to_register);
@ -527,71 +599,18 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
} }
} }
fn get_constant(&self, index: u8, position: Span) -> Result<&Value, VmError> { fn get_constant(&self, index: u8, position: Span) -> Result<&'chunk ConcreteValue, VmError> {
self.chunk self.chunk
.get_constant(index) .get_constant(index)
.map_err(|error| VmError::Chunk { error, position }) .map_err(|error| VmError::Chunk { error, position })
} }
pub fn open_register(&self, register_index: u8, position: Span) -> Result<&Value, VmError> {
let register_index = register_index as usize;
let register =
self.stack
.get(register_index)
.ok_or_else(|| VmError::RegisterIndexOutOfBounds {
index: register_index,
position,
})?;
log::trace!("Open R{register_index} to {register}");
match register {
Register::Value(value) => Ok(value),
Register::StackPointer(register_index) => self.open_register(*register_index, position),
Register::ConstantPointer(constant_index) => {
self.get_constant(*constant_index, position)
}
Register::ParentStackPointer(register_index) => {
let parent = self
.parent
.as_ref()
.ok_or(VmError::ExpectedParent { position })?;
parent.open_register(*register_index, position)
}
Register::ParentConstantPointer(constant_index) => {
let parent = self
.parent
.as_ref()
.ok_or(VmError::ExpectedParent { position })?;
parent.get_constant(*constant_index, position)
}
Register::Empty => Err(VmError::EmptyRegister {
index: register_index,
position,
}),
}
}
fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> { fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> {
let max_ip = self.chunk.len() - 1; let ip = self.ip;
if self.ip > max_ip { self.ip += 1;
return self.get_instruction(max_ip, position);
} else {
self.ip += 1;
}
self.get_instruction(self.ip - 1, position) self.get_instruction(ip, position)
}
fn define_local(&mut self, local_index: u8, register_index: u8) -> Result<(), VmError> {
log::debug!("Define local L{}", local_index);
self.local_definitions.insert(local_index, register_index);
Ok(())
} }
fn get_instruction( fn get_instruction(
@ -603,23 +622,45 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
.get_instruction(index) .get_instruction(index)
.map_err(|error| VmError::Chunk { error, position }) .map_err(|error| VmError::Chunk { error, position })
} }
fn define_local(&mut self, local_index: u8, register_index: u8) -> Result<(), VmError> {
log::debug!("Define local L{}", local_index);
self.local_definitions.insert(local_index, register_index);
Ok(())
}
} }
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
enum Register { enum Register<'stack> {
Empty, Empty,
Value(Value), Value(ConcreteValue),
List(Vec<&'stack ConcreteValue>),
StackPointer(u8), StackPointer(u8),
ConstantPointer(u8), ConstantPointer(u8),
ParentStackPointer(u8), ParentStackPointer(u8),
ParentConstantPointer(u8), ParentConstantPointer(u8),
} }
impl Display for Register { impl<'stack> Display for Register<'stack> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Self::Empty => write!(f, "empty"), Self::Empty => write!(f, "empty"),
Self::Value(value) => write!(f, "{}", value), Self::Value(value) => write!(f, "{}", value),
Self::List(values) => {
write!(f, "[")?;
for (index, value) in values.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{}", value)?;
}
write!(f, "]")
}
Self::StackPointer(index) => write!(f, "R{}", index), Self::StackPointer(index) => write!(f, "R{}", index),
Self::ConstantPointer(index) => write!(f, "C{}", index), Self::ConstantPointer(index) => write!(f, "C{}", index),
Self::ParentStackPointer(index) => write!(f, "PR{}", index), Self::ParentStackPointer(index) => write!(f, "PR{}", index),
@ -631,25 +672,52 @@ impl Display for Register {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum VmError { pub enum VmError {
// Stack errors // Stack errors
StackOverflow { position: Span }, StackOverflow {
StackUnderflow { position: Span }, position: Span,
},
StackUnderflow {
position: Span,
},
// Register errors // Register errors
EmptyRegister { index: usize, position: Span }, EmptyRegister {
RegisterIndexOutOfBounds { index: usize, position: Span }, index: usize,
position: Span,
},
RegisterIndexOutOfBounds {
index: usize,
position: Span,
},
// Local errors // Local errors
UndefinedLocal { local_index: u8, position: Span }, UndefinedLocal {
local_index: u8,
position: Span,
},
// Execution errors // Execution errors
ExpectedBoolean { found: Value, position: Span }, ExpectedBoolean {
ExpectedFunction { found: Value, position: Span }, found: ConcreteValue,
ExpectedParent { position: Span }, position: Span,
},
ExpectedFunction {
found: ConcreteValue,
position: Span,
},
ExpectedParent {
position: Span,
},
// Wrappers for foreign errors // Wrappers for foreign errors
Chunk { error: ChunkError, position: Span }, Chunk {
error: ChunkError,
position: Span,
},
NativeFunction(NativeFunctionError), NativeFunction(NativeFunctionError),
Value { error: ValueError, position: Span }, Value {
error: ValueError,
position: Span,
},
} }
impl AnnotatedError for VmError { impl AnnotatedError for VmError {
@ -678,6 +746,7 @@ impl AnnotatedError for VmError {
Self::Chunk { error, .. } => Some(error.to_string()), Self::Chunk { error, .. } => Some(error.to_string()),
Self::EmptyRegister { index, .. } => Some(format!("Register R{index} is empty")), Self::EmptyRegister { index, .. } => Some(format!("Register R{index} is empty")),
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")), Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
Self::RegisterIndexOutOfBounds { index, .. } => { Self::RegisterIndexOutOfBounds { index, .. } => {
Some(format!("Register {index} does not exist")) Some(format!("Register {index} does not exist"))
} }

View File

@ -12,12 +12,12 @@ fn constant() {
(Instruction::load_constant(0, 0, false), Span(0, 2)), (Instruction::load_constant(0, 0, false), Span(0, 2)),
(Instruction::r#return(true), Span(2, 2)) (Instruction::r#return(true), Span(2, 2))
], ],
vec![Value::integer(42)], vec![ValueOwned::Primitive(Primitive::Integer(42))],
vec![] vec![]
)) ))
); );
assert_eq!(run(source), Ok(Some(Value::integer(42)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(42))));
} }
#[test] #[test]
@ -57,10 +57,14 @@ fn parentheses_precedence() {
), ),
(Instruction::r#return(true), Span(11, 11)), (Instruction::r#return(true), Span(11, 11)),
], ],
vec![Value::integer(1), Value::integer(2), Value::integer(3)], vec![
ValueOwned::integer(1),
ValueOwned::integer(2),
ValueOwned::integer(3)
],
vec![] vec![]
)) ))
); );
assert_eq!(run(source), Ok(Some(Value::integer(9)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(9))));
} }

View File

@ -20,12 +20,12 @@ fn equal() {
(Instruction::load_boolean(0, false, false), Span(2, 4)), (Instruction::load_boolean(0, false, false), Span(2, 4)),
(Instruction::r#return(true), Span(6, 6)), (Instruction::r#return(true), Span(6, 6)),
], ],
vec![Value::integer(1), Value::integer(2)], vec![ValueOwned::integer(1), ValueOwned::integer(2)],
vec![] vec![]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::boolean(false)))); assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false))));
} }
#[test] #[test]
@ -48,12 +48,12 @@ fn greater() {
(Instruction::load_boolean(0, false, false), Span(2, 3)), (Instruction::load_boolean(0, false, false), Span(2, 3)),
(Instruction::r#return(true), Span(5, 5)), (Instruction::r#return(true), Span(5, 5)),
], ],
vec![Value::integer(1), Value::integer(2)], vec![ValueOwned::integer(1), ValueOwned::integer(2)],
vec![] vec![]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::boolean(false)))); assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false))));
} }
#[test] #[test]
@ -76,12 +76,12 @@ fn greater_than_or_equal() {
(Instruction::load_boolean(0, false, false), Span(2, 4)), (Instruction::load_boolean(0, false, false), Span(2, 4)),
(Instruction::r#return(true), Span(6, 6)), (Instruction::r#return(true), Span(6, 6)),
], ],
vec![Value::integer(1), Value::integer(2)], vec![ValueOwned::integer(1), ValueOwned::integer(2)],
vec![] vec![]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::boolean(false)))); assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false))));
} }
#[test] #[test]
@ -104,12 +104,12 @@ fn less_than() {
(Instruction::load_boolean(0, false, false), Span(2, 3)), (Instruction::load_boolean(0, false, false), Span(2, 3)),
(Instruction::r#return(true), Span(5, 5)), (Instruction::r#return(true), Span(5, 5)),
], ],
vec![Value::integer(1), Value::integer(2)], vec![ValueOwned::integer(1), ValueOwned::integer(2)],
vec![] vec![]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::boolean(true)))); assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true))));
} }
#[test] #[test]
@ -132,12 +132,12 @@ fn less_than_or_equal() {
(Instruction::load_boolean(0, false, false), Span(2, 4)), (Instruction::load_boolean(0, false, false), Span(2, 4)),
(Instruction::r#return(true), Span(6, 6)), (Instruction::r#return(true), Span(6, 6)),
], ],
vec![Value::integer(1), Value::integer(2)], vec![ValueOwned::integer(1), ValueOwned::integer(2)],
vec![] vec![]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::boolean(true)))); assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true))));
} }
#[test] #[test]
@ -160,10 +160,10 @@ fn not_equal() {
(Instruction::load_boolean(0, false, false), Span(2, 4)), (Instruction::load_boolean(0, false, false), Span(2, 4)),
(Instruction::r#return(true), Span(6, 6)), (Instruction::r#return(true), Span(6, 6)),
], ],
vec![Value::integer(1), Value::integer(2)], vec![ValueOwned::integer(1), ValueOwned::integer(2)],
vec![] vec![]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::boolean(true)))); assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true))));
} }

View File

@ -22,12 +22,12 @@ fn equality_assignment_long() {
(Instruction::get_local(1, 0), Span(43, 44)), (Instruction::get_local(1, 0), Span(43, 44)),
(Instruction::r#return(true), Span(44, 44)), (Instruction::r#return(true), Span(44, 44)),
], ],
vec![Value::integer(4), Value::string("a")], vec![ValueOwned::integer(4), ValueOwned::string("a")],
vec![Local::new(1, Type::Boolean, false, Scope::default(),)] vec![Local::new(1, Type::Boolean, false, Scope::default(),)]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::boolean(true)))); assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true))));
} }
#[test] #[test]
@ -52,12 +52,12 @@ fn equality_assignment_short() {
(Instruction::get_local(1, 0), Span(15, 16)), (Instruction::get_local(1, 0), Span(15, 16)),
(Instruction::r#return(true), Span(16, 16)), (Instruction::r#return(true), Span(16, 16)),
], ],
vec![Value::integer(4), Value::string("a")], vec![ValueOwned::integer(4), ValueOwned::string("a")],
vec![Local::new(1, Type::Boolean, false, Scope::default())] vec![Local::new(1, Type::Boolean, false, Scope::default())]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::boolean(true)))); assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true))));
} }
#[test] #[test]
@ -104,18 +104,18 @@ fn if_else_assigment_false() {
(Instruction::r#return(true), Span(149, 149)), (Instruction::r#return(true), Span(149, 149)),
], ],
vec![ vec![
Value::integer(4), ValueOwned::integer(4),
Value::integer(3), ValueOwned::integer(3),
Value::integer(1), ValueOwned::integer(1),
Value::integer(2), ValueOwned::integer(2),
Value::integer(42), ValueOwned::integer(42),
Value::string("a") ValueOwned::string("a")
], ],
vec![Local::new(5, Type::Integer, false, Scope::default())] vec![Local::new(5, Type::Integer, false, Scope::default())]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::integer(42)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(42))));
} }
#[test] #[test]
@ -162,18 +162,18 @@ fn if_else_assigment_true() {
(Instruction::r#return(true), Span(149, 149)), (Instruction::r#return(true), Span(149, 149)),
], ],
vec![ vec![
Value::integer(4), ValueOwned::integer(4),
Value::integer(1), ValueOwned::integer(1),
Value::integer(2), ValueOwned::integer(2),
Value::integer(3), ValueOwned::integer(3),
Value::integer(42), ValueOwned::integer(42),
Value::string("a") ValueOwned::string("a")
], ],
vec![Local::new(5, Type::Integer, false, Scope::default())] vec![Local::new(5, Type::Integer, false, Scope::default())]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::integer(42)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(42))));
} }
#[test] #[test]
@ -210,10 +210,10 @@ fn if_else_complex() {
(Instruction::r#return(false), Span(95, 95)), (Instruction::r#return(false), Span(95, 95)),
], ],
vec![ vec![
Value::integer(1), ValueOwned::integer(1),
Value::integer(2), ValueOwned::integer(2),
Value::integer(3), ValueOwned::integer(3),
Value::integer(4), ValueOwned::integer(4),
], ],
vec![] vec![]
)) ))
@ -275,21 +275,21 @@ fn if_else_complex() {
// (Instruction::r#return(true), Span(146, 146)), // (Instruction::r#return(true), Span(146, 146)),
// ], // ],
// vec![ // vec![
// Value::integer(0), // ValueOwned::integer(0),
// Value::integer(1), // ValueOwned::integer(1),
// Value::integer(0), // ValueOwned::integer(0),
// Value::integer(2), // ValueOwned::integer(2),
// Value::integer(1), // ValueOwned::integer(1),
// Value::integer(0), // ValueOwned::integer(0),
// Value::integer(3), // ValueOwned::integer(3),
// Value::integer(3), // ValueOwned::integer(3),
// Value::integer(4) // ValueOwned::integer(4)
// ], // ],
// vec![] // vec![]
// )) // ))
// ); // );
// assert_eq!(run(source), Ok(Some(Value::integer(4)))); // assert_eq!(run(source), Ok(Some(ValueOwned::integer(4))));
// } // }
#[test] #[test]
@ -316,12 +316,16 @@ fn if_else_false() {
(Instruction::r#move(1, 0), Span(33, 33)), (Instruction::r#move(1, 0), Span(33, 33)),
(Instruction::r#return(true), Span(33, 33)), (Instruction::r#return(true), Span(33, 33)),
], ],
vec![Value::integer(1), Value::integer(2), Value::integer(42)], vec![
ValueOwned::integer(1),
ValueOwned::integer(2),
ValueOwned::integer(42)
],
vec![] vec![]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::integer(42)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(42))));
} }
#[test] #[test]
@ -348,12 +352,12 @@ fn if_else_true() {
(Instruction::r#move(1, 0), Span(33, 33)), (Instruction::r#move(1, 0), Span(33, 33)),
(Instruction::r#return(true), Span(33, 33)) (Instruction::r#return(true), Span(33, 33))
], ],
vec![Value::integer(1), Value::integer(42)], vec![ValueOwned::integer(1), ValueOwned::integer(42)],
vec![] vec![]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::integer(42)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(42))));
} }
#[test] #[test]
@ -378,7 +382,7 @@ fn if_false() {
), ),
(Instruction::r#return(false), Span(21, 21)) (Instruction::r#return(false), Span(21, 21))
], ],
vec![Value::integer(1), Value::integer(2)], vec![ValueOwned::integer(1), ValueOwned::integer(2)],
vec![] vec![]
)), )),
); );
@ -408,7 +412,7 @@ fn if_true() {
), ),
(Instruction::r#return(false), Span(21, 21)) (Instruction::r#return(false), Span(21, 21))
], ],
vec![Value::integer(1)], vec![ValueOwned::integer(1)],
vec![] vec![]
)), )),
); );

View File

@ -6,25 +6,18 @@ fn function() {
assert_eq!( assert_eq!(
run(source), run(source),
Ok(Some(Value::function( Ok(Some(ValueOwned::function(Chunk::with_data(
Chunk::with_data( None,
None, vec![
vec![ (Instruction::add(2, 0, 1), Span(30, 31)),
(Instruction::add(2, 0, 1), Span(30, 31)), (Instruction::r#return(true), Span(35, 35)),
(Instruction::r#return(true), Span(35, 35)), ],
], vec![ValueOwned::string("a"), ValueOwned::string("b"),],
vec![Value::string("a"), Value::string("b"),], vec![
vec![ Local::new(0, Type::Integer, false, Scope::default()),
Local::new(0, Type::Integer, false, Scope::default()), Local::new(1, Type::Integer, false, Scope::default())
Local::new(1, Type::Integer, false, Scope::default()) ]
] ))))
),
FunctionType {
type_parameters: None,
value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]),
return_type: Box::new(Type::Integer),
}
)))
); );
} }
@ -44,33 +37,26 @@ fn function_call() {
(Instruction::r#return(true), Span(41, 41)), (Instruction::r#return(true), Span(41, 41)),
], ],
vec![ vec![
Value::function( ValueOwned::function(Chunk::with_data(
Chunk::with_data( None,
None, vec![
vec![ (Instruction::add(2, 0, 1), Span(30, 31)),
(Instruction::add(2, 0, 1), Span(30, 31)), (Instruction::r#return(true), Span(35, 36)),
(Instruction::r#return(true), Span(35, 36)), ],
], vec![ValueOwned::string("a"), ValueOwned::string("b"),],
vec![Value::string("a"), Value::string("b"),], vec![
vec![ Local::new(0, Type::Integer, false, Scope::default()),
Local::new(0, Type::Integer, false, Scope::default()), Local::new(1, Type::Integer, false, Scope::default())
Local::new(1, Type::Integer, false, Scope::default()) ]
] )),
), ValueOwned::integer(1),
FunctionType { ValueOwned::integer(2)
type_parameters: None,
value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]),
return_type: Box::new(Type::Integer),
}
),
Value::integer(1),
Value::integer(2)
], ],
vec![] vec![]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::integer(3)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(3))));
} }
#[test] #[test]
@ -87,26 +73,19 @@ fn function_declaration() {
(Instruction::r#return(false), Span(40, 40)) (Instruction::r#return(false), Span(40, 40))
], ],
vec![ vec![
Value::string("add"), ValueOwned::string("add"),
Value::function( ValueOwned::function(Chunk::with_data(
Chunk::with_data( None,
None, vec![
vec![ (Instruction::add(2, 0, 1), Span(35, 36)),
(Instruction::add(2, 0, 1), Span(35, 36)), (Instruction::r#return(true), Span(40, 40)),
(Instruction::r#return(true), Span(40, 40)), ],
], vec![ValueOwned::string("a"), ValueOwned::string("b")],
vec![Value::string("a"), Value::string("b")], vec![
vec![ Local::new(0, Type::Integer, false, Scope::default()),
Local::new(0, Type::Integer, false, Scope::default()), Local::new(1, Type::Integer, false, Scope::default())
Local::new(1, Type::Integer, false, Scope::default()) ]
] ),)
),
FunctionType {
type_parameters: None,
value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]),
return_type: Box::new(Type::Integer),
},
)
], ],
vec![Local::new( vec![Local::new(
0, 0,

View File

@ -17,7 +17,7 @@ fn empty_list() {
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::abstract_list(0, 0, Type::Any)))); assert_eq!(run(source), Ok(Some(ValueOwned::list([]))));
} }
#[test] #[test]
@ -35,14 +35,22 @@ fn list() {
(Instruction::load_list(3, 0), Span(0, 9)), (Instruction::load_list(3, 0), Span(0, 9)),
(Instruction::r#return(true), Span(9, 9)), (Instruction::r#return(true), Span(9, 9)),
], ],
vec![Value::integer(1), Value::integer(2), Value::integer(3)], vec![
ValueOwned::integer(1),
ValueOwned::integer(2),
ValueOwned::integer(3)
],
vec![] vec![]
)), )),
); );
assert_eq!( assert_eq!(
run(source), run(source),
Ok(Some(Value::abstract_list(0, 3, Type::Integer))) Ok(Some(ValueOwned::list([
ValueOwned::integer(1),
ValueOwned::integer(2),
ValueOwned::integer(3)
])))
); );
} }
@ -74,11 +82,11 @@ fn list_with_complex_expression() {
(Instruction::r#return(true), Span(18, 18)), (Instruction::r#return(true), Span(18, 18)),
], ],
vec![ vec![
Value::integer(1), ValueOwned::integer(1),
Value::integer(2), ValueOwned::integer(2),
Value::integer(3), ValueOwned::integer(3),
Value::integer(4), ValueOwned::integer(4),
Value::integer(5) ValueOwned::integer(5)
], ],
vec![] vec![]
)), )),
@ -86,7 +94,10 @@ fn list_with_complex_expression() {
assert_eq!( assert_eq!(
run(source), run(source),
Ok(Some(Value::abstract_list(0, 4, Type::Integer))) Ok(Some(ValueOwned::list([
ValueOwned::integer(0),
ValueOwned::integer(4)
])))
); );
} }
@ -111,10 +122,10 @@ fn list_with_simple_expression() {
(Instruction::r#return(true), Span(13, 13)), (Instruction::r#return(true), Span(13, 13)),
], ],
vec![ vec![
Value::integer(1), ValueOwned::integer(1),
Value::integer(2), ValueOwned::integer(2),
Value::integer(3), ValueOwned::integer(3),
Value::integer(4), ValueOwned::integer(4),
], ],
vec![] vec![]
)), )),
@ -122,6 +133,9 @@ fn list_with_simple_expression() {
assert_eq!( assert_eq!(
run(source), run(source),
Ok(Some(Value::abstract_list(0, 3, Type::Integer))) Ok(Some(ValueOwned::list([
ValueOwned::integer(0),
ValueOwned::integer(3)
])))
); );
} }

View File

@ -20,7 +20,7 @@ fn and() {
)) ))
); );
assert_eq!(run(source), Ok(Some(Value::boolean(false)))); assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false))));
} }
#[test] #[test]
@ -43,7 +43,7 @@ fn or() {
)) ))
); );
assert_eq!(run(source), Ok(Some(Value::boolean(true)))); assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true))));
} }
#[test] #[test]
@ -65,7 +65,7 @@ fn variable_and() {
(Instruction::get_local(3, 1), Span(34, 35)), (Instruction::get_local(3, 1), Span(34, 35)),
(Instruction::r#return(true), Span(35, 35)), (Instruction::r#return(true), Span(35, 35)),
], ],
vec![Value::string("a"), Value::string("b"),], vec![ValueOwned::string("a"), ValueOwned::string("b"),],
vec![ vec![
Local::new(0, Type::Boolean, false, Scope::default()), Local::new(0, Type::Boolean, false, Scope::default()),
Local::new(1, Type::Boolean, false, Scope::default()), Local::new(1, Type::Boolean, false, Scope::default()),
@ -73,5 +73,5 @@ fn variable_and() {
)) ))
); );
assert_eq!(run(source), Ok(Some(Value::boolean(false)))); assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false))));
} }

View File

@ -22,14 +22,14 @@ fn r#while() {
(Instruction::r#return(true), Span(42, 42)), (Instruction::r#return(true), Span(42, 42)),
], ],
vec![ vec![
Value::integer(0), ValueOwned::integer(0),
Value::string("x"), ValueOwned::string("x"),
Value::integer(5), ValueOwned::integer(5),
Value::integer(1), ValueOwned::integer(1),
], ],
vec![Local::new(1, Type::Integer, true, Scope::default())] vec![Local::new(1, Type::Integer, true, Scope::default())]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::integer(5)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(5))));
} }

View File

@ -17,12 +17,12 @@ fn add() {
), ),
(Instruction::r#return(true), Span(5, 5)) (Instruction::r#return(true), Span(5, 5))
], ],
vec![Value::integer(1), Value::integer(2)], vec![ValueOwned::integer(1), ValueOwned::integer(2)],
vec![] vec![]
)) ))
); );
assert_eq!(run(source), Ok(Some(Value::integer(3)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(3))));
} }
#[test] #[test]
@ -40,12 +40,16 @@ fn add_assign() {
(Instruction::get_local(1, 0), Span(23, 24)), (Instruction::get_local(1, 0), Span(23, 24)),
(Instruction::r#return(true), Span(24, 24)) (Instruction::r#return(true), Span(24, 24))
], ],
vec![Value::integer(1), Value::string("a"), Value::integer(2)], vec![
ValueOwned::integer(1),
ValueOwned::string("a"),
ValueOwned::integer(2)
],
vec![Local::new(1, Type::Integer, true, Scope::default())] vec![Local::new(1, Type::Integer, true, Scope::default())]
)) ))
); );
assert_eq!(run(source), Ok(Some(Value::integer(3)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(3))));
} }
#[test] #[test]
@ -97,12 +101,12 @@ fn divide() {
), ),
(Instruction::r#return(true), Span(5, 5)) (Instruction::r#return(true), Span(5, 5))
], ],
vec![Value::integer(2)], vec![ValueOwned::integer(2)],
vec![] vec![]
)) ))
); );
assert_eq!(run(source), Ok(Some(Value::integer(1)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(1))));
} }
#[test] #[test]
@ -123,12 +127,12 @@ fn divide_assign() {
(Instruction::get_local(1, 0), Span(23, 24)), (Instruction::get_local(1, 0), Span(23, 24)),
(Instruction::r#return(true), Span(24, 24)) (Instruction::r#return(true), Span(24, 24))
], ],
vec![Value::integer(2), Value::string("a")], vec![ValueOwned::integer(2), ValueOwned::string("a")],
vec![Local::new(1, Type::Integer, true, Scope::default())] vec![Local::new(1, Type::Integer, true, Scope::default())]
)) ))
); );
assert_eq!(run(source), Ok(Some(Value::integer(1)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(1))));
} }
#[test] #[test]
@ -176,17 +180,17 @@ fn math_operator_precedence() {
(Instruction::r#return(true), Span(17, 17)), (Instruction::r#return(true), Span(17, 17)),
], ],
vec![ vec![
Value::integer(1), ValueOwned::integer(1),
Value::integer(2), ValueOwned::integer(2),
Value::integer(3), ValueOwned::integer(3),
Value::integer(4), ValueOwned::integer(4),
Value::integer(5), ValueOwned::integer(5),
], ],
vec![] vec![]
)) ))
); );
assert_eq!(run(source), Ok(Some(Value::integer(1)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(1))));
} }
#[test] #[test]
@ -206,12 +210,12 @@ fn multiply() {
), ),
(Instruction::r#return(true), Span(5, 5)), (Instruction::r#return(true), Span(5, 5)),
], ],
vec![Value::integer(1), Value::integer(2)], vec![ValueOwned::integer(1), ValueOwned::integer(2)],
vec![] vec![]
)) ))
); );
assert_eq!(run(source), Ok(Some(Value::integer(2)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(2))));
} }
#[test] #[test]
@ -232,12 +236,16 @@ fn multiply_assign() {
(Instruction::get_local(1, 0), Span(22, 23)), (Instruction::get_local(1, 0), Span(22, 23)),
(Instruction::r#return(true), Span(23, 23)) (Instruction::r#return(true), Span(23, 23))
], ],
vec![Value::integer(2), Value::string("a"), Value::integer(3)], vec![
ValueOwned::integer(2),
ValueOwned::string("a"),
ValueOwned::integer(3)
],
vec![Local::new(1, Type::Integer, true, Scope::default())] vec![Local::new(1, Type::Integer, true, Scope::default())]
)) ))
); );
assert_eq!(run(source), Ok(Some(Value::integer(6)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(6))));
} }
#[test] #[test]
@ -273,12 +281,12 @@ fn subtract() {
), ),
(Instruction::r#return(true), Span(5, 5)), (Instruction::r#return(true), Span(5, 5)),
], ],
vec![Value::integer(1), Value::integer(2)], vec![ValueOwned::integer(1), ValueOwned::integer(2)],
vec![] vec![]
)) ))
); );
assert_eq!(run(source), Ok(Some(Value::integer(-1)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(-1))));
} }
#[test] #[test]
@ -299,12 +307,16 @@ fn subtract_assign() {
(Instruction::get_local(1, 0), Span(24, 25)), (Instruction::get_local(1, 0), Span(24, 25)),
(Instruction::r#return(true), Span(25, 25)), (Instruction::r#return(true), Span(25, 25)),
], ],
vec![Value::integer(42), Value::string("x"), Value::integer(2)], vec![
ValueOwned::integer(42),
ValueOwned::string("x"),
ValueOwned::integer(2)
],
vec![Local::new(1, Type::Integer, true, Scope::default())] vec![Local::new(1, Type::Integer, true, Scope::default())]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::integer(40)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(40))));
} }
#[test] #[test]

View File

@ -17,7 +17,10 @@ fn panic() {
), ),
(Instruction::r#return(true), Span(27, 27)) (Instruction::r#return(true), Span(27, 27))
], ],
vec![Value::string("Goodbye world!"), Value::integer(42)], vec![
ValueOwned::string("Goodbye world!"),
ValueOwned::integer(42)
],
vec![] vec![]
)), )),
); );
@ -50,10 +53,10 @@ fn to_string() {
), ),
(Instruction::r#return(true), Span(13, 13)) (Instruction::r#return(true), Span(13, 13))
], ],
vec![Value::integer(42)], vec![ValueOwned::integer(42)],
vec![] vec![]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::string("42")))) assert_eq!(run(source), Ok(Some(ValueOwned::string("42"))))
} }

View File

@ -9,7 +9,7 @@ fn allow_access_to_parent_scope() {
} }
"#; "#;
assert_eq!(run(source), Ok(Some(Value::integer(1)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(1))));
} }
#[test] #[test]
@ -44,15 +44,15 @@ fn block_scope() {
(Instruction::r#return(false), Span(165, 165)) (Instruction::r#return(false), Span(165, 165))
], ],
vec![ vec![
Value::integer(0), ValueOwned::integer(0),
Value::string("a"), ValueOwned::string("a"),
Value::integer(42), ValueOwned::integer(42),
Value::string("b"), ValueOwned::string("b"),
Value::integer(1), ValueOwned::integer(1),
Value::string("c"), ValueOwned::string("c"),
Value::integer(2), ValueOwned::integer(2),
Value::string("d"), ValueOwned::string("d"),
Value::string("e"), ValueOwned::string("e"),
], ],
vec![ vec![
Local::new(1, Type::Integer, false, Scope::new(0, 0)), Local::new(1, Type::Integer, false, Scope::new(0, 0)),
@ -115,16 +115,16 @@ fn multiple_block_scopes() {
(Instruction::r#return(false), Span(307, 307)) (Instruction::r#return(false), Span(307, 307))
], ],
vec![ vec![
Value::integer(0), ValueOwned::integer(0),
Value::string("a"), ValueOwned::string("a"),
Value::integer(42), ValueOwned::integer(42),
Value::string("b"), ValueOwned::string("b"),
Value::integer(1), ValueOwned::integer(1),
Value::string("c"), ValueOwned::string("c"),
Value::integer(2), ValueOwned::integer(2),
Value::string("d"), ValueOwned::string("d"),
Value::string("q"), ValueOwned::string("q"),
Value::string("e"), ValueOwned::string("e"),
], ],
vec![ vec![
Local::new(1, Type::Integer, false, Scope::new(0, 0)), Local::new(1, Type::Integer, false, Scope::new(0, 0)),

View File

@ -12,12 +12,12 @@ fn negate() {
(*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)), (*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)),
(Instruction::r#return(true), Span(5, 5)), (Instruction::r#return(true), Span(5, 5)),
], ],
vec![Value::integer(42)], vec![ValueOwned::integer(42)],
vec![] vec![]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::integer(-42)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(-42))));
} }
#[test] #[test]
@ -38,5 +38,5 @@ fn not() {
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::boolean(false)))); assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false))));
} }

View File

@ -13,7 +13,7 @@ fn define_local() {
(Instruction::define_local(0, 0, false), Span(4, 5)), (Instruction::define_local(0, 0, false), Span(4, 5)),
(Instruction::r#return(false), Span(11, 11)) (Instruction::r#return(false), Span(11, 11))
], ],
vec![Value::integer(42), Value::string("x")], vec![ValueOwned::integer(42), ValueOwned::string("x")],
vec![Local::new(1, Type::Integer, false, Scope::default())] vec![Local::new(1, Type::Integer, false, Scope::default())]
)), )),
); );
@ -54,10 +54,14 @@ fn set_local() {
(Instruction::get_local(2, 0), Span(24, 25)), (Instruction::get_local(2, 0), Span(24, 25)),
(Instruction::r#return(true), Span(25, 25)), (Instruction::r#return(true), Span(25, 25)),
], ],
vec![Value::integer(41), Value::string("x"), Value::integer(42)], vec![
ValueOwned::integer(41),
ValueOwned::string("x"),
ValueOwned::integer(42)
],
vec![Local::new(1, Type::Integer, true, Scope::default())] vec![Local::new(1, Type::Integer, true, Scope::default())]
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::integer(42)))); assert_eq!(run(source), Ok(Some(ValueOwned::integer(42))));
} }