Clean up chunk
This commit is contained in:
parent
dcb590fe07
commit
12ae935f50
@ -4,12 +4,11 @@
|
|||||||
//! list of locals that can be executed by the Dust virtual machine. Chunks have a name when they
|
//! list of locals that can be executed by the Dust virtual machine. Chunks have a name when they
|
||||||
//! belong to a named function.
|
//! belong to a named function.
|
||||||
|
|
||||||
use std::fmt::{self, Debug, Display, Formatter, Write};
|
use std::fmt::{self, Debug, Display, Write};
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{ConcreteValue, Disassembler, FunctionType, Instruction, Operation, Scope, Span, Type};
|
use crate::{ConcreteValue, Disassembler, FunctionType, Instruction, Scope, Span, Type};
|
||||||
|
|
||||||
/// In-memory representation of a Dust program or function.
|
/// In-memory representation of a Dust program or function.
|
||||||
///
|
///
|
||||||
@ -59,18 +58,10 @@ impl Chunk {
|
|||||||
self.name.as_ref()
|
self.name.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_name(&mut self, name: String) {
|
|
||||||
self.name = Some(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn r#type(&self) -> &FunctionType {
|
pub fn r#type(&self) -> &FunctionType {
|
||||||
&self.r#type
|
&self.r#type
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_type(&mut self, r#type: FunctionType) {
|
|
||||||
self.r#type = r#type;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.instructions.len()
|
self.instructions.len()
|
||||||
}
|
}
|
||||||
@ -83,113 +74,14 @@ impl Chunk {
|
|||||||
&self.constants
|
&self.constants
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constants_mut(&mut self) -> &mut Vec<ConcreteValue> {
|
|
||||||
&mut self.constants
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_constant(&self, index: u16) -> Result<&ConcreteValue, ChunkError> {
|
|
||||||
self.constants
|
|
||||||
.get(index as usize)
|
|
||||||
.ok_or(ChunkError::ConstantIndexOutOfBounds {
|
|
||||||
index: index as usize,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_or_get_constant(&mut self, value: ConcreteValue) -> u16 {
|
|
||||||
if let Some(index) = self
|
|
||||||
.constants
|
|
||||||
.iter()
|
|
||||||
.position(|constant| *constant == value)
|
|
||||||
{
|
|
||||||
index as u16
|
|
||||||
} else {
|
|
||||||
let index = self.constants.len() as u16;
|
|
||||||
|
|
||||||
self.constants.push(value);
|
|
||||||
|
|
||||||
index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn instructions(&self) -> &Vec<(Instruction, Type, Span)> {
|
pub fn instructions(&self) -> &Vec<(Instruction, Type, Span)> {
|
||||||
&self.instructions
|
&self.instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn instructions_mut(&mut self) -> &mut Vec<(Instruction, Type, Span)> {
|
|
||||||
&mut self.instructions
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_instruction(&self, index: usize) -> Result<&(Instruction, Type, Span), ChunkError> {
|
|
||||||
self.instructions
|
|
||||||
.get(index)
|
|
||||||
.ok_or(ChunkError::InstructionIndexOutOfBounds { index })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_last_operations<const COUNT: usize>(&self) -> Option<[Operation; COUNT]> {
|
|
||||||
let mut n_operations = [Operation::Return; COUNT];
|
|
||||||
|
|
||||||
for (nth, operation) in n_operations.iter_mut().rev().zip(
|
|
||||||
self.instructions
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.map(|(instruction, _, _)| instruction.operation()),
|
|
||||||
) {
|
|
||||||
*nth = operation;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(n_operations)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn locals(&self) -> &Vec<Local> {
|
pub fn locals(&self) -> &Vec<Local> {
|
||||||
&self.locals
|
&self.locals
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn locals_mut(&mut self) -> &mut Vec<Local> {
|
|
||||||
&mut self.locals
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_local(&self, index: u16) -> Result<&Local, ChunkError> {
|
|
||||||
self.locals
|
|
||||||
.get(index as usize)
|
|
||||||
.ok_or(ChunkError::LocalIndexOutOfBounds {
|
|
||||||
index: index as usize,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_local_mut(&mut self, index: u16) -> Result<&mut Local, ChunkError> {
|
|
||||||
self.locals
|
|
||||||
.get_mut(index as usize)
|
|
||||||
.ok_or(ChunkError::LocalIndexOutOfBounds {
|
|
||||||
index: index as usize,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_identifier(&self, local_index: u16) -> 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 get_constant_type(&self, constant_index: u16) -> Result<Type, ChunkError> {
|
|
||||||
self.constants
|
|
||||||
.get(constant_index as usize)
|
|
||||||
.map(|value| value.r#type())
|
|
||||||
.ok_or(ChunkError::ConstantIndexOutOfBounds {
|
|
||||||
index: constant_index as usize,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_local_type(&self, local_index: u16) -> Result<&Type, ChunkError> {
|
|
||||||
self.locals
|
|
||||||
.get(local_index as usize)
|
|
||||||
.map(|local| &local.r#type)
|
|
||||||
.ok_or(ChunkError::LocalIndexOutOfBounds {
|
|
||||||
index: local_index as usize,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disassembler(&self) -> Disassembler {
|
pub fn disassembler(&self) -> Disassembler {
|
||||||
Disassembler::new(self)
|
Disassembler::new(self)
|
||||||
}
|
}
|
||||||
@ -252,37 +144,3 @@ impl Local {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Local {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.identifier_index.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Errors that can occur when using a [`Chunk`].
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum ChunkError {
|
|
||||||
ConstantIndexOutOfBounds { index: usize },
|
|
||||||
FunctionIndexOutOfBounds { index: usize },
|
|
||||||
InstructionIndexOutOfBounds { index: usize },
|
|
||||||
LocalIndexOutOfBounds { index: usize },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ChunkError {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
ChunkError::ConstantIndexOutOfBounds { index } => {
|
|
||||||
write!(f, "Constant index {} out of bounds", index)
|
|
||||||
}
|
|
||||||
ChunkError::FunctionIndexOutOfBounds { index } => {
|
|
||||||
write!(f, "Function index {} out of bounds", index)
|
|
||||||
}
|
|
||||||
ChunkError::InstructionIndexOutOfBounds { index } => {
|
|
||||||
write!(f, "Instruction index {} out of bounds", index)
|
|
||||||
}
|
|
||||||
ChunkError::LocalIndexOutOfBounds { index } => {
|
|
||||||
write!(f, "Local index {} out of bounds", index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -19,9 +19,9 @@ use crate::{
|
|||||||
},
|
},
|
||||||
optimize_control_flow, optimize_set_local,
|
optimize_control_flow, optimize_set_local,
|
||||||
value::ConcreteValue,
|
value::ConcreteValue,
|
||||||
AnnotatedError, Argument, Chunk, ChunkError, Destination, DustError, FunctionType, Instruction,
|
AnnotatedError, Argument, Chunk, Destination, DustError, FunctionType, Instruction, LexError,
|
||||||
LexError, Lexer, Local, NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned,
|
Lexer, Local, NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type,
|
||||||
Type, TypeConflict,
|
TypeConflict,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Compiles the input and returns a chunk.
|
/// Compiles the input and returns a chunk.
|
||||||
@ -44,15 +44,19 @@ pub fn compile(source: &str) -> Result<Chunk, DustError> {
|
|||||||
.parse_top_level()
|
.parse_top_level()
|
||||||
.map_err(|error| DustError::Compile { error, source })?;
|
.map_err(|error| DustError::Compile { error, source })?;
|
||||||
|
|
||||||
Ok(compiler.finish())
|
Ok(compiler.finish(None, None))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Low-level tool for compiling the input a token at a time while assembling a chunk.
|
/// Low-level tool for compiling the input a token at a time while assembling a chunk.
|
||||||
///
|
///
|
||||||
/// See the [`compile`] function an example of how to create and use a Compiler.
|
/// See the [`compile`] function an example of how to create and use a Compiler.
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug)]
|
||||||
pub struct Compiler<'src> {
|
pub struct Compiler<'src> {
|
||||||
chunk: Chunk,
|
self_name: Option<String>,
|
||||||
|
instructions: Vec<(Instruction, Type, Span)>,
|
||||||
|
constants: Vec<ConcreteValue>,
|
||||||
|
locals: Vec<Local>,
|
||||||
|
|
||||||
lexer: Lexer<'src>,
|
lexer: Lexer<'src>,
|
||||||
|
|
||||||
current_token: Token<'src>,
|
current_token: Token<'src>,
|
||||||
@ -69,7 +73,6 @@ pub struct Compiler<'src> {
|
|||||||
impl<'src> Compiler<'src> {
|
impl<'src> Compiler<'src> {
|
||||||
pub fn new(mut lexer: Lexer<'src>) -> Result<Self, CompileError> {
|
pub fn new(mut lexer: Lexer<'src>) -> Result<Self, CompileError> {
|
||||||
let (current_token, current_position) = lexer.next_token()?;
|
let (current_token, current_position) = lexer.next_token()?;
|
||||||
let chunk = Chunk::new(None);
|
|
||||||
|
|
||||||
log::info!(
|
log::info!(
|
||||||
"Begin chunk with {} at {}",
|
"Begin chunk with {} at {}",
|
||||||
@ -78,7 +81,10 @@ impl<'src> Compiler<'src> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Ok(Compiler {
|
Ok(Compiler {
|
||||||
chunk,
|
self_name: None,
|
||||||
|
instructions: Vec::new(),
|
||||||
|
constants: Vec::new(),
|
||||||
|
locals: Vec::new(),
|
||||||
lexer,
|
lexer,
|
||||||
current_token,
|
current_token,
|
||||||
current_position,
|
current_position,
|
||||||
@ -91,20 +97,26 @@ impl<'src> Compiler<'src> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish(mut self) -> Chunk {
|
pub fn finish(
|
||||||
|
self,
|
||||||
|
type_parameters: Option<Vec<u16>>,
|
||||||
|
value_parameters: Option<Vec<(u16, Type)>>,
|
||||||
|
) -> Chunk {
|
||||||
log::info!("End chunk");
|
log::info!("End chunk");
|
||||||
|
|
||||||
if let Type::None = *self.chunk.r#type().return_type {
|
let r#type = FunctionType {
|
||||||
self.chunk.set_type(FunctionType {
|
type_parameters,
|
||||||
type_parameters: None,
|
value_parameters,
|
||||||
value_parameters: None,
|
return_type: Box::new(self.return_type.unwrap_or(Type::None)),
|
||||||
return_type: self
|
};
|
||||||
.return_type
|
|
||||||
.map_or_else(|| Box::new(Type::None), Box::new),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.chunk
|
Chunk::with_data(
|
||||||
|
self.self_name,
|
||||||
|
r#type,
|
||||||
|
self.instructions,
|
||||||
|
self.constants,
|
||||||
|
self.locals,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_eof(&self) -> bool {
|
fn is_eof(&self) -> bool {
|
||||||
@ -112,8 +124,7 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn next_register(&self) -> u16 {
|
fn next_register(&self) -> u16 {
|
||||||
self.chunk
|
self.instructions
|
||||||
.instructions()
|
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|(instruction, _, _)| {
|
.find_map(|(instruction, _, _)| {
|
||||||
@ -146,25 +157,21 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_local(&self, index: u16) -> Result<&Local, CompileError> {
|
fn get_local(&self, index: u16) -> Result<&Local, CompileError> {
|
||||||
self.chunk
|
self.locals
|
||||||
.get_local(index)
|
.get(index as usize)
|
||||||
.map_err(|error| CompileError::Chunk {
|
.ok_or(CompileError::UndeclaredVariable {
|
||||||
error,
|
identifier: format!("#{}", index),
|
||||||
position: self.current_position,
|
position: self.current_position,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_local_index(&self, identifier_text: &str) -> Result<u16, CompileError> {
|
fn get_local_index(&self, identifier_text: &str) -> Result<u16, CompileError> {
|
||||||
self.chunk
|
self.locals
|
||||||
.locals()
|
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|(index, local)| {
|
.find_map(|(index, local)| {
|
||||||
let constant = self
|
let constant = self.constants.get(local.identifier_index as usize)?;
|
||||||
.chunk
|
|
||||||
.constants()
|
|
||||||
.get(local.identifier_index as usize)?;
|
|
||||||
let identifier = if let ConcreteValue::String(identifier) = constant {
|
let identifier = if let ConcreteValue::String(identifier) = constant {
|
||||||
identifier
|
identifier
|
||||||
} else {
|
} else {
|
||||||
@ -193,16 +200,39 @@ impl<'src> Compiler<'src> {
|
|||||||
log::info!("Declaring local {identifier}");
|
log::info!("Declaring local {identifier}");
|
||||||
|
|
||||||
let identifier = ConcreteValue::string(identifier);
|
let identifier = ConcreteValue::string(identifier);
|
||||||
let identifier_index = self.chunk.push_or_get_constant(identifier);
|
let identifier_index = self.push_or_get_constant(identifier);
|
||||||
let local_index = self.chunk.locals().len() as u16;
|
let local_index = self.locals.len() as u16;
|
||||||
|
|
||||||
self.chunk
|
self.locals
|
||||||
.locals_mut()
|
|
||||||
.push(Local::new(identifier_index, r#type, is_mutable, scope));
|
.push(Local::new(identifier_index, r#type, is_mutable, scope));
|
||||||
|
|
||||||
(local_index, identifier_index)
|
(local_index, identifier_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_identifier(&self, local_index: u16) -> 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())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_or_get_constant(&mut self, value: ConcreteValue) -> u16 {
|
||||||
|
if let Some(index) = self
|
||||||
|
.constants
|
||||||
|
.iter()
|
||||||
|
.position(|constant| constant == &value)
|
||||||
|
{
|
||||||
|
index as u16
|
||||||
|
} else {
|
||||||
|
let index = self.constants.len() as u16;
|
||||||
|
|
||||||
|
self.constants.push(value);
|
||||||
|
|
||||||
|
index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn allow(&mut self, allowed: Token) -> Result<bool, CompileError> {
|
fn allow(&mut self, allowed: Token) -> Result<bool, CompileError> {
|
||||||
if self.current_token == allowed {
|
if self.current_token == allowed {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
@ -226,8 +256,7 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn pop_last_instruction(&mut self) -> Result<(Instruction, Type, Span), CompileError> {
|
fn pop_last_instruction(&mut self) -> Result<(Instruction, Type, Span), CompileError> {
|
||||||
self.chunk
|
self.instructions
|
||||||
.instructions_mut()
|
|
||||||
.pop()
|
.pop()
|
||||||
.ok_or_else(|| CompileError::ExpectedExpression {
|
.ok_or_else(|| CompileError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
@ -235,13 +264,27 @@ impl<'src> Compiler<'src> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_last_operations<const COUNT: usize>(&self) -> Option<[Operation; COUNT]> {
|
||||||
|
let mut n_operations = [Operation::Return; COUNT];
|
||||||
|
|
||||||
|
for (nth, operation) in n_operations.iter_mut().rev().zip(
|
||||||
|
self.instructions
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(|(instruction, _, _)| instruction.operation()),
|
||||||
|
) {
|
||||||
|
*nth = operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(n_operations)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_last_jumpable_mut_between(
|
fn get_last_jumpable_mut_between(
|
||||||
&mut self,
|
&mut self,
|
||||||
minimum: usize,
|
minimum: usize,
|
||||||
maximum: usize,
|
maximum: usize,
|
||||||
) -> Option<&mut Instruction> {
|
) -> Option<&mut Instruction> {
|
||||||
self.chunk
|
self.instructions
|
||||||
.instructions_mut()
|
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.rev()
|
.rev()
|
||||||
.skip(minimum)
|
.skip(minimum)
|
||||||
@ -256,32 +299,14 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_last_instruction_type(&self) -> Type {
|
fn get_last_instruction_type(&self) -> Type {
|
||||||
self.chunk
|
self.instructions
|
||||||
.instructions()
|
|
||||||
.last()
|
.last()
|
||||||
.map(|(_, r#type, _)| r#type.clone())
|
.map(|(_, r#type, _)| r#type.clone())
|
||||||
.unwrap_or(Type::None)
|
.unwrap_or(Type::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_argument_type(&self, argument: &Argument) -> Result<Type, CompileError> {
|
fn get_register_type(&self, register_index: u16) -> Result<Type, CompileError> {
|
||||||
let r#type = match argument {
|
for (instruction, r#type, _) in &self.instructions {
|
||||||
Argument::Register(register_index) => self.get_register_type(*register_index)?,
|
|
||||||
Argument::Constant(constant_index) => self
|
|
||||||
.chunk
|
|
||||||
.get_constant(*constant_index)
|
|
||||||
.map_err(|error| CompileError::Chunk {
|
|
||||||
error,
|
|
||||||
position: self.current_position,
|
|
||||||
})?
|
|
||||||
.r#type(),
|
|
||||||
Argument::Local(local_index) => self.get_local(*local_index)?.r#type.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(r#type)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_register_type(&self, register_index: u16) -> Result<Type, CompileError> {
|
|
||||||
for (instruction, r#type, _) in self.chunk.instructions() {
|
|
||||||
if instruction.a() == register_index {
|
if instruction.a() == register_index {
|
||||||
if let Operation::LoadList = instruction.operation() {
|
if let Operation::LoadList = instruction.operation() {
|
||||||
let LoadList { start_register, .. } = LoadList::from(instruction);
|
let LoadList { start_register, .. } = LoadList::from(instruction);
|
||||||
@ -336,9 +361,7 @@ impl<'src> Compiler<'src> {
|
|||||||
position.to_string()
|
position.to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
self.chunk
|
self.instructions.push((instruction, r#type, position));
|
||||||
.instructions_mut()
|
|
||||||
.push((instruction, r#type, position));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_constant(
|
fn emit_constant(
|
||||||
@ -347,7 +370,7 @@ impl<'src> Compiler<'src> {
|
|||||||
position: Span,
|
position: Span,
|
||||||
) -> Result<(), CompileError> {
|
) -> Result<(), CompileError> {
|
||||||
let r#type = constant.r#type();
|
let r#type = constant.r#type();
|
||||||
let constant_index = self.chunk.push_or_get_constant(constant);
|
let constant_index = self.push_or_get_constant(constant);
|
||||||
let destination = Destination::Register(self.next_register());
|
let destination = Destination::Register(self.next_register());
|
||||||
let instruction = Instruction::from(LoadConstant {
|
let instruction = Instruction::from(LoadConstant {
|
||||||
destination,
|
destination,
|
||||||
@ -520,7 +543,7 @@ impl<'src> Compiler<'src> {
|
|||||||
let (argument, push_back) = self.handle_binary_argument(&previous_instruction)?;
|
let (argument, push_back) = self.handle_binary_argument(&previous_instruction)?;
|
||||||
|
|
||||||
if push_back {
|
if push_back {
|
||||||
self.chunk.instructions_mut().push((
|
self.instructions.push((
|
||||||
previous_instruction,
|
previous_instruction,
|
||||||
previous_type.clone(),
|
previous_type.clone(),
|
||||||
previous_position,
|
previous_position,
|
||||||
@ -568,11 +591,11 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
fn parse_math_binary(&mut self) -> Result<(), CompileError> {
|
fn parse_math_binary(&mut self) -> Result<(), CompileError> {
|
||||||
let (left_instruction, left_type, left_position) =
|
let (left_instruction, left_type, left_position) =
|
||||||
self.chunk.instructions_mut().pop().ok_or_else(|| {
|
self.instructions
|
||||||
CompileError::ExpectedExpression {
|
.pop()
|
||||||
|
.ok_or_else(|| CompileError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
}
|
|
||||||
})?;
|
})?;
|
||||||
let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?;
|
let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?;
|
||||||
let left_is_mutable_local = if let Argument::Local(local_index) = left {
|
let left_is_mutable_local = if let Argument::Local(local_index) = left {
|
||||||
@ -601,8 +624,7 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if push_back_left {
|
if push_back_left {
|
||||||
self.chunk
|
self.instructions
|
||||||
.instructions_mut()
|
|
||||||
.push((left_instruction, left_type, left_position));
|
.push((left_instruction, left_type, left_position));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,8 +635,7 @@ impl<'src> Compiler<'src> {
|
|||||||
let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?;
|
let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?;
|
||||||
|
|
||||||
if push_back_right {
|
if push_back_right {
|
||||||
self.chunk
|
self.instructions
|
||||||
.instructions_mut()
|
|
||||||
.push((right_instruction, right_type, right_position));
|
.push((right_instruction, right_type, right_position));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -660,7 +681,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
fn parse_comparison_binary(&mut self) -> Result<(), CompileError> {
|
fn parse_comparison_binary(&mut self) -> Result<(), CompileError> {
|
||||||
if let Some([Operation::Equal | Operation::Less | Operation::LessEqual, _, _]) =
|
if let Some([Operation::Equal | Operation::Less | Operation::LessEqual, _, _]) =
|
||||||
self.chunk.get_last_operations()
|
self.get_last_operations()
|
||||||
{
|
{
|
||||||
return Err(CompileError::CannotChainComparison {
|
return Err(CompileError::CannotChainComparison {
|
||||||
position: self.current_position,
|
position: self.current_position,
|
||||||
@ -668,11 +689,11 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (left_instruction, left_type, left_position) =
|
let (left_instruction, left_type, left_position) =
|
||||||
self.chunk.instructions_mut().pop().ok_or_else(|| {
|
self.instructions
|
||||||
CompileError::ExpectedExpression {
|
.pop()
|
||||||
|
.ok_or_else(|| CompileError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
}
|
|
||||||
})?;
|
})?;
|
||||||
let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?;
|
let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?;
|
||||||
let operator = self.current_token;
|
let operator = self.current_token;
|
||||||
@ -680,8 +701,7 @@ impl<'src> Compiler<'src> {
|
|||||||
let rule = ParseRule::from(&operator);
|
let rule = ParseRule::from(&operator);
|
||||||
|
|
||||||
if push_back_left {
|
if push_back_left {
|
||||||
self.chunk
|
self.instructions
|
||||||
.instructions_mut()
|
|
||||||
.push((left_instruction, left_type, left_position));
|
.push((left_instruction, left_type, left_position));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -689,17 +709,16 @@ impl<'src> Compiler<'src> {
|
|||||||
self.parse_sub_expression(&rule.precedence)?;
|
self.parse_sub_expression(&rule.precedence)?;
|
||||||
|
|
||||||
let (right_instruction, right_type, right_position) =
|
let (right_instruction, right_type, right_position) =
|
||||||
self.chunk.instructions_mut().pop().ok_or_else(|| {
|
self.instructions
|
||||||
CompileError::ExpectedExpression {
|
.pop()
|
||||||
|
.ok_or_else(|| CompileError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
}
|
|
||||||
})?;
|
})?;
|
||||||
let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?;
|
let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?;
|
||||||
|
|
||||||
if push_back_right {
|
if push_back_right {
|
||||||
self.chunk
|
self.instructions
|
||||||
.instructions_mut()
|
|
||||||
.push((right_instruction, right_type, right_position));
|
.push((right_instruction, right_type, right_position));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -762,11 +781,8 @@ impl<'src> Compiler<'src> {
|
|||||||
let (argument, push_back) = self.handle_binary_argument(&left_instruction)?;
|
let (argument, push_back) = self.handle_binary_argument(&left_instruction)?;
|
||||||
|
|
||||||
if push_back {
|
if push_back {
|
||||||
self.chunk.instructions_mut().push((
|
self.instructions
|
||||||
left_instruction,
|
.push((left_instruction, left_type.clone(), left_position));
|
||||||
left_type.clone(),
|
|
||||||
left_position,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let operator = self.current_token;
|
let operator = self.current_token;
|
||||||
@ -817,7 +833,7 @@ impl<'src> Compiler<'src> {
|
|||||||
local_index
|
local_index
|
||||||
} else if let Some(native_function) = NativeFunction::from_str(identifier) {
|
} else if let Some(native_function) = NativeFunction::from_str(identifier) {
|
||||||
return self.parse_native_call(native_function);
|
return self.parse_native_call(native_function);
|
||||||
} else if Some(identifier) == self.chunk.name().map(|string| string.as_str()) {
|
} else if self.self_name.as_deref() == Some(identifier) {
|
||||||
let destination = Destination::Register(self.next_register());
|
let destination = Destination::Register(self.next_register());
|
||||||
let load_self = Instruction::from(LoadSelf { destination });
|
let load_self = Instruction::from(LoadSelf { destination });
|
||||||
|
|
||||||
@ -836,7 +852,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
if !self.current_scope.contains(&local.scope) {
|
if !self.current_scope.contains(&local.scope) {
|
||||||
return Err(CompileError::VariableOutOfScope {
|
return Err(CompileError::VariableOutOfScope {
|
||||||
identifier: self.chunk.get_identifier(local_index).unwrap(),
|
identifier: self.get_identifier(local_index).unwrap(),
|
||||||
position: start_position,
|
position: start_position,
|
||||||
variable_scope: local.scope,
|
variable_scope: local.scope,
|
||||||
access_scope: self.current_scope,
|
access_scope: self.current_scope,
|
||||||
@ -846,7 +862,7 @@ impl<'src> Compiler<'src> {
|
|||||||
if self.allow(Token::Equal)? {
|
if self.allow(Token::Equal)? {
|
||||||
if !is_mutable {
|
if !is_mutable {
|
||||||
return Err(CompileError::CannotMutateImmutableVariable {
|
return Err(CompileError::CannotMutateImmutableVariable {
|
||||||
identifier: self.chunk.get_identifier(local_index).unwrap(),
|
identifier: self.get_identifier(local_index).unwrap(),
|
||||||
position: start_position,
|
position: start_position,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -860,7 +876,7 @@ impl<'src> Compiler<'src> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
self.emit_instruction(set_local, Type::None, start_position);
|
self.emit_instruction(set_local, Type::None, start_position);
|
||||||
optimize_set_local(&mut self.chunk);
|
optimize_set_local(&mut self.instructions);
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -961,7 +977,7 @@ impl<'src> Compiler<'src> {
|
|||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
self.chunk.get_last_operations(),
|
self.get_last_operations(),
|
||||||
Some([
|
Some([
|
||||||
Operation::Equal | Operation::Less | Operation::LessEqual,
|
Operation::Equal | Operation::Less | Operation::LessEqual,
|
||||||
Operation::Jump,
|
Operation::Jump,
|
||||||
@ -969,10 +985,10 @@ impl<'src> Compiler<'src> {
|
|||||||
Operation::LoadBoolean,
|
Operation::LoadBoolean,
|
||||||
])
|
])
|
||||||
) {
|
) {
|
||||||
self.chunk.instructions_mut().pop();
|
self.instructions.pop();
|
||||||
self.chunk.instructions_mut().pop();
|
self.instructions.pop();
|
||||||
self.chunk.instructions_mut().pop();
|
self.instructions.pop();
|
||||||
} else if let Some((instruction, _, _)) = self.chunk.instructions().last() {
|
} else if let Some((instruction, _, _)) = self.instructions.last() {
|
||||||
let test_register = instruction.a();
|
let test_register = instruction.a();
|
||||||
let test = Instruction::from(Test {
|
let test = Instruction::from(Test {
|
||||||
argument: Argument::Register(test_register),
|
argument: Argument::Register(test_register),
|
||||||
@ -982,7 +998,7 @@ impl<'src> Compiler<'src> {
|
|||||||
self.emit_instruction(test, Type::None, self.current_position)
|
self.emit_instruction(test, Type::None, self.current_position)
|
||||||
}
|
}
|
||||||
|
|
||||||
let if_block_start = self.chunk.len();
|
let if_block_start = self.instructions.len();
|
||||||
let if_block_start_position = self.current_position;
|
let if_block_start_position = self.current_position;
|
||||||
|
|
||||||
if let Token::LeftBrace = self.current_token {
|
if let Token::LeftBrace = self.current_token {
|
||||||
@ -995,7 +1011,7 @@ impl<'src> Compiler<'src> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let if_block_end = self.chunk.len();
|
let if_block_end = self.instructions.len();
|
||||||
let mut if_block_distance = (if_block_end - if_block_start) as u16;
|
let mut if_block_distance = (if_block_end - if_block_start) as u16;
|
||||||
let if_block_type = self.get_last_instruction_type();
|
let if_block_type = self.get_last_instruction_type();
|
||||||
let if_last_register = self.next_register().saturating_sub(1);
|
let if_last_register = self.next_register().saturating_sub(1);
|
||||||
@ -1022,7 +1038,7 @@ impl<'src> Compiler<'src> {
|
|||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
let else_block_end = self.chunk.len();
|
let else_block_end = self.instructions.len();
|
||||||
let else_block_distance = (else_block_end - if_block_end) as u16;
|
let else_block_distance = (else_block_end - if_block_end) as u16;
|
||||||
let else_block_type = self.get_last_instruction_type();
|
let else_block_type = self.get_last_instruction_type();
|
||||||
|
|
||||||
@ -1047,8 +1063,7 @@ impl<'src> Compiler<'src> {
|
|||||||
is_positive: true,
|
is_positive: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.chunk
|
self.instructions
|
||||||
.instructions_mut()
|
|
||||||
.insert(if_block_end, (jump, Type::None, self.current_position));
|
.insert(if_block_end, (jump, Type::None, self.current_position));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1059,8 +1074,7 @@ impl<'src> Compiler<'src> {
|
|||||||
is_positive: true,
|
is_positive: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.chunk
|
self.instructions
|
||||||
.instructions_mut()
|
|
||||||
.insert(if_block_end, (jump, Type::None, self.current_position));
|
.insert(if_block_end, (jump, Type::None, self.current_position));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1070,12 +1084,11 @@ impl<'src> Compiler<'src> {
|
|||||||
is_positive: true,
|
is_positive: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.chunk
|
self.instructions
|
||||||
.instructions_mut()
|
|
||||||
.insert(if_block_start, (jump, Type::None, if_block_start_position));
|
.insert(if_block_start, (jump, Type::None, if_block_start_position));
|
||||||
|
|
||||||
if self.chunk.len() >= 4 {
|
if self.instructions.len() >= 4 {
|
||||||
optimize_control_flow(&mut self.chunk);
|
optimize_control_flow(&mut self.instructions);
|
||||||
}
|
}
|
||||||
|
|
||||||
let else_last_register = self.next_register().saturating_sub(1);
|
let else_last_register = self.next_register().saturating_sub(1);
|
||||||
@ -1094,12 +1107,12 @@ impl<'src> Compiler<'src> {
|
|||||||
fn parse_while(&mut self) -> Result<(), CompileError> {
|
fn parse_while(&mut self) -> Result<(), CompileError> {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let expression_start = self.chunk.len() as u16;
|
let expression_start = self.instructions.len() as u16;
|
||||||
|
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
self.chunk.get_last_operations(),
|
self.get_last_operations(),
|
||||||
Some([
|
Some([
|
||||||
Operation::Equal | Operation::Less | Operation::LessEqual,
|
Operation::Equal | Operation::Less | Operation::LessEqual,
|
||||||
Operation::Jump,
|
Operation::Jump,
|
||||||
@ -1107,24 +1120,23 @@ impl<'src> Compiler<'src> {
|
|||||||
Operation::LoadBoolean,
|
Operation::LoadBoolean,
|
||||||
],)
|
],)
|
||||||
) {
|
) {
|
||||||
self.chunk.instructions_mut().pop();
|
self.instructions.pop();
|
||||||
self.chunk.instructions_mut().pop();
|
self.instructions.pop();
|
||||||
self.chunk.instructions_mut().pop();
|
self.instructions.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
let block_start = self.chunk.len();
|
let block_start = self.instructions.len();
|
||||||
|
|
||||||
self.parse_block()?;
|
self.parse_block()?;
|
||||||
|
|
||||||
let block_end = self.chunk.len() as u16;
|
let block_end = self.instructions.len() as u16;
|
||||||
let jump_distance = block_end - block_start as u16 + 1;
|
let jump_distance = block_end - block_start as u16 + 1;
|
||||||
let jump = Instruction::from(Jump {
|
let jump = Instruction::from(Jump {
|
||||||
offset: jump_distance,
|
offset: jump_distance,
|
||||||
is_positive: true,
|
is_positive: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.chunk
|
self.instructions
|
||||||
.instructions_mut()
|
|
||||||
.insert(block_start, (jump, Type::None, self.current_position));
|
.insert(block_start, (jump, Type::None, self.current_position));
|
||||||
|
|
||||||
let jump_back_distance = block_end - expression_start + 1;
|
let jump_back_distance = block_end - expression_start + 1;
|
||||||
@ -1203,7 +1215,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
let expression_type = self.get_last_instruction_type();
|
let expression_type = self.get_last_instruction_type();
|
||||||
|
|
||||||
if expression_type == Type::None || self.chunk.is_empty() {
|
if expression_type == Type::None || self.instructions.is_empty() {
|
||||||
return Err(CompileError::ExpectedExpression {
|
return Err(CompileError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
position: self.current_position,
|
position: self.current_position,
|
||||||
@ -1322,7 +1334,8 @@ impl<'src> Compiler<'src> {
|
|||||||
let position = function_compiler.current_position;
|
let position = function_compiler.current_position;
|
||||||
|
|
||||||
function_compiler.advance()?;
|
function_compiler.advance()?;
|
||||||
function_compiler.chunk.set_name(text.to_string());
|
|
||||||
|
function_compiler.self_name = Some(text.to_string());
|
||||||
|
|
||||||
Some((text, position))
|
Some((text, position))
|
||||||
} else {
|
} else {
|
||||||
@ -1388,13 +1401,8 @@ impl<'src> Compiler<'src> {
|
|||||||
} else {
|
} else {
|
||||||
Box::new(Type::None)
|
Box::new(Type::None)
|
||||||
};
|
};
|
||||||
let function_type = FunctionType {
|
|
||||||
type_parameters: None,
|
|
||||||
value_parameters,
|
|
||||||
return_type,
|
|
||||||
};
|
|
||||||
|
|
||||||
function_compiler.chunk.set_type(function_type.clone());
|
function_compiler.return_type = Some((*return_type).clone());
|
||||||
|
|
||||||
function_compiler.expect(Token::LeftBrace)?;
|
function_compiler.expect(Token::LeftBrace)?;
|
||||||
function_compiler.parse_top_level()?;
|
function_compiler.parse_top_level()?;
|
||||||
@ -1404,10 +1412,16 @@ impl<'src> Compiler<'src> {
|
|||||||
self.current_token = function_compiler.current_token;
|
self.current_token = function_compiler.current_token;
|
||||||
self.current_position = function_compiler.current_position;
|
self.current_position = function_compiler.current_position;
|
||||||
|
|
||||||
let function = ConcreteValue::Function(function_compiler.finish());
|
let function =
|
||||||
let constant_index = self.chunk.push_or_get_constant(function);
|
ConcreteValue::Function(function_compiler.finish(None, value_parameters.clone()));
|
||||||
|
let constant_index = self.push_or_get_constant(function);
|
||||||
let function_end = self.current_position.1;
|
let function_end = self.current_position.1;
|
||||||
let register = self.next_register();
|
let register = self.next_register();
|
||||||
|
let function_type = FunctionType {
|
||||||
|
type_parameters: None,
|
||||||
|
value_parameters,
|
||||||
|
return_type,
|
||||||
|
};
|
||||||
|
|
||||||
self.lexer.skip_to(function_end);
|
self.lexer.skip_to(function_end);
|
||||||
|
|
||||||
@ -1454,8 +1468,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
fn parse_call(&mut self) -> Result<(), CompileError> {
|
fn parse_call(&mut self) -> Result<(), CompileError> {
|
||||||
let (last_instruction, _, _) =
|
let (last_instruction, _, _) =
|
||||||
self.chunk
|
self.instructions
|
||||||
.instructions()
|
|
||||||
.last()
|
.last()
|
||||||
.ok_or_else(|| CompileError::ExpectedExpression {
|
.ok_or_else(|| CompileError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
@ -1478,7 +1491,7 @@ impl<'src> Compiler<'src> {
|
|||||||
let register_type = self.get_register_type(function.index())?;
|
let register_type = self.get_register_type(function.index())?;
|
||||||
let function_return_type = match register_type {
|
let function_return_type = match register_type {
|
||||||
Type::Function(function_type) => *function_type.return_type,
|
Type::Function(function_type) => *function_type.return_type,
|
||||||
Type::SelfChunk => (*self.chunk.r#type().return_type).clone(),
|
Type::SelfChunk => self.return_type.clone().unwrap_or(Type::None),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(CompileError::ExpectedFunction {
|
return Err(CompileError::ExpectedFunction {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
@ -1977,11 +1990,21 @@ pub enum CompileError {
|
|||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Wrappers around foreign errors
|
// Chunk errors
|
||||||
Chunk {
|
ConstantIndexOutOfBounds {
|
||||||
error: ChunkError,
|
index: usize,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
InstructionIndexOutOfBounds {
|
||||||
|
index: usize,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
|
LocalIndexOutOfBounds {
|
||||||
|
index: usize,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Wrappers around foreign errors
|
||||||
Lex(LexError),
|
Lex(LexError),
|
||||||
ParseFloatError {
|
ParseFloatError {
|
||||||
error: ParseFloatError,
|
error: ParseFloatError,
|
||||||
@ -2004,7 +2027,7 @@ impl AnnotatedError for CompileError {
|
|||||||
Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable",
|
Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable",
|
||||||
Self::CannotResolveRegisterType { .. } => "Cannot resolve register type",
|
Self::CannotResolveRegisterType { .. } => "Cannot resolve register type",
|
||||||
Self::CannotResolveVariableType { .. } => "Cannot resolve type",
|
Self::CannotResolveVariableType { .. } => "Cannot resolve type",
|
||||||
Self::Chunk { .. } => "Chunk error",
|
Self::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
|
||||||
Self::ExpectedExpression { .. } => "Expected an expression",
|
Self::ExpectedExpression { .. } => "Expected an expression",
|
||||||
Self::ExpectedFunction { .. } => "Expected a function",
|
Self::ExpectedFunction { .. } => "Expected a function",
|
||||||
Self::ExpectedFunctionType { .. } => "Expected a function type",
|
Self::ExpectedFunctionType { .. } => "Expected a function type",
|
||||||
@ -2013,9 +2036,11 @@ impl AnnotatedError for CompileError {
|
|||||||
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
|
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
|
||||||
Self::IfElseBranchMismatch { .. } => "Type mismatch in if/else branches",
|
Self::IfElseBranchMismatch { .. } => "Type mismatch in if/else branches",
|
||||||
Self::IfMissingElse { .. } => "If statement missing else branch",
|
Self::IfMissingElse { .. } => "If statement missing else branch",
|
||||||
|
Self::InstructionIndexOutOfBounds { .. } => "Instruction index out of bounds",
|
||||||
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
|
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
|
||||||
Self::Lex(error) => error.description(),
|
Self::Lex(error) => error.description(),
|
||||||
Self::ListItemTypeConflict { .. } => "List item type conflict",
|
Self::ListItemTypeConflict { .. } => "List item type conflict",
|
||||||
|
Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
|
||||||
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::ReturnTypeConflict { .. } => "Return type conflict",
|
Self::ReturnTypeConflict { .. } => "Return type conflict",
|
||||||
@ -2030,7 +2055,6 @@ impl AnnotatedError for CompileError {
|
|||||||
Self::CannotMutateImmutableVariable { identifier, .. } => {
|
Self::CannotMutateImmutableVariable { identifier, .. } => {
|
||||||
Some(format!("{identifier} is immutable"))
|
Some(format!("{identifier} is immutable"))
|
||||||
}
|
}
|
||||||
Self::Chunk { error, .. } => Some(error.to_string()),
|
|
||||||
Self::ExpectedExpression { found, .. } => Some(format!("Found {found}")),
|
Self::ExpectedExpression { found, .. } => Some(format!("Found {found}")),
|
||||||
Self::ExpectedFunction { found, actual_type, .. } => {
|
Self::ExpectedFunction { found, actual_type, .. } => {
|
||||||
Some(format!("Expected \"{found}\" to be a function but it has type {actual_type}"))
|
Some(format!("Expected \"{found}\" to be a function but it has type {actual_type}"))
|
||||||
@ -2102,7 +2126,7 @@ impl AnnotatedError for CompileError {
|
|||||||
Self::CannotMutateImmutableVariable { position, .. } => *position,
|
Self::CannotMutateImmutableVariable { position, .. } => *position,
|
||||||
Self::CannotResolveRegisterType { position, .. } => *position,
|
Self::CannotResolveRegisterType { position, .. } => *position,
|
||||||
Self::CannotResolveVariableType { position, .. } => *position,
|
Self::CannotResolveVariableType { position, .. } => *position,
|
||||||
Self::Chunk { position, .. } => *position,
|
Self::ConstantIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::ExpectedExpression { position, .. } => *position,
|
Self::ExpectedExpression { position, .. } => *position,
|
||||||
Self::ExpectedFunction { position, .. } => *position,
|
Self::ExpectedFunction { position, .. } => *position,
|
||||||
Self::ExpectedFunctionType { position, .. } => *position,
|
Self::ExpectedFunctionType { position, .. } => *position,
|
||||||
@ -2111,9 +2135,11 @@ impl AnnotatedError for CompileError {
|
|||||||
Self::ExpectedTokenMultiple { position, .. } => *position,
|
Self::ExpectedTokenMultiple { position, .. } => *position,
|
||||||
Self::IfElseBranchMismatch { position, .. } => *position,
|
Self::IfElseBranchMismatch { position, .. } => *position,
|
||||||
Self::IfMissingElse { position } => *position,
|
Self::IfMissingElse { position } => *position,
|
||||||
|
Self::InstructionIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::InvalidAssignmentTarget { position, .. } => *position,
|
Self::InvalidAssignmentTarget { position, .. } => *position,
|
||||||
Self::Lex(error) => error.position(),
|
Self::Lex(error) => error.position(),
|
||||||
Self::ListItemTypeConflict { position, .. } => *position,
|
Self::ListItemTypeConflict { position, .. } => *position,
|
||||||
|
Self::LocalIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::ParseFloatError { position, .. } => *position,
|
Self::ParseFloatError { position, .. } => *position,
|
||||||
Self::ParseIntError { position, .. } => *position,
|
Self::ParseIntError { position, .. } => *position,
|
||||||
Self::ReturnTypeConflict { position, .. } => *position,
|
Self::ReturnTypeConflict { position, .. } => *position,
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
//! - Use the associated function on `Instruction`
|
//! - Use the associated function on `Instruction`
|
||||||
//! - Use the corresponding struct and call `Instruction::from`
|
//! - Use the corresponding struct and call `Instruction::from`
|
||||||
//!
|
//!
|
||||||
//! # Example
|
//! # Examples
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # use dust_lang::instruction::{Instruction, Move};
|
//! # use dust_lang::instruction::{Instruction, Move};
|
||||||
@ -30,8 +30,6 @@
|
|||||||
//! assert_eq!(move_1, move_2);
|
//! assert_eq!(move_1, move_2);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! # Example
|
|
||||||
//!
|
|
||||||
//! Use the `Destination` and `Argument` enums to create instructions. This is easier to read and
|
//! Use the `Destination` and `Argument` enums to create instructions. This is easier to read and
|
||||||
//! enforces consistency in how the `Instruction` methods are called.
|
//! enforces consistency in how the `Instruction` methods are called.
|
||||||
//!
|
//!
|
||||||
|
@ -15,7 +15,7 @@ pub mod r#type;
|
|||||||
pub mod value;
|
pub mod value;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
|
|
||||||
pub use crate::chunk::{Chunk, ChunkError, Local};
|
pub use crate::chunk::{Chunk, Local};
|
||||||
pub use crate::compiler::{compile, CompileError, Compiler};
|
pub use crate::compiler::{compile, CompileError, Compiler};
|
||||||
pub use crate::disassembler::Disassembler;
|
pub use crate::disassembler::Disassembler;
|
||||||
pub use crate::dust_error::{AnnotatedError, DustError};
|
pub use crate::dust_error::{AnnotatedError, DustError};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Built-in functions that implement extended functionality.
|
//! Built-in functions that implement extended functionality.
|
||||||
//!
|
//!
|
||||||
//! Native functions are used either to implement features that are not possible to implement in
|
//! Native functions are used to implement features that are not possible to implement in Dust
|
||||||
//! Dust itself or that are more efficient to implement in Rust.
|
//! itself or that are more efficient to implement in Rust.
|
||||||
mod logic;
|
mod logic;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
@ -15,14 +15,14 @@ use serde::{Deserialize, Serialize};
|
|||||||
use crate::{AnnotatedError, FunctionType, Instruction, Span, Type, Value, Vm, VmError};
|
use crate::{AnnotatedError, FunctionType, Instruction, Span, Type, Value, Vm, VmError};
|
||||||
|
|
||||||
macro_rules! define_native_function {
|
macro_rules! define_native_function {
|
||||||
($(($name:ident, $byte:literal, $str:expr, $type:expr, $function:expr)),*) => {
|
($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => {
|
||||||
/// A dust-native function.
|
/// A dust-native function.
|
||||||
///
|
///
|
||||||
/// See the [module-level documentation](index.html) for more information.
|
/// See the [module-level documentation](index.html) for more information.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub enum NativeFunction {
|
pub enum NativeFunction {
|
||||||
$(
|
$(
|
||||||
$name = $byte as isize,
|
$name = $bytes as isize,
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,14 +75,14 @@ macro_rules! define_native_function {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<u16> for NativeFunction {
|
impl From<u16> for NativeFunction {
|
||||||
fn from(byte: u16) -> Self {
|
fn from(bytes: u16) -> Self {
|
||||||
match byte {
|
match bytes {
|
||||||
$(
|
$(
|
||||||
$byte => NativeFunction::$name,
|
$bytes => NativeFunction::$name,
|
||||||
)*
|
)*
|
||||||
_ => {
|
_ => {
|
||||||
if cfg!(test) {
|
if cfg!(test) {
|
||||||
panic!("Invalid native function byte: {}", byte)
|
panic!("Invalid native function byte: {}", bytes)
|
||||||
} else {
|
} else {
|
||||||
NativeFunction::Panic
|
NativeFunction::Panic
|
||||||
}
|
}
|
||||||
@ -91,11 +91,11 @@ macro_rules! define_native_function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<NativeFunction> for u8 {
|
impl From<NativeFunction> for u16 {
|
||||||
fn from(native_function: NativeFunction) -> Self {
|
fn from(native_function: NativeFunction) -> Self {
|
||||||
match native_function {
|
match native_function {
|
||||||
$(
|
$(
|
||||||
NativeFunction::$name => $byte,
|
NativeFunction::$name => $bytes,
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,23 @@
|
|||||||
//! Tools used by the compiler to optimize a chunk's bytecode.
|
//! Tools used by the compiler to optimize a chunk's bytecode.
|
||||||
|
|
||||||
use crate::{instruction::SetLocal, Chunk, Operation};
|
use crate::{instruction::SetLocal, Instruction, Operation, Span, Type};
|
||||||
|
|
||||||
|
fn get_last_operations<const COUNT: usize>(
|
||||||
|
instructions: &[(Instruction, Type, Span)],
|
||||||
|
) -> Option<[Operation; COUNT]> {
|
||||||
|
let mut n_operations = [Operation::Return; COUNT];
|
||||||
|
|
||||||
|
for (nth, operation) in n_operations.iter_mut().rev().zip(
|
||||||
|
instructions
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(|(instruction, _, _)| instruction.operation()),
|
||||||
|
) {
|
||||||
|
*nth = operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(n_operations)
|
||||||
|
}
|
||||||
|
|
||||||
/// Optimizes a short control flow pattern.
|
/// Optimizes a short control flow pattern.
|
||||||
///
|
///
|
||||||
@ -22,9 +39,9 @@ use crate::{instruction::SetLocal, Chunk, Operation};
|
|||||||
/// - `Jump`
|
/// - `Jump`
|
||||||
/// - `LoadBoolean` or `LoadConstant`
|
/// - `LoadBoolean` or `LoadConstant`
|
||||||
/// - `LoadBoolean` or `LoadConstant`
|
/// - `LoadBoolean` or `LoadConstant`
|
||||||
pub fn optimize_control_flow(chunk: &mut Chunk) {
|
pub fn optimize_control_flow(instructions: &mut [(Instruction, Type, Span)]) {
|
||||||
if !matches!(
|
if !matches!(
|
||||||
chunk.get_last_operations(),
|
get_last_operations(instructions),
|
||||||
Some([
|
Some([
|
||||||
Operation::Equal | Operation::Less | Operation::LessEqual | Operation::Test,
|
Operation::Equal | Operation::Less | Operation::LessEqual | Operation::Test,
|
||||||
Operation::Jump,
|
Operation::Jump,
|
||||||
@ -37,7 +54,6 @@ pub fn optimize_control_flow(chunk: &mut Chunk) {
|
|||||||
|
|
||||||
log::debug!("Consolidating registers for control flow optimization");
|
log::debug!("Consolidating registers for control flow optimization");
|
||||||
|
|
||||||
let instructions = chunk.instructions_mut();
|
|
||||||
let first_loader = &mut instructions.iter_mut().nth_back(1).unwrap().0;
|
let first_loader = &mut instructions.iter_mut().nth_back(1).unwrap().0;
|
||||||
|
|
||||||
first_loader.set_c_to_boolean(true);
|
first_loader.set_c_to_boolean(true);
|
||||||
@ -67,9 +83,9 @@ pub fn optimize_control_flow(chunk: &mut Chunk) {
|
|||||||
/// The instructions must be in the following order:
|
/// The instructions must be in the following order:
|
||||||
/// - `Add`, `Subtract`, `Multiply`, `Divide` or `Modulo`
|
/// - `Add`, `Subtract`, `Multiply`, `Divide` or `Modulo`
|
||||||
/// - `SetLocal`
|
/// - `SetLocal`
|
||||||
pub fn optimize_set_local(chunk: &mut Chunk) {
|
pub fn optimize_set_local(instructions: &mut Vec<(Instruction, Type, Span)>) {
|
||||||
if !matches!(
|
if !matches!(
|
||||||
chunk.get_last_operations(),
|
get_last_operations(instructions),
|
||||||
Some([
|
Some([
|
||||||
Operation::Add
|
Operation::Add
|
||||||
| Operation::Subtract
|
| Operation::Subtract
|
||||||
@ -84,7 +100,6 @@ pub fn optimize_set_local(chunk: &mut Chunk) {
|
|||||||
|
|
||||||
log::debug!("Condensing math and SetLocal to math instruction");
|
log::debug!("Condensing math and SetLocal to math instruction");
|
||||||
|
|
||||||
let instructions = chunk.instructions_mut();
|
|
||||||
let set_local = SetLocal::from(&instructions.pop().unwrap().0);
|
let set_local = SetLocal::from(&instructions.pop().unwrap().0);
|
||||||
let math_instruction = instructions.last_mut().unwrap().0;
|
let math_instruction = instructions.last_mut().unwrap().0;
|
||||||
let math_instruction_new = *math_instruction
|
let math_instruction_new = *math_instruction
|
||||||
|
@ -6,9 +6,9 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
compile, instruction::*, AbstractValue, AnnotatedError, Argument, Chunk, ChunkError,
|
compile, instruction::*, AbstractValue, AnnotatedError, Argument, Chunk, ConcreteValue,
|
||||||
ConcreteValue, Destination, DustError, Instruction, NativeFunctionError, Operation, Span,
|
Destination, DustError, Instruction, NativeFunctionError, Operation, Span, Value, ValueError,
|
||||||
Value, ValueError, ValueRef,
|
ValueRef,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
|
pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
|
||||||
@ -690,9 +690,10 @@ impl<'a> Vm<'a> {
|
|||||||
|
|
||||||
fn get_constant(&self, constant_index: u16) -> Result<&ConcreteValue, VmError> {
|
fn get_constant(&self, constant_index: u16) -> Result<&ConcreteValue, VmError> {
|
||||||
self.chunk
|
self.chunk
|
||||||
.get_constant(constant_index)
|
.constants()
|
||||||
.map_err(|error| VmError::Chunk {
|
.get(constant_index as usize)
|
||||||
error,
|
.ok_or_else(|| VmError::ConstantIndexOutOfBounds {
|
||||||
|
index: constant_index as usize,
|
||||||
position: self.current_position,
|
position: self.current_position,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -715,11 +716,11 @@ impl<'a> Vm<'a> {
|
|||||||
|
|
||||||
fn read(&mut self) -> Result<Instruction, VmError> {
|
fn read(&mut self) -> Result<Instruction, VmError> {
|
||||||
let (instruction, _type, position) =
|
let (instruction, _type, position) =
|
||||||
self.chunk
|
self.chunk.instructions().get(self.ip).ok_or_else(|| {
|
||||||
.get_instruction(self.ip)
|
VmError::InstructionIndexOutOfBounds {
|
||||||
.map_err(|error| VmError::Chunk {
|
index: self.ip,
|
||||||
error,
|
|
||||||
position: self.current_position,
|
position: self.current_position,
|
||||||
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
@ -816,11 +817,21 @@ pub enum VmError {
|
|||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Wrappers for foreign errors
|
// Chunk errors
|
||||||
Chunk {
|
ConstantIndexOutOfBounds {
|
||||||
error: ChunkError,
|
index: usize,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
InstructionIndexOutOfBounds {
|
||||||
|
index: usize,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
|
LocalIndexOutOfBounds {
|
||||||
|
index: usize,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Wrappers for foreign errors
|
||||||
NativeFunction(NativeFunctionError),
|
NativeFunction(NativeFunctionError),
|
||||||
Value {
|
Value {
|
||||||
error: ValueError,
|
error: ValueError,
|
||||||
@ -835,13 +846,15 @@ impl AnnotatedError for VmError {
|
|||||||
|
|
||||||
fn description(&self) -> &'static str {
|
fn description(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::Chunk { .. } => "Chunk error",
|
Self::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
|
||||||
Self::EmptyRegister { .. } => "Empty register",
|
Self::EmptyRegister { .. } => "Empty register",
|
||||||
Self::ExpectedBoolean { .. } => "Expected boolean",
|
Self::ExpectedBoolean { .. } => "Expected boolean",
|
||||||
Self::ExpectedConcreteValue { .. } => "Expected concrete value",
|
Self::ExpectedConcreteValue { .. } => "Expected concrete value",
|
||||||
Self::ExpectedFunction { .. } => "Expected function",
|
Self::ExpectedFunction { .. } => "Expected function",
|
||||||
Self::ExpectedParent { .. } => "Expected parent",
|
Self::ExpectedParent { .. } => "Expected parent",
|
||||||
Self::ExpectedValue { .. } => "Expected value",
|
Self::ExpectedValue { .. } => "Expected value",
|
||||||
|
Self::InstructionIndexOutOfBounds { .. } => "Instruction index out of bounds",
|
||||||
|
Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
|
||||||
Self::NativeFunction(error) => error.description(),
|
Self::NativeFunction(error) => error.description(),
|
||||||
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
||||||
Self::StackOverflow { .. } => "Stack overflow",
|
Self::StackOverflow { .. } => "Stack overflow",
|
||||||
@ -854,7 +867,6 @@ impl AnnotatedError for VmError {
|
|||||||
|
|
||||||
fn details(&self) -> Option<String> {
|
fn details(&self) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
Self::Chunk { error, .. } => Some(error.to_string()),
|
|
||||||
Self::EmptyRegister { index, .. } => Some(format!("Register R{index} is empty")),
|
Self::EmptyRegister { index, .. } => Some(format!("Register R{index} is empty")),
|
||||||
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
|
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
|
||||||
|
|
||||||
@ -870,13 +882,15 @@ impl AnnotatedError for VmError {
|
|||||||
|
|
||||||
fn position(&self) -> Span {
|
fn position(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Self::Chunk { position, .. } => *position,
|
Self::ConstantIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::EmptyRegister { position, .. } => *position,
|
Self::EmptyRegister { position, .. } => *position,
|
||||||
Self::ExpectedBoolean { position, .. } => *position,
|
Self::ExpectedBoolean { position, .. } => *position,
|
||||||
Self::ExpectedConcreteValue { position, .. } => *position,
|
Self::ExpectedConcreteValue { position, .. } => *position,
|
||||||
Self::ExpectedFunction { position, .. } => *position,
|
Self::ExpectedFunction { position, .. } => *position,
|
||||||
Self::ExpectedParent { position } => *position,
|
Self::ExpectedParent { position } => *position,
|
||||||
Self::ExpectedValue { position, .. } => *position,
|
Self::ExpectedValue { position, .. } => *position,
|
||||||
|
Self::InstructionIndexOutOfBounds { position, .. } => *position,
|
||||||
|
Self::LocalIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::NativeFunction(error) => error.position(),
|
Self::NativeFunction(error) => error.position(),
|
||||||
Self::RegisterIndexOutOfBounds { position, .. } => *position,
|
Self::RegisterIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::StackOverflow { position } => *position,
|
Self::StackOverflow { position } => *position,
|
||||||
|
Loading…
Reference in New Issue
Block a user