1
0

Begin removing chunk errors; Use constants for identifiers

This commit is contained in:
Jeff 2024-11-04 15:38:58 -05:00
parent a9e675cb4f
commit a2e7a4e73e
12 changed files with 429 additions and 617 deletions

View File

@ -6,11 +6,11 @@ use std::{
use colored::Colorize;
use serde::{Deserialize, Serialize};
use crate::{AnnotatedError, Identifier, Instruction, Span, Type, Value};
use crate::{Instruction, Span, Type, Value};
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Chunk {
name: Option<Identifier>,
name: Option<String>,
instructions: Vec<(Instruction, Span)>,
constants: Vec<Value>,
locals: Vec<Local>,
@ -18,7 +18,7 @@ pub struct Chunk {
}
impl Chunk {
pub fn new(name: Option<Identifier>) -> Self {
pub fn new(name: Option<String>) -> Self {
Self {
name,
instructions: Vec::new(),
@ -29,7 +29,7 @@ impl Chunk {
}
pub fn with_data(
name: Option<Identifier>,
name: Option<String>,
instructions: Vec<(Instruction, Span)>,
constants: Vec<Value>,
locals: Vec<Local>,
@ -43,11 +43,11 @@ impl Chunk {
}
}
pub fn name(&self) -> Option<&Identifier> {
pub fn name(&self) -> Option<&String> {
self.name.as_ref()
}
pub fn set_name(&mut self, name: Identifier) {
pub fn set_name(&mut self, name: String) {
self.name = Some(name);
}
@ -59,7 +59,19 @@ impl Chunk {
self.instructions.is_empty()
}
pub fn instructions(&self) -> &[(Instruction, Span)] {
pub fn constants(&self) -> &Vec<Value> {
&self.constants
}
pub fn constants_mut(&mut self) -> &mut Vec<Value> {
&mut self.constants
}
pub fn take_constants(self) -> Vec<Value> {
self.constants
}
pub fn instructions(&self) -> &Vec<(Instruction, Span)> {
&self.instructions
}
@ -67,151 +79,42 @@ impl Chunk {
&mut self.instructions
}
pub fn get_instruction(
&self,
offset: usize,
position: Span,
) -> Result<&(Instruction, Span), ChunkError> {
self.instructions
.get(offset)
.ok_or(ChunkError::InstructionIndexOfBounds { offset, position })
pub fn locals(&self) -> &Vec<Local> {
&self.locals
}
pub fn push_instruction(&mut self, instruction: Instruction, position: Span) {
self.instructions.push((instruction, position));
pub fn locals_mut(&mut self) -> &mut Vec<Local> {
&mut self.locals
}
pub fn insert_instruction(
&mut self,
index: usize,
instruction: Instruction,
position: Span,
) -> Result<(), ChunkError> {
if index > self.instructions.len() {
Err(ChunkError::InstructionIndexOfBounds {
offset: index,
position,
})
} else {
self.instructions.insert(index, (instruction, position));
Ok(())
}
pub fn scope_depth(&self) -> usize {
self.scope_depth
}
pub fn take_constants(self) -> Vec<Value> {
self.constants
pub fn get_constant(&self, index: u8) -> Option<&Value> {
self.constants.get(index as usize)
}
pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> {
let index = index as usize;
self.constants
.get(index)
.ok_or(ChunkError::ConstantIndexOutOfBounds { index, position })
}
pub fn push_or_get_constant(&mut self, value: Value, position: Span) -> Result<u8, ChunkError> {
pub fn push_or_get_constant(&mut self, value: Value) -> u8 {
if let Some(index) = self
.constants
.iter()
.position(|constant| constant == &value)
{
return Ok(index as u8);
return index as u8;
}
let starting_length = self.constants.len();
self.constants.push(value);
if starting_length + 1 > (u8::MAX as usize) {
Err(ChunkError::ConstantOverflow { position })
} else {
self.constants.push(value);
Ok(starting_length as u8)
}
(self.constants.len() - 1) as u8
}
pub fn locals(&self) -> &[Local] {
&self.locals
}
pub fn get_local(&self, index: u8, position: Span) -> Result<&Local, ChunkError> {
let index = index as usize;
self.locals
.get(index)
.ok_or(ChunkError::LocalIndexOutOfBounds { index, position })
}
pub fn get_identifier(&self, index: u8) -> Option<&Identifier> {
self.locals
.get(index as usize)
.map(|local| &local.identifier)
}
pub fn get_local_index(&self, identifier_text: &str, position: Span) -> Result<u8, ChunkError> {
self.locals
.iter()
.enumerate()
.rev()
.find_map(|(index, local)| {
if local.identifier.as_str() == identifier_text {
Some(index as u8)
} else {
None
}
})
.ok_or(ChunkError::IdentifierNotFound {
identifier: Identifier::new(identifier_text),
position,
})
}
pub fn declare_local(
&mut self,
identifier: Identifier,
r#type: Option<Type>,
is_mutable: bool,
register_index: u8,
position: Span,
) -> Result<u8, ChunkError> {
log::debug!("Declare local {identifier}");
let starting_length = self.locals.len();
if starting_length + 1 > (u8::MAX as usize) {
Err(ChunkError::LocalOverflow { position })
} else {
self.locals.push(Local::new(
identifier,
r#type,
is_mutable,
self.scope_depth,
register_index,
));
Ok(starting_length as u8)
}
}
pub fn define_local(
&mut self,
local_index: u8,
register_index: u8,
position: Span,
) -> Result<(), ChunkError> {
let local = self.locals.get_mut(local_index as usize).ok_or_else(|| {
ChunkError::LocalIndexOutOfBounds {
index: local_index as usize,
position,
}
})?;
log::debug!("Define local {}", local.identifier);
local.register_index = register_index;
Ok(())
pub fn get_identifier(&self, local_index: u8) -> Option<String> {
self.locals.get(local_index as usize).and_then(|local| {
self.constants
.get(local.identifier_index as usize)
.map(|value| value.to_string())
})
}
pub fn begin_scope(&mut self) {
@ -257,7 +160,7 @@ impl PartialEq for Chunk {
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Local {
pub identifier: Identifier,
pub identifier_index: u8,
pub r#type: Option<Type>,
pub is_mutable: bool,
pub depth: usize,
@ -266,14 +169,14 @@ pub struct Local {
impl Local {
pub fn new(
identifier: Identifier,
identifier_index: u8,
r#type: Option<Type>,
mutable: bool,
depth: usize,
register_index: u8,
) -> Self {
Self {
identifier,
identifier_index,
r#type,
is_mutable: mutable,
depth,
@ -524,7 +427,7 @@ impl<'a> ChunkDisassembler<'a> {
for (
index,
Local {
identifier,
identifier_index,
r#type,
depth,
register_index,
@ -532,7 +435,12 @@ impl<'a> ChunkDisassembler<'a> {
},
) in self.chunk.locals.iter().enumerate()
{
let identifier_display = identifier.as_str();
let identifier_display = self
.chunk
.constants
.get(*identifier_index as usize)
.map(|value| value.to_string())
.unwrap_or_else(|| "unknown".to_string());
let type_display = r#type
.as_ref()
.map(|r#type| r#type.to_string())
@ -577,76 +485,3 @@ impl<'a> ChunkDisassembler<'a> {
disassembly
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ChunkError {
InstructionIndexOfBounds {
offset: usize,
position: Span,
},
ConstantOverflow {
position: Span,
},
ConstantIndexOutOfBounds {
index: usize,
position: Span,
},
LocalIndexOutOfBounds {
index: usize,
position: Span,
},
LocalOverflow {
position: Span,
},
IdentifierNotFound {
identifier: Identifier,
position: Span,
},
}
impl AnnotatedError for ChunkError {
fn title() -> &'static str {
"Chunk Error"
}
fn description(&self) -> &'static str {
match self {
ChunkError::InstructionIndexOfBounds { .. } => "Instruction index out of bounds",
ChunkError::ConstantOverflow { .. } => "Constant overflow",
ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
ChunkError::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
ChunkError::LocalOverflow { .. } => "Local overflow",
ChunkError::IdentifierNotFound { .. } => "Identifier not found",
}
}
fn details(&self) -> Option<String> {
match self {
ChunkError::InstructionIndexOfBounds { offset, .. } => {
Some(format!("Instruction index: {}", offset))
}
ChunkError::ConstantIndexOutOfBounds { index, .. } => {
Some(format!("Constant index: {}", index))
}
ChunkError::LocalIndexOutOfBounds { index, .. } => {
Some(format!("Local index: {}", index))
}
ChunkError::IdentifierNotFound { identifier, .. } => {
Some(format!("Identifier: {}", identifier))
}
ChunkError::LocalOverflow { .. } => None,
ChunkError::ConstantOverflow { .. } => None,
}
}
fn position(&self) -> Span {
match self {
ChunkError::InstructionIndexOfBounds { position, .. } => *position,
ChunkError::ConstantIndexOutOfBounds { position, .. } => *position,
ChunkError::IdentifierNotFound { position, .. } => *position,
ChunkError::LocalIndexOutOfBounds { position, .. } => *position,
ChunkError::LocalOverflow { position, .. } => *position,
ChunkError::ConstantOverflow { position, .. } => *position,
}
}
}

View File

@ -1,115 +0,0 @@
//! Key used to identify a value or type.
//!
//! Identifiers are used to uniquely identify values and types in Dust programs.
//!
//! # Examples
//! ```
//! # use dust_lang::Identifier;
//! let foo = Identifier::new("foo");
//! let also_foo = foo.clone();
//! let another_foo = also_foo.clone();
//!
//! assert_eq!(foo.strong_count(), 3); // One for each of the above.
//! ```
use std::{
fmt::{self, Display, Formatter},
sync::Arc,
};
use serde::{de::Visitor, Deserialize, Serialize};
/// Key used to identify a value or type.
///
/// See the [module-level documentation](index.html) for more information.
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct Identifier(Arc<String>);
impl Identifier {
pub fn new<T: ToString>(text: T) -> Self {
let string = text.to_string();
Identifier(Arc::new(string.clone()))
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
pub fn strong_count(&self) -> usize {
Arc::strong_count(&self.0)
}
}
impl From<String> for Identifier {
fn from(string: String) -> Self {
Identifier::new(string)
}
}
impl From<&str> for Identifier {
fn from(slice: &str) -> Self {
Identifier::new(slice)
}
}
impl Display for Identifier {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Serialize for Identifier {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.0.as_str())
}
}
impl<'de> Deserialize<'de> for Identifier {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_identifier(IdentifierVisitor)
}
}
struct IdentifierVisitor;
impl<'de> Visitor<'de> for IdentifierVisitor {
type Value = Identifier;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a UTF-8 string")
}
fn visit_char<E>(self, v: char) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
self.visit_str(v.encode_utf8(&mut [0u8; 4]))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Identifier::new(v))
}
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
self.visit_str(v)
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
self.visit_str(&v)
}
}

View File

@ -1,7 +1,6 @@
mod chunk;
mod dust_error;
mod formatter;
mod identifier;
mod instruction;
mod lexer;
mod native_function;
@ -12,10 +11,9 @@ mod r#type;
mod value;
mod vm;
pub use chunk::{Chunk, ChunkDisassembler, ChunkError, Local};
pub use chunk::{Chunk, ChunkDisassembler, Local};
pub use dust_error::{AnnotatedError, DustError};
pub use formatter::{format, Formatter};
pub use identifier::Identifier;
pub use instruction::Instruction;
pub use lexer::{lex, LexError, Lexer};
pub use native_function::{NativeFunction, NativeFunctionError};

View File

@ -174,7 +174,7 @@ impl NativeFunction {
message.push(' ');
}
let argument = vm.get(argument_index, position)?;
let argument = vm.get_register(argument_index, position)?;
message.push_str(&argument.to_string());
}
@ -197,7 +197,7 @@ impl NativeFunction {
let mut string = String::new();
for argument_index in 0..argument_count {
let argument = vm.get(argument_index, position)?;
let argument = vm.get_register(argument_index, position)?;
string.push_str(&argument.to_string());
}
@ -238,7 +238,7 @@ impl NativeFunction {
stdout.write(b" ").map_err(map_err)?;
}
let argument_string = vm.get(argument_index, position)?.to_string();
let argument_string = vm.get_register(argument_index, position)?.to_string();
stdout
.write_all(argument_string.as_bytes())
@ -264,7 +264,7 @@ impl NativeFunction {
stdout.write(b" ").map_err(map_err)?;
}
let argument_string = vm.get(argument_index, position)?.to_string();
let argument_string = vm.get_register(argument_index, position)?.to_string();
stdout
.write_all(argument_string.as_bytes())

View File

@ -9,8 +9,8 @@ use colored::Colorize;
use serde::{Deserialize, Serialize};
use crate::{
AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Identifier, Instruction, LexError,
Lexer, NativeFunction, Operation, Span, Token, TokenKind, TokenOwned, Type, Value,
AnnotatedError, Chunk, DustError, FunctionType, Instruction, LexError, Lexer, Local,
NativeFunction, Operation, Span, Token, TokenKind, TokenOwned, Type, Value,
};
pub fn parse(source: &str) -> Result<Chunk, DustError> {
@ -107,6 +107,67 @@ impl<'src> Parser<'src> {
Ok(())
}
fn get_local(&self, index: u8) -> Result<&Local, ParseError> {
let index = index as usize;
self.chunk
.locals()
.get(index)
.ok_or(ParseError::LocalIndexOutOfBounds {
index,
position: self.current_position,
})
}
fn get_local_index(&self, identifier_text: &str) -> Result<u8, ParseError> {
self.chunk
.locals()
.iter()
.enumerate()
.rev()
.find_map(|(index, local)| {
let identifier = self
.chunk
.constants()
.get(local.identifier_index as usize)?
.as_string()?;
if identifier == identifier_text {
Some(index as u8)
} else {
None
}
})
.ok_or(ParseError::UndeclaredVariable {
identifier: identifier_text.to_string(),
position: self.current_position,
})
}
pub fn declare_local(
&mut self,
identifier: &str,
r#type: Option<Type>,
is_mutable: bool,
register_index: u8,
) -> (u8, u8) {
log::debug!("Declare local {identifier}");
let scope_depth = self.chunk.scope_depth();
let identifier = Value::string(identifier);
let identifier_index = self.chunk.push_or_get_constant(identifier);
self.chunk.locals_mut().push(Local::new(
identifier_index,
r#type,
is_mutable,
scope_depth,
register_index,
));
(self.chunk.locals().len() as u8 - 1, identifier_index)
}
fn allow(&mut self, allowed: Token) -> Result<bool, ParseError> {
if self.current_token == allowed {
self.advance()?;
@ -138,7 +199,7 @@ impl<'src> Parser<'src> {
self.current_statement_length += 1;
self.chunk.push_instruction(instruction, position);
self.chunk.instructions_mut().push((instruction, position));
}
fn optimize_statement(&mut self) {
@ -236,7 +297,7 @@ impl<'src> Parser<'src> {
}
fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), ParseError> {
let constant_index = self.chunk.push_or_get_constant(value, position)?;
let constant_index = self.chunk.push_or_get_constant(value);
let register = self.next_register();
self.emit_instruction(
@ -476,7 +537,7 @@ impl<'src> Parser<'src> {
let argument = match instruction.operation() {
Operation::GetLocal => {
let local_index = instruction.b();
let local = self.chunk.get_local(local_index, self.current_position)?;
let local = self.get_local(local_index)?;
is_mutable_local = local.is_mutable;
local.register_index
@ -754,25 +815,17 @@ impl<'src> Parser<'src> {
}
fn parse_variable(&mut self, allowed: Allowed) -> Result<(), ParseError> {
let token = self.current_token;
let start_position = self.current_position;
let local_index = if let Token::Identifier(text) = token {
if let Ok(local_index) = self.chunk.get_local_index(text, start_position) {
let local_index = if let Token::Identifier(text) = self.current_token {
if let Ok(local_index) = self.get_local_index(text) {
local_index
} else if let Some(name) = self.chunk.name() {
if name.as_str() == text {
let register = self.next_register();
self.emit_instruction(Instruction::load_self(register), start_position);
self.chunk.declare_local(
Identifier::new(text),
None,
false,
register,
start_position,
)?;
self.declare_local(text, None, false, register);
self.current_is_expression = true;
@ -783,7 +836,7 @@ impl<'src> Parser<'src> {
self.parse_native_call(allowed)
} else {
Err(ParseError::UndeclaredVariable {
identifier: Identifier::new(text),
identifier: text.to_string(),
position: start_position,
})
};
@ -792,7 +845,7 @@ impl<'src> Parser<'src> {
self.parse_native_call(allowed)
} else {
Err(ParseError::UndeclaredVariable {
identifier: Identifier::new(text),
identifier: text.to_string(),
position: start_position,
})
};
@ -807,12 +860,9 @@ impl<'src> Parser<'src> {
self.advance()?;
let is_mutable = self
.chunk
.get_local(local_index, start_position)?
.is_mutable;
if self.allow(Token::Equal)? {
let is_mutable = self.get_local(local_index)?.is_mutable;
if !allowed.assignment {
return Err(ParseError::InvalidAssignmentTarget {
found: self.current_token.to_owned(),
@ -822,7 +872,7 @@ impl<'src> Parser<'src> {
if !is_mutable {
return Err(ParseError::CannotMutateImmutableVariable {
identifier: self.chunk.get_identifier(local_index).cloned().unwrap(),
identifier: self.chunk.get_identifier(local_index).unwrap(),
position: start_position,
});
}
@ -838,10 +888,7 @@ impl<'src> Parser<'src> {
})?;
if previous_instruction.operation().is_math() {
let register_index = self
.chunk
.get_local(local_index, start_position)?
.register_index;
let register_index = self.get_local(local_index)?.register_index;
log::trace!("Condensing SET_LOCAL to binary math expression");
@ -1019,18 +1066,22 @@ impl<'src> Parser<'src> {
if let Some(skippable) = self.get_last_jumpable_mut() {
skippable.set_c_to_boolean(true);
} else {
self.chunk.insert_instruction(
self.chunk.instructions_mut().insert(
else_start,
Instruction::jump(jump_distance, true),
self.current_position,
)?;
(
Instruction::jump(jump_distance, true),
self.current_position,
),
);
}
} else {
self.chunk.insert_instruction(
self.chunk.instructions_mut().insert(
else_start,
Instruction::jump(jump_distance, true),
self.current_position,
)?;
(
Instruction::jump(jump_distance, true),
self.current_position,
),
);
}
} else {
self.current_is_expression = false;
@ -1054,11 +1105,13 @@ impl<'src> Parser<'src> {
if else_end - if_block_end > 1 {
let jump_distance = (else_end - if_block_end) as u8;
self.chunk.insert_instruction(
self.chunk.instructions_mut().insert(
if_block_end,
Instruction::jump(jump_distance, true),
self.current_position,
)?;
(
Instruction::jump(jump_distance, true),
self.current_position,
),
);
}
} else {
return Err(ParseError::ExpectedTokenMultiple {
@ -1096,11 +1149,13 @@ impl<'src> Parser<'src> {
let block_end = self.chunk.len() as u8;
self.chunk.insert_instruction(
self.chunk.instructions_mut().insert(
block_start,
Instruction::jump(block_end - block_start as u8 + 1, true),
self.current_position,
)?;
(
Instruction::jump(block_end - block_start as u8 + 1, true),
self.current_position,
),
);
let jump_back_distance = block_end - expression_start + 1;
let jump_back = Instruction::jump(jump_back_distance, false);
@ -1267,7 +1322,7 @@ impl<'src> Parser<'src> {
let identifier = if let Token::Identifier(text) = self.current_token {
self.advance()?;
Identifier::new(text)
text
} else {
return Err(ParseError::ExpectedToken {
expected: TokenKind::Identifier,
@ -1289,9 +1344,7 @@ impl<'src> Parser<'src> {
self.expect(Token::Equal)?;
self.parse_expression()?;
let local_index = self
.chunk
.declare_local(identifier, r#type, is_mutable, register, position)?;
let (local_index, _) = self.declare_local(identifier, r#type, is_mutable, register);
let register = self.next_register().saturating_sub(1);
self.emit_instruction(
@ -1310,27 +1363,25 @@ impl<'src> Parser<'src> {
let mut function_parser = Parser::new(self.lexer)?;
let identifier = if let Token::Identifier(text) = function_parser.current_token {
let position = function_parser.current_position;
let identifier = Identifier::new(text);
function_parser.advance()?;
function_parser.chunk.set_name(identifier.clone());
function_parser.chunk.set_name(text.to_string());
Some((identifier, position))
Some((text, position))
} else {
None
};
function_parser.expect(Token::LeftParenthesis)?;
let mut value_parameters: Option<Vec<(Identifier, Type)>> = None;
let mut value_parameters: Option<Vec<(u8, Type)>> = None;
while function_parser.current_token != Token::RightParenthesis {
let start = function_parser.current_position.0;
let is_mutable = function_parser.allow(Token::Mut)?;
let parameter = if let Token::Identifier(text) = function_parser.current_token {
function_parser.advance()?;
Identifier::new(text)
text
} else {
return Err(ParseError::ExpectedToken {
expected: TokenKind::Identifier,
@ -1348,26 +1399,22 @@ impl<'src> Parser<'src> {
function_parser.advance()?;
let end = function_parser.current_position.1;
if let Some(value_parameters) = value_parameters.as_mut() {
value_parameters.push((parameter.clone(), r#type.clone()));
} else {
value_parameters = Some(vec![(parameter.clone(), r#type.clone())]);
};
let register = value_parameters
.as_ref()
.map(|values| values.len() as u8 - 1)
.unwrap_or(0);
function_parser.chunk.declare_local(
let (_, identifier_index) = function_parser.declare_local(
parameter,
Some(r#type),
Some(r#type.clone()),
is_mutable,
register,
Span(start, end),
)?;
);
if let Some(value_parameters) = value_parameters.as_mut() {
value_parameters.push((identifier_index, r#type));
} else {
value_parameters = Some(vec![(identifier_index, r#type)]);
};
function_parser.minimum_register += 1;
@ -1409,13 +1456,12 @@ impl<'src> Parser<'src> {
if let Some((identifier, identifier_position)) = identifier {
let register = self.next_register();
let local_index = self.chunk.declare_local(
let (local_index, _) = self.declare_local(
identifier,
Some(Type::Function(function_type)),
false,
register,
Span(function_start, function_end),
)?;
);
self.emit_constant(function, Span(function_start, function_end))?;
self.emit_instruction(
@ -1867,17 +1913,7 @@ impl From<&Token<'_>> for ParseRule<'_> {
#[derive(Debug, PartialEq)]
pub enum ParseError {
CannotChainComparison {
position: Span,
},
CannotMutateImmutableVariable {
identifier: Identifier,
position: Span,
},
ExpectedExpression {
found: TokenOwned,
position: Span,
},
// Token errors
ExpectedToken {
expected: TokenKind,
found: TokenOwned,
@ -1888,19 +1924,42 @@ pub enum ParseError {
found: TokenOwned,
position: Span,
},
// Expression errors
CannotChainComparison {
position: Span,
},
ExpectedExpression {
found: TokenOwned,
position: Span,
},
// Variable errors
CannotMutateImmutableVariable {
identifier: String,
position: Span,
},
ExpectedMutableVariable {
found: TokenOwned,
position: Span,
},
UndeclaredVariable {
identifier: String,
position: Span,
},
// Statement errors
InvalidAssignmentTarget {
found: TokenOwned,
position: Span,
},
UndeclaredVariable {
identifier: Identifier,
UnexpectedReturn {
position: Span,
},
UnexpectedReturn {
// Chunk errors
LocalIndexOutOfBounds {
index: usize,
position: Span,
},
RegisterOverflow {
@ -1911,7 +1970,6 @@ pub enum ParseError {
},
// Wrappers around foreign errors
Chunk(ChunkError),
Lex(LexError),
ParseFloatError {
error: ParseFloatError,
@ -1923,12 +1981,6 @@ pub enum ParseError {
},
}
impl From<ChunkError> for ParseError {
fn from(error: ChunkError) -> Self {
Self::Chunk(error)
}
}
impl AnnotatedError for ParseError {
fn title() -> &'static str {
"Parse Error"
@ -1939,18 +1991,18 @@ impl AnnotatedError for ParseError {
Self::CannotChainComparison { .. } => "Cannot chain comparison",
Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable",
Self::ExpectedExpression { .. } => "Expected an expression",
Self::ExpectedMutableVariable { .. } => "Expected a mutable variable",
Self::ExpectedToken { .. } => "Expected a specific token",
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
Self::ExpectedMutableVariable { .. } => "Expected a mutable variable",
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
Self::UndeclaredVariable { .. } => "Undeclared variable",
Self::UnexpectedReturn { .. } => "Unexpected return",
Self::RegisterOverflow { .. } => "Register overflow",
Self::RegisterUnderflow { .. } => "Register underflow",
Self::Lex(error) => error.description(),
Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
Self::ParseFloatError { .. } => "Failed to parse float",
Self::ParseIntError { .. } => "Failed to parse integer",
Self::Chunk(error) => error.description(),
Self::Lex(error) => error.description(),
Self::RegisterOverflow { .. } => "Register overflow",
Self::RegisterUnderflow { .. } => "Register underflow",
Self::UndeclaredVariable { .. } => "Undeclared variable",
Self::UnexpectedReturn { .. } => "Unexpected return",
}
}
@ -1993,16 +2045,18 @@ impl AnnotatedError for ParseError {
Self::InvalidAssignmentTarget { found, .. } => {
Some(format!("Invalid assignment target, found {found}"))
}
Self::Lex(error) => error.details(),
Self::LocalIndexOutOfBounds { index, .. } => {
Some(format!("Local index {index} out of bounds"))
}
Self::ParseFloatError { error, .. } => Some(error.to_string()),
Self::ParseIntError { error, .. } => Some(error.to_string()),
Self::RegisterOverflow { .. } => None,
Self::RegisterUnderflow { .. } => None,
Self::UndeclaredVariable { identifier, .. } => {
Some(format!("Undeclared variable {identifier}"))
}
Self::UnexpectedReturn { .. } => None,
Self::RegisterOverflow { .. } => None,
Self::RegisterUnderflow { .. } => None,
Self::ParseFloatError { error, .. } => Some(error.to_string()),
Self::ParseIntError { error, .. } => Some(error.to_string()),
Self::Chunk(error) => error.details(),
Self::Lex(error) => error.details(),
}
}
@ -2011,18 +2065,18 @@ impl AnnotatedError for ParseError {
Self::CannotChainComparison { position } => *position,
Self::CannotMutateImmutableVariable { position, .. } => *position,
Self::ExpectedExpression { position, .. } => *position,
Self::ExpectedMutableVariable { position, .. } => *position,
Self::ExpectedToken { position, .. } => *position,
Self::ExpectedTokenMultiple { position, .. } => *position,
Self::ExpectedMutableVariable { position, .. } => *position,
Self::InvalidAssignmentTarget { position, .. } => *position,
Self::UndeclaredVariable { position, .. } => *position,
Self::UnexpectedReturn { position } => *position,
Self::RegisterOverflow { position } => *position,
Self::RegisterUnderflow { position } => *position,
Self::Lex(error) => error.position(),
Self::LocalIndexOutOfBounds { position, .. } => *position,
Self::ParseFloatError { position, .. } => *position,
Self::ParseIntError { position, .. } => *position,
Self::Chunk(error) => error.position(),
Self::Lex(error) => error.position(),
Self::RegisterOverflow { position } => *position,
Self::RegisterUnderflow { position } => *position,
Self::UndeclaredVariable { position, .. } => *position,
Self::UnexpectedReturn { position } => *position,
}
}
}

View File

@ -17,8 +17,6 @@ use std::{
use serde::{Deserialize, Serialize};
use crate::Identifier;
/// Description of a kind of value.
///
/// See the [module documentation](index.html) for more information.
@ -32,7 +30,7 @@ pub enum Type {
Float,
Function(FunctionType),
Generic {
identifier: Identifier,
identifier_index: u8,
concrete_type: Option<Box<Type>>,
},
Integer,
@ -45,7 +43,7 @@ pub enum Type {
item_type: Box<Type>,
},
Map {
pairs: HashMap<Identifier, Type>,
pairs: HashMap<u8, Type>,
},
Number,
Range {
@ -230,47 +228,6 @@ impl Type {
expected: self.clone(),
})
}
pub fn has_field(&self, field: &Identifier) -> bool {
match field.as_str() {
"to_string" => true,
"length" => {
matches!(
self,
Type::List { .. }
| Type::ListOf { .. }
| Type::ListEmpty
| Type::Map { .. }
| Type::String { .. }
)
}
"is_even" | "is_odd" => matches!(self, Type::Integer | Type::Float),
_ => match self {
Type::Struct(StructType::Fields { fields, .. }) => fields.contains_key(field),
Type::Map { pairs } => pairs.contains_key(field),
_ => false,
},
}
}
pub fn get_field_type(&self, field: &Identifier) -> Option<Type> {
match field.as_str() {
"length" => match self {
Type::List { .. } => Some(Type::Integer),
Type::ListOf { .. } => Some(Type::Integer),
Type::ListEmpty => Some(Type::Integer),
Type::Map { .. } => Some(Type::Integer),
Type::String { .. } => Some(Type::Integer),
_ => None,
},
"is_even" | "is_odd" => Some(Type::Boolean),
_ => match self {
Type::Struct(StructType::Fields { fields, .. }) => fields.get(field).cloned(),
Type::Map { pairs } => pairs.get(field).cloned(),
_ => None,
},
}
}
}
impl Display for Type {
@ -285,7 +242,10 @@ impl Display for Type {
Type::Function(function_type) => write!(f, "{function_type}"),
Type::Generic { concrete_type, .. } => {
match concrete_type.clone().map(|r#box| *r#box) {
Some(Type::Generic { identifier, .. }) => write!(f, "{identifier}"),
Some(Type::Generic {
identifier_index: identifier,
..
}) => write!(f, "{identifier}"),
Some(concrete_type) => write!(f, "implied to be {concrete_type}"),
None => write!(f, "unknown"),
}
@ -416,8 +376,8 @@ impl Ord for Type {
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct FunctionType {
pub type_parameters: Option<Vec<Identifier>>,
pub value_parameters: Option<Vec<(Identifier, Type)>>,
pub type_parameters: Option<Vec<u8>>,
pub value_parameters: Option<Vec<(u8, Type)>>,
pub return_type: Option<Box<Type>>,
}
@ -463,25 +423,17 @@ impl Display for FunctionType {
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum StructType {
Unit {
name: Identifier,
},
Tuple {
name: Identifier,
fields: Vec<Type>,
},
Fields {
name: Identifier,
fields: HashMap<Identifier, Type>,
},
Unit { name: u8 },
Tuple { name: u8, fields: Vec<Type> },
Fields { name: u8, fields: HashMap<u8, Type> },
}
impl StructType {
pub fn name(&self) -> &Identifier {
pub fn name(&self) -> u8 {
match self {
StructType::Unit { name } => name,
StructType::Tuple { name, .. } => name,
StructType::Fields { name, .. } => name,
StructType::Unit { name } => *name,
StructType::Tuple { name, .. } => *name,
StructType::Fields { name, .. } => *name,
}
}
}
@ -583,7 +535,7 @@ impl Ord for StructType {
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct EnumType {
pub name: Identifier,
pub name: u8,
pub variants: Vec<StructType>,
}

View File

@ -83,6 +83,14 @@ impl Value {
Value::Primitive(Primitive::String(to_string.to_string()))
}
pub fn as_string(&self) -> Option<&String> {
if let Value::Primitive(Primitive::String(string)) = self {
Some(string)
} else {
None
}
}
pub fn is_function(&self) -> bool {
matches!(self, Value::Function(_))
}
@ -894,7 +902,7 @@ impl Object {
display.push_str(", ");
}
let value_display = match vm.get(register, position) {
let value_display = match vm.get_register(register, position) {
Ok(value) => value.display(vm, position)?,
Err(error) => {
return Err(ValueError::CannotDisplay {

View File

@ -1,9 +1,8 @@
use std::{cmp::Ordering, mem::replace};
use crate::{
parse, value::Primitive, AnnotatedError, Chunk, ChunkError, DustError, FunctionType,
Identifier, Instruction, NativeFunction, NativeFunctionError, Operation, Span, Type, Value,
ValueError,
parse, value::Primitive, AnnotatedError, Chunk, DustError, FunctionType, Instruction, Local,
NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError,
};
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
@ -42,14 +41,24 @@ impl Vm {
position: Span,
) -> Result<(&Value, &Value), VmError> {
let left = if instruction.b_is_constant() {
vm.chunk.get_constant(instruction.b(), position)?
vm.chunk.get_constant(instruction.b()).ok_or_else(|| {
VmError::ConstantIndexOutOfBounds {
index: instruction.b() as usize,
position,
}
})?
} else {
vm.get(instruction.b(), position)?
vm.get_register(instruction.b(), position)?
};
let right = if instruction.c_is_constant() {
vm.chunk.get_constant(instruction.c(), position)?
vm.chunk.get_constant(instruction.c()).ok_or_else(|| {
VmError::ConstantIndexOutOfBounds {
index: instruction.c() as usize,
position,
}
})?
} else {
vm.get(instruction.c(), position)?
vm.get_register(instruction.c(), position)?
};
Ok((left, right))
@ -89,7 +98,7 @@ impl Vm {
let jump = instruction.c_as_boolean();
let value = Value::boolean(boolean);
self.set(to_register, value, position)?;
self.set_register(to_register, value, position)?;
if jump {
self.ip += 1;
@ -115,11 +124,11 @@ impl Vm {
let item_type = if is_empty {
Type::Any
} else {
self.get(first_register, position)?.r#type()
self.get_register(first_register, position)?.r#type()
};
let value = Value::list(first_register, last_register, item_type);
self.set(to_register, value, position)?;
self.set_register(to_register, value, position)?;
}
Operation::LoadSelf => {
let to_register = instruction.a();
@ -132,34 +141,37 @@ impl Vm {
},
);
self.set(to_register, value, position)?;
self.set_register(to_register, value, position)?;
}
Operation::DefineLocal => {
let from_register = instruction.a();
let to_local = instruction.b();
self.chunk.define_local(to_local, from_register, position)?;
self.define_local(to_local, from_register, position)?;
}
Operation::GetLocal => {
let to_register = instruction.a();
let local_index = instruction.b();
let local = self.chunk.get_local(local_index, position)?;
let local = self.get_local(local_index, position)?;
self.set_pointer(to_register, local.register_index, position)?;
}
Operation::SetLocal => {
let register = instruction.a();
let local_index = instruction.b();
let local = self.chunk.get_local(local_index, position)?;
let local = self.get_local(local_index, position)?;
if !local.is_mutable {
return Err(VmError::CannotMutateImmutableLocal {
identifier: local.identifier.clone(),
identifier: self
.chunk
.get_identifier(local.identifier_index)
.unwrap_or_else(|| "unknown".to_string()),
position,
});
}
self.chunk.define_local(local_index, register, position)?;
self.define_local(local_index, register, position)?;
}
Operation::Add => {
let (left, right) = get_arguments(&mut self, instruction, position)?;
@ -167,7 +179,7 @@ impl Vm {
.add(right)
.map_err(|error| VmError::Value { error, position })?;
self.set(instruction.a(), sum, position)?;
self.set_register(instruction.a(), sum, position)?;
}
Operation::Subtract => {
let (left, right) = get_arguments(&mut self, instruction, position)?;
@ -175,7 +187,7 @@ impl Vm {
.subtract(right)
.map_err(|error| VmError::Value { error, position })?;
self.set(instruction.a(), difference, position)?;
self.set_register(instruction.a(), difference, position)?;
}
Operation::Multiply => {
let (left, right) = get_arguments(&mut self, instruction, position)?;
@ -183,7 +195,7 @@ impl Vm {
.multiply(right)
.map_err(|error| VmError::Value { error, position })?;
self.set(instruction.a(), product, position)?;
self.set_register(instruction.a(), product, position)?;
}
Operation::Divide => {
let (left, right) = get_arguments(&mut self, instruction, position)?;
@ -191,7 +203,7 @@ impl Vm {
.divide(right)
.map_err(|error| VmError::Value { error, position })?;
self.set(instruction.a(), quotient, position)?;
self.set_register(instruction.a(), quotient, position)?;
}
Operation::Modulo => {
let (left, right) = get_arguments(&mut self, instruction, position)?;
@ -199,12 +211,12 @@ impl Vm {
.modulo(right)
.map_err(|error| VmError::Value { error, position })?;
self.set(instruction.a(), remainder, position)?;
self.set_register(instruction.a(), remainder, position)?;
}
Operation::Test => {
let register = instruction.a();
let test_value = instruction.c_as_boolean();
let value = self.get(register, position)?;
let value = self.get_register(register, position)?;
let boolean = if let Value::Primitive(Primitive::Boolean(boolean)) = value {
*boolean
} else {
@ -221,7 +233,7 @@ impl Vm {
Operation::TestSet => todo!(),
Operation::Equal => {
debug_assert_eq!(
self.chunk.get_instruction(self.ip, position)?.0.operation(),
self.get_instruction(self.ip, position)?.0.operation(),
Operation::Jump
);
@ -243,7 +255,7 @@ impl Vm {
if boolean == compare_to {
self.ip += 1;
} else {
let (jump, _) = *self.chunk.get_instruction(self.ip, position)?;
let (jump, _) = self.get_instruction(self.ip, position)?;
let jump_distance = jump.a();
let is_positive = jump.b_as_boolean();
let new_ip = if is_positive {
@ -257,7 +269,7 @@ impl Vm {
}
Operation::Less => {
debug_assert_eq!(
self.chunk.get_instruction(self.ip, position)?.0.operation(),
self.get_instruction(self.ip, position)?.0.operation(),
Operation::Jump
);
@ -279,7 +291,7 @@ impl Vm {
if boolean == compare_to {
self.ip += 1;
} else {
let jump = self.chunk.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 {
@ -293,7 +305,7 @@ impl Vm {
}
Operation::LessEqual => {
debug_assert_eq!(
self.chunk.get_instruction(self.ip, position)?.0.operation(),
self.get_instruction(self.ip, position)?.0.operation(),
Operation::Jump
);
@ -316,7 +328,7 @@ impl Vm {
if boolean == compare_to {
self.ip += 1;
} else {
let jump = self.chunk.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 {
@ -330,27 +342,37 @@ impl Vm {
}
Operation::Negate => {
let value = if instruction.b_is_constant() {
self.chunk.get_constant(instruction.b(), position)?
self.chunk.get_constant(instruction.b()).ok_or_else(|| {
VmError::ConstantIndexOutOfBounds {
index: instruction.b() as usize,
position,
}
})?
} else {
self.get(instruction.b(), position)?
self.get_register(instruction.b(), position)?
};
let negated = value
.negate()
.map_err(|error| VmError::Value { error, position })?;
self.set(instruction.a(), negated, position)?;
self.set_register(instruction.a(), negated, position)?;
}
Operation::Not => {
let value = if instruction.b_is_constant() {
self.chunk.get_constant(instruction.b(), position)?
self.chunk.get_constant(instruction.b()).ok_or_else(|| {
VmError::ConstantIndexOutOfBounds {
index: instruction.b() as usize,
position,
}
})?
} else {
self.get(instruction.b(), position)?
self.get_register(instruction.b(), position)?
};
let not = value
.not()
.map_err(|error| VmError::Value { error, position })?;
self.set(instruction.a(), not, position)?;
self.set_register(instruction.a(), not, position)?;
}
Operation::Jump => {
let jump_distance = instruction.b();
@ -366,7 +388,7 @@ impl Vm {
let to_register = instruction.a();
let function_register = instruction.b();
let argument_count = instruction.c();
let value = self.get(function_register, position)?.clone();
let value = self.get_register(function_register, position)?.clone();
let function = if let Value::Function(function) = value {
function
} else {
@ -381,20 +403,20 @@ impl Vm {
for argument_index in
first_argument_index..first_argument_index + argument_count
{
let argument = match self.get(argument_index, position) {
let argument = match self.get_register(argument_index, position) {
Ok(value) => value.clone(),
Err(VmError::EmptyRegister { .. }) => continue,
Err(error) => return Err(error),
};
let top_of_stack = function_vm.stack.len() as u8;
function_vm.set(top_of_stack, argument, position)?;
function_vm.set_register(top_of_stack, argument, position)?;
}
let return_value = function_vm.run()?;
if let Some(value) = return_value {
self.set(to_register, value, position)?;
self.set_register(to_register, value, position)?;
}
}
Operation::CallNative => {
@ -404,7 +426,7 @@ impl Vm {
if let Some(value) = return_value {
let to_register = instruction.a();
self.set(to_register, value, position)?;
self.set_register(to_register, value, position)?;
}
}
Operation::Return => {
@ -415,7 +437,7 @@ impl Vm {
}
if let Some(register) = self.last_assigned_register {
let value = self.empty(register, position)?;
let value = self.empty_register(register, position)?;
return Ok(Some(value));
} else {
@ -428,7 +450,12 @@ impl Vm {
Ok(None)
}
fn set(&mut self, to_register: u8, value: Value, position: Span) -> Result<(), VmError> {
fn set_register(
&mut self,
to_register: u8,
value: Value,
position: Span,
) -> Result<(), VmError> {
let length = self.stack.len();
self.last_assigned_register = Some(to_register);
let to_register = to_register as usize;
@ -564,7 +591,7 @@ impl Vm {
}
}
pub fn get(&self, index: u8, position: Span) -> Result<&Value, VmError> {
pub fn get_register(&self, index: u8, position: Span) -> Result<&Value, VmError> {
let index = index as usize;
let register = self
.stack
@ -573,21 +600,19 @@ impl Vm {
match register {
Register::Value(value) => Ok(value),
Register::Pointer(register_index) => {
let value = self.get(*register_index, position)?;
Ok(value)
}
Register::Constant(constant_index) => {
let value = self.chunk.get_constant(*constant_index, position)?;
Ok(value)
}
Register::Pointer(register_index) => self.get_register(*register_index, position),
Register::Constant(constant_index) => self
.chunk
.get_constant(*constant_index)
.ok_or_else(|| VmError::ConstantIndexOutOfBounds {
index: *constant_index as usize,
position,
}),
Register::Empty => Err(VmError::EmptyRegister { index, position }),
}
}
fn empty(mut self, index: u8, position: Span) -> Result<Value, VmError> {
fn empty_register(mut self, index: u8, position: Span) -> Result<Value, VmError> {
let index = index as usize;
if index >= self.stack.len() {
@ -599,7 +624,7 @@ impl Vm {
match register {
Register::Value(value) => Ok(value),
Register::Pointer(register_index) => {
let value = self.empty(register_index, position)?;
let value = self.empty_register(register_index, position)?;
Ok(value)
}
@ -617,12 +642,62 @@ impl Vm {
self.ip = self.chunk.len() - 1;
}
let current = self.chunk.get_instruction(self.ip, position)?;
let current = self.chunk.instructions().get(self.ip).ok_or_else(|| {
VmError::InstructionIndexOutOfBounds {
index: self.ip,
position,
}
})?;
self.ip += 1;
Ok(current)
}
fn define_local(
&mut self,
local_index: u8,
register_index: u8,
position: Span,
) -> Result<(), VmError> {
let local = self
.chunk
.locals_mut()
.get_mut(local_index as usize)
.ok_or_else(|| VmError::LocalIndexOutOfBounds {
index: local_index as usize,
position,
})?;
log::debug!("Define local L{}", local_index);
local.register_index = register_index;
Ok(())
}
fn get_local(&self, local_index: u8, position: Span) -> Result<&Local, VmError> {
let local_index = local_index as usize;
self.chunk
.locals()
.get(local_index)
.ok_or_else(|| VmError::LocalIndexOutOfBounds {
index: local_index,
position,
})
}
fn get_instruction(
&self,
index: usize,
position: Span,
) -> Result<&(Instruction, Span), VmError> {
self.chunk
.instructions()
.get(index)
.ok_or_else(|| VmError::InstructionIndexOutOfBounds { index, position })
}
}
#[derive(Debug, Eq, PartialEq)]
@ -636,7 +711,7 @@ enum Register {
#[derive(Debug, Clone, PartialEq)]
pub enum VmError {
CannotMutateImmutableLocal {
identifier: Identifier,
identifier: String,
position: Span,
},
EmptyRegister {
@ -651,10 +726,22 @@ pub enum VmError {
found: Value,
position: Span,
},
ConstantIndexOutOfBounds {
index: usize,
position: Span,
},
RegisterIndexOutOfBounds {
index: usize,
position: Span,
},
InstructionIndexOutOfBounds {
index: usize,
position: Span,
},
LocalIndexOutOfBounds {
index: usize,
position: Span,
},
InvalidInstruction {
instruction: Instruction,
position: Span,
@ -666,25 +753,18 @@ pub enum VmError {
position: Span,
},
UndefinedVariable {
identifier: Identifier,
identifier: String,
position: Span,
},
// Wrappers for foreign errors
NativeFunction(NativeFunctionError),
Chunk(ChunkError),
Value {
error: ValueError,
position: Span,
},
}
impl From<ChunkError> for VmError {
fn from(error: ChunkError) -> Self {
Self::Chunk(error)
}
}
impl AnnotatedError for VmError {
fn title() -> &'static str {
"Runtime Error"
@ -693,15 +773,17 @@ impl AnnotatedError for VmError {
fn description(&self) -> &'static str {
match self {
Self::CannotMutateImmutableLocal { .. } => "Cannot mutate immutable variable",
Self::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
Self::EmptyRegister { .. } => "Empty register",
Self::ExpectedBoolean { .. } => "Expected boolean",
Self::ExpectedFunction { .. } => "Expected function",
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
Self::InstructionIndexOutOfBounds { .. } => "Instruction index out of bounds",
Self::InvalidInstruction { .. } => "Invalid instruction",
Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
Self::StackOverflow { .. } => "Stack overflow",
Self::StackUnderflow { .. } => "Stack underflow",
Self::UndefinedVariable { .. } => "Undefined variable",
Self::Chunk(error) => error.description(),
Self::NativeFunction(error) => error.description(),
Self::Value { .. } => "Value error",
}
@ -709,15 +791,23 @@ impl AnnotatedError for VmError {
fn details(&self) -> Option<String> {
match self {
Self::ConstantIndexOutOfBounds { index, .. } => {
Some(format!("Constant C{index} does not exist"))
}
Self::EmptyRegister { index, .. } => Some(format!("Register {index} is empty")),
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
Self::RegisterIndexOutOfBounds { index, .. } => {
Some(format!("Register {index} does not exist"))
}
Self::InstructionIndexOutOfBounds { index, .. } => {
Some(format!("Instruction {index} does not exist"))
}
Self::LocalIndexOutOfBounds { index, .. } => {
Some(format!("Local L{index} does not exist"))
}
Self::UndefinedVariable { identifier, .. } => {
Some(format!("{identifier} is not in scope"))
}
Self::Chunk(error) => error.details(),
Self::NativeFunction(error) => error.details(),
Self::Value { error, .. } => Some(error.to_string()),
_ => None,
@ -727,15 +817,17 @@ impl AnnotatedError for VmError {
fn position(&self) -> Span {
match self {
Self::CannotMutateImmutableLocal { position, .. } => *position,
Self::ConstantIndexOutOfBounds { position, .. } => *position,
Self::EmptyRegister { position, .. } => *position,
Self::ExpectedBoolean { position, .. } => *position,
Self::ExpectedFunction { position, .. } => *position,
Self::RegisterIndexOutOfBounds { position, .. } => *position,
Self::InstructionIndexOutOfBounds { position, .. } => *position,
Self::InvalidInstruction { position, .. } => *position,
Self::LocalIndexOutOfBounds { position, .. } => *position,
Self::StackUnderflow { position } => *position,
Self::StackOverflow { position } => *position,
Self::UndefinedVariable { position, .. } => *position,
Self::Chunk(error) => error.position(),
Self::NativeFunction(error) => error.position(),
Self::Value { position, .. } => *position,
}

View File

@ -23,7 +23,7 @@ fn equality_assignment_long() {
(Instruction::r#return(true), Span(44, 44)),
],
vec![Value::integer(4)],
vec![Local::new(Identifier::new("a"), None, false, 0, 0)]
vec![Local::new(0, None, false, 0, 0)]
)),
);
@ -53,7 +53,7 @@ fn equality_assignment_short() {
(Instruction::r#return(true), Span(16, 16)),
],
vec![Value::integer(4)],
vec![Local::new(Identifier::new("a"), None, false, 0, 0)]
vec![Local::new(0, None, false, 0, 0)]
)),
);
@ -111,7 +111,7 @@ fn if_else_assigment() {
Value::integer(3),
Value::integer(42),
],
vec![Local::new(Identifier::new("a"), None, false, 0, 0)]
vec![Local::new(0, None, false, 0, 0)]
)),
);

View File

@ -41,7 +41,7 @@ fn add_assign() {
(Instruction::r#return(true), Span(24, 24))
],
vec![Value::integer(1), Value::integer(2)],
vec![Local::new(Identifier::new("a"), None, true, 0, 0)]
vec![Local::new(0, None, true, 0, 0)]
))
);
@ -82,7 +82,7 @@ fn define_local() {
(Instruction::r#return(false), Span(11, 11))
],
vec![Value::integer(42)],
vec![Local::new(Identifier::new("x"), None, false, 0, 0)]
vec![Local::new(0, None, false, 0, 0)]
)),
);
@ -133,7 +133,7 @@ fn divide_assign() {
(Instruction::r#return(true), Span(24, 24))
],
vec![Value::integer(2)],
vec![Local::new(Identifier::new("a"), None, true, 0, 0)]
vec![Local::new(0, None, true, 0, 0)]
))
);
@ -359,7 +359,7 @@ fn multiply_assign() {
(Instruction::r#return(true), Span(23, 23))
],
vec![Value::integer(2), Value::integer(3)],
vec![Local::new(Identifier::new("a"), None, true, 0, 0),]
vec![Local::new(0, None, true, 0, 0),]
))
);
@ -453,7 +453,7 @@ fn set_local() {
(Instruction::r#return(true), Span(25, 25)),
],
vec![Value::integer(41), Value::integer(42)],
vec![Local::new(Identifier::new("x"), None, true, 0, 0)]
vec![Local::new(0, None, true, 0, 0)]
)),
);
@ -504,7 +504,7 @@ fn subtract_assign() {
(Instruction::r#return(true), Span(25, 25)),
],
vec![Value::integer(42), Value::integer(2)],
vec![Local::new(Identifier::new("x"), None, true, 0, 0)]
vec![Local::new(0, None, true, 0, 0)]
)),
);
@ -532,8 +532,8 @@ fn variable_and() {
],
vec![],
vec![
Local::new(Identifier::new("a"), None, false, 0, 0),
Local::new(Identifier::new("b"), None, false, 0, 1),
Local::new(0, None, false, 0, 0),
Local::new(0, None, false, 0, 1),
]
))
);
@ -563,7 +563,7 @@ fn r#while() {
(Instruction::r#return(true), Span(42, 42)),
],
vec![Value::integer(0), Value::integer(5), Value::integer(1),],
vec![Local::new(Identifier::new("x"), None, true, 0, 0),]
vec![Local::new(0, None, true, 0, 0),]
)),
);

View File

@ -15,16 +15,13 @@ fn function() {
],
vec![],
vec![
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0),
Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1)
Local::new(0, Some(Type::Integer), false, 0, 0),
Local::new(0, Some(Type::Integer), false, 0, 1)
]
),
FunctionType {
type_parameters: None,
value_parameters: Some(vec![
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
return_type: Some(Box::new(Type::Integer)),
}
)))
@ -53,27 +50,21 @@ fn function_declaration() {
],
vec![],
vec![
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0),
Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1)
Local::new(0, Some(Type::Integer), false, 0, 0),
Local::new(0, Some(Type::Integer), false, 0, 1)
]
),
FunctionType {
type_parameters: None,
value_parameters: Some(vec![
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
return_type: Some(Box::new(Type::Integer)),
},
)],
vec![Local::new(
Identifier::new("add"),
0,
Some(Type::Function(FunctionType {
type_parameters: None,
value_parameters: Some(vec![
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
return_type: Some(Box::new(Type::Integer)),
})),
false,
@ -111,16 +102,13 @@ fn function_call() {
],
vec![],
vec![
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0),
Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1)
Local::new(0, Some(Type::Integer), false, 0, 0),
Local::new(0, Some(Type::Integer), false, 0, 1)
]
),
FunctionType {
type_parameters: None,
value_parameters: Some(vec![
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
return_type: Some(Box::new(Type::Integer)),
}
),

View File

@ -50,11 +50,11 @@ fn block_scope() {
Value::integer(2),
],
vec![
Local::new(Identifier::new("a"), None, false, 0, 0),
Local::new(Identifier::new("b"), None, false, 1, 1),
Local::new(Identifier::new("c"), None, false, 2, 2),
Local::new(Identifier::new("d"), None, false, 1, 3),
Local::new(Identifier::new("e"), None, false, 0, 4),
Local::new(0, None, false, 0, 0),
Local::new(0, None, false, 1, 1),
Local::new(0, None, false, 2, 2),
Local::new(0, None, false, 1, 3),
Local::new(0, None, false, 0, 4),
]
)),
);