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 colored::Colorize;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{AnnotatedError, Identifier, Instruction, Span, Type, Value}; use crate::{Instruction, Span, Type, Value};
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Chunk { pub struct Chunk {
name: Option<Identifier>, name: Option<String>,
instructions: Vec<(Instruction, Span)>, instructions: Vec<(Instruction, Span)>,
constants: Vec<Value>, constants: Vec<Value>,
locals: Vec<Local>, locals: Vec<Local>,
@ -18,7 +18,7 @@ pub struct Chunk {
} }
impl Chunk { impl Chunk {
pub fn new(name: Option<Identifier>) -> Self { pub fn new(name: Option<String>) -> Self {
Self { Self {
name, name,
instructions: Vec::new(), instructions: Vec::new(),
@ -29,7 +29,7 @@ impl Chunk {
} }
pub fn with_data( pub fn with_data(
name: Option<Identifier>, name: Option<String>,
instructions: Vec<(Instruction, Span)>, instructions: Vec<(Instruction, Span)>,
constants: Vec<Value>, constants: Vec<Value>,
locals: Vec<Local>, 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() self.name.as_ref()
} }
pub fn set_name(&mut self, name: Identifier) { pub fn set_name(&mut self, name: String) {
self.name = Some(name); self.name = Some(name);
} }
@ -59,7 +59,19 @@ impl Chunk {
self.instructions.is_empty() 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 &self.instructions
} }
@ -67,151 +79,42 @@ impl Chunk {
&mut self.instructions &mut self.instructions
} }
pub fn get_instruction( pub fn locals(&self) -> &Vec<Local> {
&self, &self.locals
offset: usize,
position: Span,
) -> Result<&(Instruction, Span), ChunkError> {
self.instructions
.get(offset)
.ok_or(ChunkError::InstructionIndexOfBounds { offset, position })
} }
pub fn push_instruction(&mut self, instruction: Instruction, position: Span) { pub fn locals_mut(&mut self) -> &mut Vec<Local> {
self.instructions.push((instruction, position)); &mut self.locals
} }
pub fn insert_instruction( pub fn scope_depth(&self) -> usize {
&mut self, self.scope_depth
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 take_constants(self) -> Vec<Value> { pub fn get_constant(&self, index: u8) -> Option<&Value> {
self.constants self.constants.get(index as usize)
} }
pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> { pub fn push_or_get_constant(&mut self, value: Value) -> u8 {
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> {
if let Some(index) = self if let Some(index) = self
.constants .constants
.iter() .iter()
.position(|constant| constant == &value) .position(|constant| constant == &value)
{ {
return Ok(index as u8); return index as u8;
} }
let starting_length = self.constants.len();
if starting_length + 1 > (u8::MAX as usize) {
Err(ChunkError::ConstantOverflow { position })
} else {
self.constants.push(value); self.constants.push(value);
Ok(starting_length as u8) (self.constants.len() - 1) as u8
}
} }
pub fn locals(&self) -> &[Local] { pub fn get_identifier(&self, local_index: u8) -> Option<String> {
&self.locals self.locals.get(local_index as usize).and_then(|local| {
} self.constants
.get(local.identifier_index as usize)
pub fn get_local(&self, index: u8, position: Span) -> Result<&Local, ChunkError> { .map(|value| value.to_string())
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 begin_scope(&mut self) { pub fn begin_scope(&mut self) {
@ -257,7 +160,7 @@ impl PartialEq for Chunk {
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Local { pub struct Local {
pub identifier: Identifier, pub identifier_index: u8,
pub r#type: Option<Type>, pub r#type: Option<Type>,
pub is_mutable: bool, pub is_mutable: bool,
pub depth: usize, pub depth: usize,
@ -266,14 +169,14 @@ pub struct Local {
impl Local { impl Local {
pub fn new( pub fn new(
identifier: Identifier, identifier_index: u8,
r#type: Option<Type>, r#type: Option<Type>,
mutable: bool, mutable: bool,
depth: usize, depth: usize,
register_index: u8, register_index: u8,
) -> Self { ) -> Self {
Self { Self {
identifier, identifier_index,
r#type, r#type,
is_mutable: mutable, is_mutable: mutable,
depth, depth,
@ -524,7 +427,7 @@ impl<'a> ChunkDisassembler<'a> {
for ( for (
index, index,
Local { Local {
identifier, identifier_index,
r#type, r#type,
depth, depth,
register_index, register_index,
@ -532,7 +435,12 @@ impl<'a> ChunkDisassembler<'a> {
}, },
) in self.chunk.locals.iter().enumerate() ) 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 let type_display = r#type
.as_ref() .as_ref()
.map(|r#type| r#type.to_string()) .map(|r#type| r#type.to_string())
@ -577,76 +485,3 @@ impl<'a> ChunkDisassembler<'a> {
disassembly 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 chunk;
mod dust_error; mod dust_error;
mod formatter; mod formatter;
mod identifier;
mod instruction; mod instruction;
mod lexer; mod lexer;
mod native_function; mod native_function;
@ -12,10 +11,9 @@ mod r#type;
mod value; mod value;
mod vm; mod vm;
pub use chunk::{Chunk, ChunkDisassembler, ChunkError, Local}; pub use chunk::{Chunk, ChunkDisassembler, Local};
pub use dust_error::{AnnotatedError, DustError}; pub use dust_error::{AnnotatedError, DustError};
pub use formatter::{format, Formatter}; pub use formatter::{format, Formatter};
pub use identifier::Identifier;
pub use instruction::Instruction; pub use instruction::Instruction;
pub use lexer::{lex, LexError, Lexer}; pub use lexer::{lex, LexError, Lexer};
pub use native_function::{NativeFunction, NativeFunctionError}; pub use native_function::{NativeFunction, NativeFunctionError};

View File

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

View File

@ -9,8 +9,8 @@ use colored::Colorize;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Identifier, Instruction, LexError, AnnotatedError, Chunk, DustError, FunctionType, Instruction, LexError, Lexer, Local,
Lexer, NativeFunction, Operation, Span, Token, TokenKind, TokenOwned, Type, Value, NativeFunction, Operation, Span, Token, TokenKind, TokenOwned, Type, Value,
}; };
pub fn parse(source: &str) -> Result<Chunk, DustError> { pub fn parse(source: &str) -> Result<Chunk, DustError> {
@ -107,6 +107,67 @@ impl<'src> Parser<'src> {
Ok(()) 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> { fn allow(&mut self, allowed: Token) -> Result<bool, ParseError> {
if self.current_token == allowed { if self.current_token == allowed {
self.advance()?; self.advance()?;
@ -138,7 +199,7 @@ impl<'src> Parser<'src> {
self.current_statement_length += 1; self.current_statement_length += 1;
self.chunk.push_instruction(instruction, position); self.chunk.instructions_mut().push((instruction, position));
} }
fn optimize_statement(&mut self) { fn optimize_statement(&mut self) {
@ -236,7 +297,7 @@ impl<'src> Parser<'src> {
} }
fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), ParseError> { 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(); let register = self.next_register();
self.emit_instruction( self.emit_instruction(
@ -476,7 +537,7 @@ impl<'src> Parser<'src> {
let argument = match instruction.operation() { let argument = match instruction.operation() {
Operation::GetLocal => { Operation::GetLocal => {
let local_index = instruction.b(); 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; is_mutable_local = local.is_mutable;
local.register_index local.register_index
@ -754,25 +815,17 @@ impl<'src> Parser<'src> {
} }
fn parse_variable(&mut self, allowed: Allowed) -> Result<(), ParseError> { fn parse_variable(&mut self, allowed: Allowed) -> Result<(), ParseError> {
let token = self.current_token;
let start_position = self.current_position; let start_position = self.current_position;
let local_index = if let Token::Identifier(text) = token { let local_index = if let Token::Identifier(text) = self.current_token {
if let Ok(local_index) = self.chunk.get_local_index(text, start_position) { if let Ok(local_index) = self.get_local_index(text) {
local_index local_index
} else if let Some(name) = self.chunk.name() { } else if let Some(name) = self.chunk.name() {
if name.as_str() == text { if name.as_str() == text {
let register = self.next_register(); let register = self.next_register();
self.emit_instruction(Instruction::load_self(register), start_position); self.emit_instruction(Instruction::load_self(register), start_position);
self.declare_local(text, None, false, register);
self.chunk.declare_local(
Identifier::new(text),
None,
false,
register,
start_position,
)?;
self.current_is_expression = true; self.current_is_expression = true;
@ -783,7 +836,7 @@ impl<'src> Parser<'src> {
self.parse_native_call(allowed) self.parse_native_call(allowed)
} else { } else {
Err(ParseError::UndeclaredVariable { Err(ParseError::UndeclaredVariable {
identifier: Identifier::new(text), identifier: text.to_string(),
position: start_position, position: start_position,
}) })
}; };
@ -792,7 +845,7 @@ impl<'src> Parser<'src> {
self.parse_native_call(allowed) self.parse_native_call(allowed)
} else { } else {
Err(ParseError::UndeclaredVariable { Err(ParseError::UndeclaredVariable {
identifier: Identifier::new(text), identifier: text.to_string(),
position: start_position, position: start_position,
}) })
}; };
@ -807,12 +860,9 @@ impl<'src> Parser<'src> {
self.advance()?; self.advance()?;
let is_mutable = self
.chunk
.get_local(local_index, start_position)?
.is_mutable;
if self.allow(Token::Equal)? { if self.allow(Token::Equal)? {
let is_mutable = self.get_local(local_index)?.is_mutable;
if !allowed.assignment { if !allowed.assignment {
return Err(ParseError::InvalidAssignmentTarget { return Err(ParseError::InvalidAssignmentTarget {
found: self.current_token.to_owned(), found: self.current_token.to_owned(),
@ -822,7 +872,7 @@ impl<'src> Parser<'src> {
if !is_mutable { if !is_mutable {
return Err(ParseError::CannotMutateImmutableVariable { return Err(ParseError::CannotMutateImmutableVariable {
identifier: self.chunk.get_identifier(local_index).cloned().unwrap(), identifier: self.chunk.get_identifier(local_index).unwrap(),
position: start_position, position: start_position,
}); });
} }
@ -838,10 +888,7 @@ impl<'src> Parser<'src> {
})?; })?;
if previous_instruction.operation().is_math() { if previous_instruction.operation().is_math() {
let register_index = self let register_index = self.get_local(local_index)?.register_index;
.chunk
.get_local(local_index, start_position)?
.register_index;
log::trace!("Condensing SET_LOCAL to binary math expression"); 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() { if let Some(skippable) = self.get_last_jumpable_mut() {
skippable.set_c_to_boolean(true); skippable.set_c_to_boolean(true);
} else { } else {
self.chunk.insert_instruction( self.chunk.instructions_mut().insert(
else_start, else_start,
(
Instruction::jump(jump_distance, true), Instruction::jump(jump_distance, true),
self.current_position, self.current_position,
)?; ),
);
} }
} else { } else {
self.chunk.insert_instruction( self.chunk.instructions_mut().insert(
else_start, else_start,
(
Instruction::jump(jump_distance, true), Instruction::jump(jump_distance, true),
self.current_position, self.current_position,
)?; ),
);
} }
} else { } else {
self.current_is_expression = false; self.current_is_expression = false;
@ -1054,11 +1105,13 @@ impl<'src> Parser<'src> {
if else_end - if_block_end > 1 { if else_end - if_block_end > 1 {
let jump_distance = (else_end - if_block_end) as u8; let jump_distance = (else_end - if_block_end) as u8;
self.chunk.insert_instruction( self.chunk.instructions_mut().insert(
if_block_end, if_block_end,
(
Instruction::jump(jump_distance, true), Instruction::jump(jump_distance, true),
self.current_position, self.current_position,
)?; ),
);
} }
} else { } else {
return Err(ParseError::ExpectedTokenMultiple { return Err(ParseError::ExpectedTokenMultiple {
@ -1096,11 +1149,13 @@ impl<'src> Parser<'src> {
let block_end = self.chunk.len() as u8; let block_end = self.chunk.len() as u8;
self.chunk.insert_instruction( self.chunk.instructions_mut().insert(
block_start, block_start,
(
Instruction::jump(block_end - block_start as u8 + 1, true), Instruction::jump(block_end - block_start as u8 + 1, true),
self.current_position, self.current_position,
)?; ),
);
let jump_back_distance = block_end - expression_start + 1; let jump_back_distance = block_end - expression_start + 1;
let jump_back = Instruction::jump(jump_back_distance, false); 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 { let identifier = if let Token::Identifier(text) = self.current_token {
self.advance()?; self.advance()?;
Identifier::new(text) text
} else { } else {
return Err(ParseError::ExpectedToken { return Err(ParseError::ExpectedToken {
expected: TokenKind::Identifier, expected: TokenKind::Identifier,
@ -1289,9 +1344,7 @@ impl<'src> Parser<'src> {
self.expect(Token::Equal)?; self.expect(Token::Equal)?;
self.parse_expression()?; self.parse_expression()?;
let local_index = self let (local_index, _) = self.declare_local(identifier, r#type, is_mutable, register);
.chunk
.declare_local(identifier, r#type, is_mutable, register, position)?;
let register = self.next_register().saturating_sub(1); let register = self.next_register().saturating_sub(1);
self.emit_instruction( self.emit_instruction(
@ -1310,27 +1363,25 @@ impl<'src> Parser<'src> {
let mut function_parser = Parser::new(self.lexer)?; let mut function_parser = Parser::new(self.lexer)?;
let identifier = if let Token::Identifier(text) = function_parser.current_token { let identifier = if let Token::Identifier(text) = function_parser.current_token {
let position = function_parser.current_position; let position = function_parser.current_position;
let identifier = Identifier::new(text);
function_parser.advance()?; 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 { } else {
None None
}; };
function_parser.expect(Token::LeftParenthesis)?; 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 { while function_parser.current_token != Token::RightParenthesis {
let start = function_parser.current_position.0;
let is_mutable = function_parser.allow(Token::Mut)?; let is_mutable = function_parser.allow(Token::Mut)?;
let parameter = if let Token::Identifier(text) = function_parser.current_token { let parameter = if let Token::Identifier(text) = function_parser.current_token {
function_parser.advance()?; function_parser.advance()?;
Identifier::new(text) text
} else { } else {
return Err(ParseError::ExpectedToken { return Err(ParseError::ExpectedToken {
expected: TokenKind::Identifier, expected: TokenKind::Identifier,
@ -1348,26 +1399,22 @@ impl<'src> Parser<'src> {
function_parser.advance()?; 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 let register = value_parameters
.as_ref() .as_ref()
.map(|values| values.len() as u8 - 1) .map(|values| values.len() as u8 - 1)
.unwrap_or(0); .unwrap_or(0);
let (_, identifier_index) = function_parser.declare_local(
function_parser.chunk.declare_local(
parameter, parameter,
Some(r#type), Some(r#type.clone()),
is_mutable, is_mutable,
register, 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; function_parser.minimum_register += 1;
@ -1409,13 +1456,12 @@ impl<'src> Parser<'src> {
if let Some((identifier, identifier_position)) = identifier { if let Some((identifier, identifier_position)) = identifier {
let register = self.next_register(); let register = self.next_register();
let local_index = self.chunk.declare_local( let (local_index, _) = self.declare_local(
identifier, identifier,
Some(Type::Function(function_type)), Some(Type::Function(function_type)),
false, false,
register, register,
Span(function_start, function_end), );
)?;
self.emit_constant(function, Span(function_start, function_end))?; self.emit_constant(function, Span(function_start, function_end))?;
self.emit_instruction( self.emit_instruction(
@ -1867,17 +1913,7 @@ impl From<&Token<'_>> for ParseRule<'_> {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum ParseError { pub enum ParseError {
CannotChainComparison { // Token errors
position: Span,
},
CannotMutateImmutableVariable {
identifier: Identifier,
position: Span,
},
ExpectedExpression {
found: TokenOwned,
position: Span,
},
ExpectedToken { ExpectedToken {
expected: TokenKind, expected: TokenKind,
found: TokenOwned, found: TokenOwned,
@ -1888,19 +1924,42 @@ pub enum ParseError {
found: TokenOwned, found: TokenOwned,
position: Span, position: Span,
}, },
// Expression errors
CannotChainComparison {
position: Span,
},
ExpectedExpression {
found: TokenOwned,
position: Span,
},
// Variable errors
CannotMutateImmutableVariable {
identifier: String,
position: Span,
},
ExpectedMutableVariable { ExpectedMutableVariable {
found: TokenOwned, found: TokenOwned,
position: Span, position: Span,
}, },
UndeclaredVariable {
identifier: String,
position: Span,
},
// Statement errors
InvalidAssignmentTarget { InvalidAssignmentTarget {
found: TokenOwned, found: TokenOwned,
position: Span, position: Span,
}, },
UndeclaredVariable { UnexpectedReturn {
identifier: Identifier,
position: Span, position: Span,
}, },
UnexpectedReturn {
// Chunk errors
LocalIndexOutOfBounds {
index: usize,
position: Span, position: Span,
}, },
RegisterOverflow { RegisterOverflow {
@ -1911,7 +1970,6 @@ pub enum ParseError {
}, },
// Wrappers around foreign errors // Wrappers around foreign errors
Chunk(ChunkError),
Lex(LexError), Lex(LexError),
ParseFloatError { ParseFloatError {
error: 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 { impl AnnotatedError for ParseError {
fn title() -> &'static str { fn title() -> &'static str {
"Parse Error" "Parse Error"
@ -1939,18 +1991,18 @@ impl AnnotatedError for ParseError {
Self::CannotChainComparison { .. } => "Cannot chain comparison", Self::CannotChainComparison { .. } => "Cannot chain comparison",
Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable", Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable",
Self::ExpectedExpression { .. } => "Expected an expression", Self::ExpectedExpression { .. } => "Expected an expression",
Self::ExpectedMutableVariable { .. } => "Expected a mutable variable",
Self::ExpectedToken { .. } => "Expected a specific token", Self::ExpectedToken { .. } => "Expected a specific token",
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens", Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
Self::ExpectedMutableVariable { .. } => "Expected a mutable variable",
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target", Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
Self::UndeclaredVariable { .. } => "Undeclared variable", Self::Lex(error) => error.description(),
Self::UnexpectedReturn { .. } => "Unexpected return", Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
Self::RegisterOverflow { .. } => "Register overflow",
Self::RegisterUnderflow { .. } => "Register underflow",
Self::ParseFloatError { .. } => "Failed to parse float", Self::ParseFloatError { .. } => "Failed to parse float",
Self::ParseIntError { .. } => "Failed to parse integer", Self::ParseIntError { .. } => "Failed to parse integer",
Self::Chunk(error) => error.description(), Self::RegisterOverflow { .. } => "Register overflow",
Self::Lex(error) => error.description(), Self::RegisterUnderflow { .. } => "Register underflow",
Self::UndeclaredVariable { .. } => "Undeclared variable",
Self::UnexpectedReturn { .. } => "Unexpected return",
} }
} }
@ -1993,16 +2045,18 @@ impl AnnotatedError for ParseError {
Self::InvalidAssignmentTarget { found, .. } => { Self::InvalidAssignmentTarget { found, .. } => {
Some(format!("Invalid assignment target, found {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, .. } => { Self::UndeclaredVariable { identifier, .. } => {
Some(format!("Undeclared variable {identifier}")) Some(format!("Undeclared variable {identifier}"))
} }
Self::UnexpectedReturn { .. } => None, 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::CannotChainComparison { position } => *position,
Self::CannotMutateImmutableVariable { position, .. } => *position, Self::CannotMutateImmutableVariable { position, .. } => *position,
Self::ExpectedExpression { position, .. } => *position, Self::ExpectedExpression { position, .. } => *position,
Self::ExpectedMutableVariable { position, .. } => *position,
Self::ExpectedToken { position, .. } => *position, Self::ExpectedToken { position, .. } => *position,
Self::ExpectedTokenMultiple { position, .. } => *position, Self::ExpectedTokenMultiple { position, .. } => *position,
Self::ExpectedMutableVariable { position, .. } => *position,
Self::InvalidAssignmentTarget { position, .. } => *position, Self::InvalidAssignmentTarget { position, .. } => *position,
Self::UndeclaredVariable { position, .. } => *position, Self::Lex(error) => error.position(),
Self::UnexpectedReturn { position } => *position, Self::LocalIndexOutOfBounds { position, .. } => *position,
Self::RegisterOverflow { position } => *position,
Self::RegisterUnderflow { position } => *position,
Self::ParseFloatError { position, .. } => *position, Self::ParseFloatError { position, .. } => *position,
Self::ParseIntError { position, .. } => *position, Self::ParseIntError { position, .. } => *position,
Self::Chunk(error) => error.position(), Self::RegisterOverflow { position } => *position,
Self::Lex(error) => error.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 serde::{Deserialize, Serialize};
use crate::Identifier;
/// Description of a kind of value. /// Description of a kind of value.
/// ///
/// See the [module documentation](index.html) for more information. /// See the [module documentation](index.html) for more information.
@ -32,7 +30,7 @@ pub enum Type {
Float, Float,
Function(FunctionType), Function(FunctionType),
Generic { Generic {
identifier: Identifier, identifier_index: u8,
concrete_type: Option<Box<Type>>, concrete_type: Option<Box<Type>>,
}, },
Integer, Integer,
@ -45,7 +43,7 @@ pub enum Type {
item_type: Box<Type>, item_type: Box<Type>,
}, },
Map { Map {
pairs: HashMap<Identifier, Type>, pairs: HashMap<u8, Type>,
}, },
Number, Number,
Range { Range {
@ -230,47 +228,6 @@ impl Type {
expected: self.clone(), 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 { impl Display for Type {
@ -285,7 +242,10 @@ impl Display for Type {
Type::Function(function_type) => write!(f, "{function_type}"), Type::Function(function_type) => write!(f, "{function_type}"),
Type::Generic { concrete_type, .. } => { Type::Generic { concrete_type, .. } => {
match concrete_type.clone().map(|r#box| *r#box) { 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}"), Some(concrete_type) => write!(f, "implied to be {concrete_type}"),
None => write!(f, "unknown"), None => write!(f, "unknown"),
} }
@ -416,8 +376,8 @@ impl Ord for Type {
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct FunctionType { pub struct FunctionType {
pub type_parameters: Option<Vec<Identifier>>, pub type_parameters: Option<Vec<u8>>,
pub value_parameters: Option<Vec<(Identifier, Type)>>, pub value_parameters: Option<Vec<(u8, Type)>>,
pub return_type: Option<Box<Type>>, pub return_type: Option<Box<Type>>,
} }
@ -463,25 +423,17 @@ impl Display for FunctionType {
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum StructType { pub enum StructType {
Unit { Unit { name: u8 },
name: Identifier, Tuple { name: u8, fields: Vec<Type> },
}, Fields { name: u8, fields: HashMap<u8, Type> },
Tuple {
name: Identifier,
fields: Vec<Type>,
},
Fields {
name: Identifier,
fields: HashMap<Identifier, Type>,
},
} }
impl StructType { impl StructType {
pub fn name(&self) -> &Identifier { pub fn name(&self) -> u8 {
match self { match self {
StructType::Unit { name } => name, StructType::Unit { name } => *name,
StructType::Tuple { name, .. } => name, StructType::Tuple { name, .. } => *name,
StructType::Fields { name, .. } => name, StructType::Fields { name, .. } => *name,
} }
} }
} }
@ -583,7 +535,7 @@ impl Ord for StructType {
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct EnumType { pub struct EnumType {
pub name: Identifier, pub name: u8,
pub variants: Vec<StructType>, pub variants: Vec<StructType>,
} }

View File

@ -83,6 +83,14 @@ impl Value {
Value::Primitive(Primitive::String(to_string.to_string())) 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 { pub fn is_function(&self) -> bool {
matches!(self, Value::Function(_)) matches!(self, Value::Function(_))
} }
@ -894,7 +902,7 @@ impl Object {
display.push_str(", "); 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)?, Ok(value) => value.display(vm, position)?,
Err(error) => { Err(error) => {
return Err(ValueError::CannotDisplay { return Err(ValueError::CannotDisplay {

View File

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

View File

@ -23,7 +23,7 @@ fn equality_assignment_long() {
(Instruction::r#return(true), Span(44, 44)), (Instruction::r#return(true), Span(44, 44)),
], ],
vec![Value::integer(4)], 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)), (Instruction::r#return(true), Span(16, 16)),
], ],
vec![Value::integer(4)], 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(3),
Value::integer(42), 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)) (Instruction::r#return(true), Span(24, 24))
], ],
vec![Value::integer(1), Value::integer(2)], 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)) (Instruction::r#return(false), Span(11, 11))
], ],
vec![Value::integer(42)], 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)) (Instruction::r#return(true), Span(24, 24))
], ],
vec![Value::integer(2)], 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)) (Instruction::r#return(true), Span(23, 23))
], ],
vec![Value::integer(2), Value::integer(3)], 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)), (Instruction::r#return(true), Span(25, 25)),
], ],
vec![Value::integer(41), Value::integer(42)], 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)), (Instruction::r#return(true), Span(25, 25)),
], ],
vec![Value::integer(42), Value::integer(2)], 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![],
vec![ vec![
Local::new(Identifier::new("a"), None, false, 0, 0), Local::new(0, None, false, 0, 0),
Local::new(Identifier::new("b"), None, false, 0, 1), Local::new(0, None, false, 0, 1),
] ]
)) ))
); );
@ -563,7 +563,7 @@ fn r#while() {
(Instruction::r#return(true), Span(42, 42)), (Instruction::r#return(true), Span(42, 42)),
], ],
vec![Value::integer(0), Value::integer(5), Value::integer(1),], 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![],
vec![ vec![
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0), Local::new(0, 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, 1)
] ]
), ),
FunctionType { FunctionType {
type_parameters: None, type_parameters: None,
value_parameters: Some(vec![ value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
return_type: Some(Box::new(Type::Integer)), return_type: Some(Box::new(Type::Integer)),
} }
))) )))
@ -53,27 +50,21 @@ fn function_declaration() {
], ],
vec![], vec![],
vec![ vec![
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0), Local::new(0, 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, 1)
] ]
), ),
FunctionType { FunctionType {
type_parameters: None, type_parameters: None,
value_parameters: Some(vec![ value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
return_type: Some(Box::new(Type::Integer)), return_type: Some(Box::new(Type::Integer)),
}, },
)], )],
vec![Local::new( vec![Local::new(
Identifier::new("add"), 0,
Some(Type::Function(FunctionType { Some(Type::Function(FunctionType {
type_parameters: None, type_parameters: None,
value_parameters: Some(vec![ value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
return_type: Some(Box::new(Type::Integer)), return_type: Some(Box::new(Type::Integer)),
})), })),
false, false,
@ -111,16 +102,13 @@ fn function_call() {
], ],
vec![], vec![],
vec![ vec![
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0), Local::new(0, 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, 1)
] ]
), ),
FunctionType { FunctionType {
type_parameters: None, type_parameters: None,
value_parameters: Some(vec![ value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]),
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
return_type: Some(Box::new(Type::Integer)), return_type: Some(Box::new(Type::Integer)),
} }
), ),

View File

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