1
0

Continue rewrite of instructions and operations

This commit is contained in:
Jeff 2024-12-09 07:01:07 -05:00
parent cc069df7ee
commit a9e867aaab
40 changed files with 957 additions and 999 deletions

View File

@ -137,20 +137,23 @@ The compiler always checks types on the fly, so there is no need for a separate
### Instructions ### Instructions
Dust's virtual machine is register-based and uses 64-bit instructions, which encode nine pieces of Dust's virtual machine is register-based and uses 64-bit instructions, which encode ten pieces of
information: information:
Bit | Description Bit | Description
----- | ----------- ----- | -----------
0-8 | The operation code. 0-5 | Operation code
9 | Boolean flag indicating whether the second argument is a constant 6-8 | Unused, reserved in case more operation codes are needed
10 | Boolean flag indicating whether the third argument is a constant 9 | Flag indicating that A is a local
11 | Boolean flag indicating whether the first argument is a local 10 | Flag indicating that B is a constant
12 | Boolean flag indicating whether the second argument is a local 11 | Flag indicating that B is a local
13 | Boolean flag indicating whether the third argument is a local 12 | Flag indicating that C is a constant
17-32 | First argument, usually the destination register or local where a value is stored 13 | Flag indicating that C is a local
33-48 | Second argument, a register, local, constant or boolean flag 14 | D Argument (boolean value)
49-63 | Third argument, a register, local, constant or boolean flag 15-16 | Unused
17-32 | A argument (unsigned 16-bit integer)
33-48 | B argument (unsigned 16-bit integer)
49-63 | C argument (unsigned 16-bit integer)
Because the instructions are 64 bits, the maximum number of registers is 2^16, which is more than Because the instructions are 64 bits, the maximum number of registers is 2^16, which is more than
enough, even for programs that are very large. This also means that chunks can store up to 2^16 enough, even for programs that are very large. This also means that chunks can store up to 2^16

View File

@ -20,8 +20,7 @@ pub struct Chunk {
name: Option<String>, name: Option<String>,
r#type: FunctionType, r#type: FunctionType,
instructions: SmallVec<[Instruction; 32]>, instructions: SmallVec<[(Instruction, Span); 32]>,
positions: SmallVec<[Span; 32]>,
constants: SmallVec<[ConcreteValue; 16]>, constants: SmallVec<[ConcreteValue; 16]>,
locals: SmallVec<[Local; 8]>, locals: SmallVec<[Local; 8]>,
} }
@ -31,7 +30,6 @@ impl Chunk {
Self { Self {
name, name,
instructions: SmallVec::new(), instructions: SmallVec::new(),
positions: SmallVec::new(),
constants: SmallVec::new(), constants: SmallVec::new(),
locals: SmallVec::new(), locals: SmallVec::new(),
r#type: FunctionType { r#type: FunctionType {
@ -45,8 +43,7 @@ impl Chunk {
pub fn with_data( pub fn with_data(
name: Option<String>, name: Option<String>,
r#type: FunctionType, r#type: FunctionType,
instructions: SmallVec<[Instruction; 32]>, instructions: SmallVec<[(Instruction, Span); 32]>,
positions: SmallVec<[Span; 32]>,
constants: SmallVec<[ConcreteValue; 16]>, constants: SmallVec<[ConcreteValue; 16]>,
locals: SmallVec<[Local; 8]>, locals: SmallVec<[Local; 8]>,
) -> Self { ) -> Self {
@ -54,7 +51,6 @@ impl Chunk {
name, name,
r#type, r#type,
instructions, instructions,
positions,
constants, constants,
locals, locals,
} }
@ -80,14 +76,10 @@ impl Chunk {
&self.constants &self.constants
} }
pub fn instructions(&self) -> &SmallVec<[Instruction; 32]> { pub fn instructions(&self) -> &SmallVec<[(Instruction, Span); 32]> {
&self.instructions &self.instructions
} }
pub fn positions(&self) -> &SmallVec<[Span; 32]> {
&self.positions
}
pub fn locals(&self) -> &SmallVec<[Local; 8]> { pub fn locals(&self) -> &SmallVec<[Local; 8]> {
&self.locals &self.locals
} }
@ -96,9 +88,9 @@ impl Chunk {
self.instructions() self.instructions()
.iter() .iter()
.rev() .rev()
.find_map(|instruction| { .find_map(|(instruction, _)| {
if instruction.yields_value() { if instruction.yields_value() {
Some(instruction.a() as usize + 1) Some(instruction.a as usize + 1)
} else { } else {
None None
} }
@ -145,10 +137,10 @@ 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 {
/// The index of the identifier in the constants table. /// The index of the identifier in the constants table.
pub identifier_index: u16, pub identifier_index: u8,
/// The expected type of the local's value. /// Stack index where the local's value is stored.
pub r#type: Type, pub register_index: u8,
/// Whether the local is mutable. /// Whether the local is mutable.
pub is_mutable: bool, pub is_mutable: bool,
@ -159,11 +151,11 @@ pub struct Local {
impl Local { impl Local {
/// Creates a new Local instance. /// Creates a new Local instance.
pub fn new(identifier_index: u16, r#type: Type, mutable: bool, scope: Scope) -> Self { pub fn new(identifier_index: u8, register_index: u8, is_mutable: bool, scope: Scope) -> Self {
Self { Self {
identifier_index, identifier_index,
r#type, register_index,
is_mutable: mutable, is_mutable,
scope, scope,
} }
} }

View File

@ -4,6 +4,8 @@
//! - [`compile`] borrows a string and returns a chunk, handling the entire compilation process and //! - [`compile`] borrows a string and returns a chunk, handling the entire compilation process and
//! turning any resulting [`ComplileError`] into a [`DustError`]. //! turning any resulting [`ComplileError`] into a [`DustError`].
//! - [`Compiler`] uses a lexer to get tokens and assembles a chunk. //! - [`Compiler`] uses a lexer to get tokens and assembles a chunk.
mod optimize;
use std::{ use std::{
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
mem::replace, mem::replace,
@ -11,16 +13,17 @@ use std::{
}; };
use colored::Colorize; use colored::Colorize;
use optimize::{optimize_control_flow, optimize_set_local};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use crate::{ use crate::{
instruction::{ instruction::{
Call, CallNative, Close, DefineLocal, GetLocal, Jump, LoadBoolean, LoadConstant, LoadList, Call, CallNative, Close, GetLocal, Jump, LoadConstant, LoadList, LoadSelf, Move, Negate,
LoadSelf, Move, Negate, Not, Return, SetLocal, Test, Not, Return, SetLocal, Test,
}, },
optimize_control_flow, optimize_set_local, AnnotatedError, Argument, Chunk, ConcreteValue, AnnotatedError, Argument, Chunk, ConcreteValue, DustError, DustString, FunctionType,
Destination, DustError, DustString, FunctionType, Instruction, LexError, Lexer, Local, Instruction, LexError, Lexer, Local, NativeFunction, Operation, Scope, Span, Token, TokenKind,
NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type, TypeConflict, TokenOwned, Type, TypeConflict,
}; };
/// Compiles the input and returns a chunk. /// Compiles the input and returns a chunk.
@ -32,7 +35,7 @@ use crate::{
/// let source = "40 + 2 == 42"; /// let source = "40 + 2 == 42";
/// let chunk = compile(source).unwrap(); /// let chunk = compile(source).unwrap();
/// ///
/// assert_eq!(chunk.len(), 6); /// assert_eq!(chunk.len(), 3);
/// ``` /// ```
pub fn compile(source: &str) -> Result<Chunk, DustError> { pub fn compile(source: &str) -> Result<Chunk, DustError> {
let lexer = Lexer::new(source); let lexer = Lexer::new(source);
@ -55,7 +58,7 @@ pub struct Compiler<'src> {
self_name: Option<DustString>, self_name: Option<DustString>,
instructions: SmallVec<[(Instruction, Type, Span); 32]>, instructions: SmallVec<[(Instruction, Type, Span); 32]>,
constants: SmallVec<[ConcreteValue; 16]>, constants: SmallVec<[ConcreteValue; 16]>,
locals: SmallVec<[Local; 8]>, locals: SmallVec<[(Local, Type); 8]>,
lexer: Lexer<'src>, lexer: Lexer<'src>,
@ -65,7 +68,7 @@ pub struct Compiler<'src> {
previous_position: Span, previous_position: Span,
return_type: Option<Type>, return_type: Option<Type>,
minimum_register: u16, minimum_register: u8,
block_index: u8, block_index: u8,
current_scope: Scope, current_scope: Scope,
} }
@ -99,8 +102,8 @@ impl<'src> Compiler<'src> {
pub fn finish( pub fn finish(
self, self,
type_parameters: Option<SmallVec<[u16; 4]>>, type_parameters: Option<SmallVec<[u8; 4]>>,
value_parameters: Option<SmallVec<[(u16, Type); 4]>>, value_parameters: Option<SmallVec<[(u8, Type); 4]>>,
) -> Chunk { ) -> Chunk {
log::info!("End chunk"); log::info!("End chunk");
@ -109,20 +112,14 @@ impl<'src> Compiler<'src> {
value_parameters, value_parameters,
return_type: self.return_type.unwrap_or(Type::None), return_type: self.return_type.unwrap_or(Type::None),
}; };
let (instructions, positions) = self let instructions = self
.instructions .instructions
.into_iter() .into_iter()
.map(|(instruction, _, position)| (instruction, position)) .map(|(instruction, _, position)| (instruction, position))
.unzip(); .collect();
let locals = self.locals.into_iter().map(|(local, _)| local).collect();
Chunk::with_data( Chunk::with_data(self.self_name, r#type, instructions, self.constants, locals)
self.self_name,
r#type,
instructions,
positions,
self.constants,
self.locals,
)
} }
pub fn compile(&mut self) -> Result<(), CompileError> { pub fn compile(&mut self) -> Result<(), CompileError> {
@ -143,13 +140,13 @@ impl<'src> Compiler<'src> {
matches!(self.current_token, Token::Eof) matches!(self.current_token, Token::Eof)
} }
fn next_register(&self) -> u16 { fn next_register(&self) -> u8 {
self.instructions self.instructions
.iter() .iter()
.rev() .rev()
.find_map(|(instruction, _, _)| { .find_map(|(instruction, _, _)| {
if instruction.yields_value() { if instruction.yields_value() {
Some(instruction.a() + 1) Some(instruction.a + 1)
} else { } else {
None None
} }
@ -176,7 +173,7 @@ impl<'src> Compiler<'src> {
Ok(()) Ok(())
} }
fn get_local(&self, index: u16) -> Result<&Local, CompileError> { fn get_local(&self, index: u8) -> Result<&(Local, Type), CompileError> {
self.locals self.locals
.get(index as usize) .get(index as usize)
.ok_or(CompileError::UndeclaredVariable { .ok_or(CompileError::UndeclaredVariable {
@ -185,12 +182,12 @@ impl<'src> Compiler<'src> {
}) })
} }
fn get_local_index(&self, identifier_text: &str) -> Result<u16, CompileError> { fn get_local_index(&self, identifier_text: &str) -> Result<u8, CompileError> {
self.locals self.locals
.iter() .iter()
.enumerate() .enumerate()
.rev() .rev()
.find_map(|(index, local)| { .find_map(|(index, (local, _))| {
let constant = self.constants.get(local.identifier_index as usize)?; let constant = self.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
@ -199,7 +196,7 @@ impl<'src> Compiler<'src> {
}; };
if identifier == identifier_text { if identifier == identifier_text {
Some(index as u16) Some(index as u8)
} else { } else {
None None
} }
@ -213,39 +210,44 @@ impl<'src> Compiler<'src> {
fn declare_local( fn declare_local(
&mut self, &mut self,
identifier: &str, identifier: &str,
register_index: u8,
r#type: Type, r#type: Type,
is_mutable: bool, is_mutable: bool,
scope: Scope, scope: Scope,
) -> (u16, u16) { ) -> (u8, u8) {
log::info!("Declaring local {identifier}"); log::info!("Declaring local {identifier}");
let identifier = ConcreteValue::string(identifier); let identifier = ConcreteValue::string(identifier);
let identifier_index = self.push_or_get_constant(identifier); let identifier_index = self.push_or_get_constant(identifier);
let local_index = self.locals.len() as u16; let local_index = self.locals.len() as u8;
self.locals self.locals.push((
.push(Local::new(identifier_index, r#type, is_mutable, scope)); Local::new(identifier_index, register_index, is_mutable, scope),
r#type,
));
(local_index, identifier_index) (local_index, identifier_index)
} }
fn get_identifier(&self, local_index: u16) -> Option<String> { fn get_identifier(&self, local_index: u8) -> Option<String> {
self.locals.get(local_index as usize).and_then(|local| { self.locals
.get(local_index as usize)
.and_then(|(local, _)| {
self.constants self.constants
.get(local.identifier_index as usize) .get(local.identifier_index as usize)
.map(|value| value.to_string()) .map(|value| value.to_string())
}) })
} }
fn push_or_get_constant(&mut self, value: ConcreteValue) -> u16 { fn push_or_get_constant(&mut self, value: ConcreteValue) -> u8 {
if let Some(index) = self if let Some(index) = self
.constants .constants
.iter() .iter()
.position(|constant| constant == &value) .position(|constant| constant == &value)
{ {
index as u16 index as u8
} else { } else {
let index = self.constants.len() as u16; let index = self.constants.len() as u8;
self.constants.push(value); self.constants.push(value);
@ -325,17 +327,30 @@ impl<'src> Compiler<'src> {
.unwrap_or(Type::None) .unwrap_or(Type::None)
} }
fn get_register_type(&self, register_index: u16) -> Result<Type, CompileError> { fn get_register_type(&self, register_index: u8) -> Result<Type, CompileError> {
if let Some((_, r#type)) = self
.locals
.iter()
.find(|(local, _)| local.register_index == register_index)
{
return Ok(r#type.clone());
}
for (instruction, r#type, _) in &self.instructions { for (instruction, r#type, _) in &self.instructions {
if instruction.a() == register_index { if !instruction.yields_value() {
if let Operation::LoadList = instruction.operation() { continue;
}
let operation = instruction.operation();
if let Operation::LoadList = operation {
let LoadList { start_register, .. } = LoadList::from(instruction); let LoadList { start_register, .. } = LoadList::from(instruction);
let item_type = self.get_register_type(start_register)?; let item_type = self.get_register_type(start_register)?;
return Ok(Type::List(Box::new(item_type))); return Ok(Type::List(Box::new(item_type)));
} }
if let Operation::LoadSelf = instruction.operation() { if let Operation::LoadSelf = operation {
return Ok(Type::SelfChunk); return Ok(Type::SelfChunk);
} }
@ -343,7 +358,6 @@ impl<'src> Compiler<'src> {
return Ok(r#type.clone()); return Ok(r#type.clone());
} }
} }
}
Err(CompileError::CannotResolveRegisterType { Err(CompileError::CannotResolveRegisterType {
register_index: register_index as usize, register_index: register_index as usize,
@ -391,14 +405,10 @@ impl<'src> Compiler<'src> {
) -> Result<(), CompileError> { ) -> Result<(), CompileError> {
let r#type = constant.r#type(); let r#type = constant.r#type();
let constant_index = self.push_or_get_constant(constant); let constant_index = self.push_or_get_constant(constant);
let destination = Destination::Register(self.next_register()); let destination = self.next_register();
let instruction = Instruction::from(LoadConstant { let load_constant = Instruction::load_constant(destination, constant_index, false);
destination,
constant_index,
jump_next: false,
});
self.emit_instruction(instruction, r#type, position); self.emit_instruction(load_constant, r#type, position);
Ok(()) Ok(())
} }
@ -410,14 +420,10 @@ impl<'src> Compiler<'src> {
self.advance()?; self.advance()?;
let boolean = text.parse::<bool>().unwrap(); let boolean = text.parse::<bool>().unwrap();
let destination = Destination::Register(self.next_register()); let destination = self.next_register();
let instruction = Instruction::from(LoadBoolean { let load_boolean = Instruction::load_boolean(destination, boolean, false);
destination,
value: boolean,
jump_next: false,
});
self.emit_instruction(instruction, Type::Boolean, position); self.emit_instruction(load_boolean, Type::Boolean, position);
Ok(()) Ok(())
} else { } else {
@ -576,7 +582,7 @@ impl<'src> Compiler<'src> {
)) ))
} }
let destination = Destination::Register(self.next_register()); let destination = self.next_register();
let instruction = match operator.kind() { let instruction = match operator.kind() {
TokenKind::Bang => Instruction::from(Not { TokenKind::Bang => Instruction::from(Not {
destination, destination,
@ -624,8 +630,13 @@ impl<'src> Compiler<'src> {
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 Operation::GetLocal = left_instruction.operation() {
self.get_local(local_index)?.is_mutable let GetLocal { local_index, .. } = GetLocal::from(&left_instruction);
self.locals
.get(local_index as usize)
.map(|(local, _)| local.is_mutable)
.unwrap_or(false)
} else { } else {
false false
}; };
@ -742,12 +753,11 @@ impl<'src> Compiler<'src> {
let destination = if is_assignment { let destination = if is_assignment {
match left { match left {
Argument::Register(register) => Destination::Register(register), Argument::Register(register) => register,
Argument::Local(local_index) => Destination::Local(local_index), Argument::Constant(_) => self.next_register(),
Argument::Constant(_) => Destination::Register(self.next_register()),
} }
} else { } else {
Destination::Register(self.next_register()) self.next_register()
}; };
let instruction = match operator { let instruction = match operator {
Token::Plus | Token::PlusEqual => Instruction::add(destination, left, right), Token::Plus | Token::PlusEqual => Instruction::add(destination, left, right),
@ -823,13 +833,14 @@ impl<'src> Compiler<'src> {
.push((right_instruction, right_type, right_position)); .push((right_instruction, right_type, right_position));
} }
let destination = self.next_register();
let comparison = match operator { let comparison = match operator {
Token::DoubleEqual => Instruction::equal(true, left, right), Token::DoubleEqual => Instruction::equal(destination, true, left, right),
Token::BangEqual => Instruction::equal(false, left, right), Token::BangEqual => Instruction::equal(destination, false, left, right),
Token::Less => Instruction::less(true, left, right), Token::Less => Instruction::less(destination, true, left, right),
Token::LessEqual => Instruction::less_equal(true, left, right), Token::LessEqual => Instruction::less_equal(destination, true, left, right),
Token::Greater => Instruction::less_equal(false, left, right), Token::Greater => Instruction::less_equal(destination, false, left, right),
Token::GreaterEqual => Instruction::less(false, left, right), Token::GreaterEqual => Instruction::less(destination, false, left, right),
_ => { _ => {
return Err(CompileError::ExpectedTokenMultiple { return Err(CompileError::ExpectedTokenMultiple {
expected: &[ expected: &[
@ -845,26 +856,8 @@ impl<'src> Compiler<'src> {
}) })
} }
}; };
let destination = Destination::Register(self.next_register());
let jump = Instruction::from(Jump {
offset: 1,
is_positive: true,
});
let load_true = Instruction::from(LoadBoolean {
destination,
value: true,
jump_next: true,
});
let load_false = Instruction::from(LoadBoolean {
destination,
value: false,
jump_next: false,
});
self.emit_instruction(comparison, Type::None, operator_position); self.emit_instruction(comparison, Type::Boolean, operator_position);
self.emit_instruction(jump, Type::None, operator_position);
self.emit_instruction(load_true, Type::Boolean, operator_position);
self.emit_instruction(load_false, Type::Boolean, operator_position);
Ok(()) Ok(())
} }
@ -893,10 +886,8 @@ impl<'src> Compiler<'src> {
}); });
} }
if let Some([Operation::Test, Operation::Jump]) = self.get_last_operations() {}
let (argument, push_back) = self.handle_binary_argument(&left_instruction)?; let (argument, push_back) = self.handle_binary_argument(&left_instruction)?;
let is_local = matches!(argument, Argument::Local(_)); let is_local = left_instruction.operation() == Operation::GetLocal;
if push_back || is_local { if push_back || is_local {
self.instructions self.instructions
@ -929,7 +920,7 @@ impl<'src> Compiler<'src> {
if is_logic_chain { if is_logic_chain {
let expression_length = self.instructions.len() - jump_index - 1; let expression_length = self.instructions.len() - jump_index - 1;
jump_distance += expression_length as u16; jump_distance += expression_length as u8;
let jump = Instruction::jump(jump_distance, true); let jump = Instruction::jump(jump_distance, true);
self.instructions self.instructions
@ -957,7 +948,7 @@ impl<'src> Compiler<'src> {
} 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 self.self_name.as_deref() == Some(identifier) { } else if self.self_name.as_deref() == Some(identifier) {
let destination = Destination::Register(self.next_register()); let destination = self.next_register();
let load_self = Instruction::from(LoadSelf { destination }); let load_self = Instruction::from(LoadSelf { destination });
self.emit_instruction(load_self, Type::SelfChunk, start_position); self.emit_instruction(load_self, Type::SelfChunk, start_position);
@ -970,7 +961,9 @@ impl<'src> Compiler<'src> {
}); });
}; };
let local = self.get_local(local_index)?; let (local, r#type) = self
.get_local(local_index)
.map(|(local, r#type)| (local, r#type.clone()))?;
let is_mutable = local.is_mutable; let is_mutable = local.is_mutable;
if !self.current_scope.contains(&local.scope) { if !self.current_scope.contains(&local.scope) {
@ -999,17 +992,16 @@ 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.instructions); optimize_set_local(self);
return Ok(()); return Ok(());
} }
let destination = Destination::Register(self.next_register()); let destination = self.next_register();
let get_local = Instruction::from(GetLocal { let get_local = Instruction::from(GetLocal {
destination, destination,
local_index, local_index,
}); });
let r#type = self.get_local(local_index)?.r#type.clone();
self.emit_instruction(get_local, r#type, self.previous_position); self.emit_instruction(get_local, r#type, self.previous_position);
@ -1083,7 +1075,7 @@ impl<'src> Compiler<'src> {
self.allow(Token::Comma)?; self.allow(Token::Comma)?;
} }
let destination = Destination::Register(self.next_register()); let destination = self.next_register();
let end = self.previous_position.1; let end = self.previous_position.1;
let load_list = Instruction::from(LoadList { let load_list = Instruction::from(LoadList {
destination, destination,
@ -1099,22 +1091,18 @@ impl<'src> Compiler<'src> {
self.advance()?; self.advance()?;
self.parse_expression()?; self.parse_expression()?;
if matches!( if let Some((instruction, _, _)) = self.instructions.last() {
self.get_last_operations(), let argument = match instruction.destination_as_argument() {
Some([ Some(argument) => argument,
Operation::Equal | Operation::Less | Operation::LessEqual, None => {
Operation::Jump, return Err(CompileError::ExpectedExpression {
Operation::LoadBoolean, found: self.previous_token.to_owned(),
Operation::LoadBoolean, position: self.previous_position,
]) });
) { }
self.instructions.pop(); };
self.instructions.pop();
self.instructions.pop();
} else if let Some((instruction, _, _)) = self.instructions.last() {
let test_register = instruction.a();
let test = Instruction::from(Test { let test = Instruction::from(Test {
argument: Argument::Register(test_register), argument,
test_value: true, test_value: true,
}); });
@ -1135,7 +1123,7 @@ impl<'src> Compiler<'src> {
} }
let if_block_end = self.instructions.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 u8;
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);
@ -1162,7 +1150,7 @@ impl<'src> Compiler<'src> {
}; };
let else_block_end = self.instructions.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 u8;
let else_block_type = self.get_last_instruction_type(); let else_block_type = self.get_last_instruction_type();
if let Err(conflict) = if_block_type.check(&else_block_type) { if let Err(conflict) = if_block_type.check(&else_block_type) {
@ -1178,7 +1166,7 @@ impl<'src> Compiler<'src> {
if let Some(skippable) = if let Some(skippable) =
self.get_last_jumpable_mut_between(1, if_block_distance as usize) self.get_last_jumpable_mut_between(1, if_block_distance as usize)
{ {
skippable.set_c_to_boolean(true); skippable.c = true as u8;
} else { } else {
if_block_distance += 1; if_block_distance += 1;
let jump = Instruction::from(Jump { let jump = Instruction::from(Jump {
@ -1230,7 +1218,7 @@ 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.instructions.len() as u16; let expression_start = self.instructions.len() as u8;
self.parse_expression()?; self.parse_expression()?;
@ -1252,8 +1240,8 @@ impl<'src> Compiler<'src> {
self.parse_block()?; self.parse_block()?;
let block_end = self.instructions.len() as u16; let block_end = self.instructions.len() as u8;
let jump_distance = block_end - block_start as u16 + 1; let jump_distance = block_end - block_start as u8 + 1;
let jump = Instruction::from(Jump { let jump = Instruction::from(Jump {
offset: jump_distance, offset: jump_distance,
is_positive: true, is_positive: true,
@ -1303,7 +1291,7 @@ impl<'src> Compiler<'src> {
let argument_count = destination - start_register; let argument_count = destination - start_register;
let return_type = function.r#type().return_type; let return_type = function.r#type().return_type;
let call_native = Instruction::from(CallNative { let call_native = Instruction::from(CallNative {
destination: Destination::Register(destination), destination,
function, function,
argument_count, argument_count,
}); });
@ -1417,21 +1405,20 @@ impl<'src> Compiler<'src> {
self.expect(Token::Equal)?; self.expect(Token::Equal)?;
self.parse_expression()?; self.parse_expression()?;
let register = self.next_register() - 1; let register_index = self.next_register() - 1;
let r#type = if let Some(r#type) = explicit_type { let r#type = if let Some(r#type) = explicit_type {
r#type r#type
} else { } else {
self.get_register_type(register)? self.get_register_type(register_index)?
}; };
let (local_index, _) =
self.declare_local(identifier, r#type, is_mutable, self.current_scope);
let define_local = Instruction::from(DefineLocal {
local_index,
register,
is_mutable,
});
self.emit_instruction(define_local, Type::None, position); self.declare_local(
identifier,
register_index,
r#type,
is_mutable,
self.current_scope,
);
Ok(()) Ok(())
} }
@ -1453,7 +1440,7 @@ impl<'src> Compiler<'src> {
function_compiler.expect(Token::LeftParenthesis)?; function_compiler.expect(Token::LeftParenthesis)?;
let mut value_parameters: Option<SmallVec<[(u16, Type); 4]>> = None; let mut value_parameters: Option<SmallVec<[(u8, Type); 4]>> = None;
while !function_compiler.allow(Token::RightParenthesis)? { while !function_compiler.allow(Token::RightParenthesis)? {
let is_mutable = function_compiler.allow(Token::Mut)?; let is_mutable = function_compiler.allow(Token::Mut)?;
@ -1478,8 +1465,10 @@ impl<'src> Compiler<'src> {
function_compiler.advance()?; function_compiler.advance()?;
let local_register_index = function_compiler.next_register();
let (_, identifier_index) = function_compiler.declare_local( let (_, identifier_index) = function_compiler.declare_local(
parameter, parameter,
local_register_index,
r#type.clone(), r#type.clone(),
is_mutable, is_mutable,
function_compiler.current_scope, function_compiler.current_scope,
@ -1526,7 +1515,7 @@ impl<'src> Compiler<'src> {
let function = let function =
ConcreteValue::function(function_compiler.finish(None, value_parameters.clone())); ConcreteValue::function(function_compiler.finish(None, value_parameters.clone()));
let constant_index = self.push_or_get_constant(function); let constant_index = self.push_or_get_constant(function);
let register = self.next_register(); let destination = self.next_register();
let function_type = FunctionType { let function_type = FunctionType {
type_parameters: None, type_parameters: None,
value_parameters, value_parameters,
@ -1536,30 +1525,23 @@ impl<'src> Compiler<'src> {
if let Some((identifier, position)) = identifier_info { if let Some((identifier, position)) = identifier_info {
let (local_index, _) = self.declare_local( let (local_index, _) = self.declare_local(
identifier, identifier,
destination,
Type::function(function_type.clone()), Type::function(function_type.clone()),
false, false,
self.current_scope, self.current_scope,
); );
let load_constant = Instruction::from(LoadConstant { let load_constant = Instruction::load_constant(destination, constant_index, false);
destination: Destination::Register(register), let set_local = Instruction::set_local(destination, local_index);
constant_index,
jump_next: false,
});
let define_local = Instruction::from(DefineLocal {
local_index,
register,
is_mutable: false,
});
self.emit_instruction( self.emit_instruction(
load_constant, load_constant,
Type::function(function_type), Type::function(function_type),
Span(function_start, function_end), Span(function_start, function_end),
); );
self.emit_instruction(define_local, Type::None, position); self.emit_instruction(set_local, Type::None, position);
} else { } else {
let load_constant = Instruction::from(LoadConstant { let load_constant = Instruction::from(LoadConstant {
destination: Destination::Register(register), destination,
constant_index, constant_index,
jump_next: false, jump_next: false,
}); });
@ -1637,9 +1619,9 @@ impl<'src> Compiler<'src> {
} }
let end = self.current_position.1; let end = self.current_position.1;
let register = self.next_register(); let destination = self.next_register();
let call = Instruction::from(Call { let call = Instruction::from(Call {
destination: Destination::Register(register), destination,
function, function,
argument_count, argument_count,
}); });

View File

@ -2,7 +2,7 @@
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::{instruction::SetLocal, Instruction, Operation, Span, Type}; use crate::{instruction::SetLocal, CompileError, Compiler, Instruction, Operation, Span, Type};
fn get_last_operations<const COUNT: usize>( fn get_last_operations<const COUNT: usize>(
instructions: &[(Instruction, Type, Span)], instructions: &[(Instruction, Type, Span)],
@ -58,13 +58,12 @@ pub fn optimize_control_flow(instructions: &mut [(Instruction, Type, Span)]) {
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.c = true as u8;
let first_loader_register = first_loader.a(); let first_loader_destination = first_loader.a;
let second_loader = &mut instructions.last_mut().unwrap().0; let second_loader = &mut instructions.last_mut().unwrap().0;
let second_loader_new = *second_loader.clone().set_a(first_loader_register);
*second_loader = second_loader_new; second_loader.a = first_loader_destination;
} }
/// Optimizes a math instruction followed by a SetLocal instruction. /// Optimizes a math instruction followed by a SetLocal instruction.
@ -85,9 +84,9 @@ pub fn optimize_control_flow(instructions: &mut [(Instruction, Type, Span)]) {
/// 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(instructions: &mut SmallVec<[(Instruction, Type, Span); 32]>) { pub fn optimize_set_local(compiler: &mut Compiler) -> Result<(), CompileError> {
if !matches!( if !matches!(
get_last_operations(instructions), compiler.get_last_operations(),
Some([ Some([
Operation::Add Operation::Add
| Operation::Subtract | Operation::Subtract
@ -97,17 +96,17 @@ pub fn optimize_set_local(instructions: &mut SmallVec<[(Instruction, Type, Span)
Operation::SetLocal, Operation::SetLocal,
]) ])
) { ) {
return; return Ok(());
} }
log::debug!("Condensing math and SetLocal to math instruction"); log::debug!("Condensing math and SetLocal to math instruction");
let set_local = SetLocal::from(&instructions.pop().unwrap().0); let set_local = SetLocal::from(&compiler.instructions.pop().unwrap().0);
let math_instruction = instructions.last_mut().unwrap().0; let (local, _) = compiler.get_local(set_local.local_index)?;
let math_instruction_new = *math_instruction let local_register_index = local.register_index;
.clone() let math_instruction = &mut compiler.instructions.last_mut().unwrap().0;
.set_a(set_local.local_index)
.set_a_is_local(true);
instructions.last_mut().unwrap().0 = math_instruction_new; math_instruction.a = local_register_index;
Ok(())
} }

View File

@ -61,8 +61,8 @@ const CONSTANT_HEADER: [&str; 4] = [
const LOCAL_HEADER: [&str; 4] = [ const LOCAL_HEADER: [&str; 4] = [
"Locals", "Locals",
"------", "------",
" i SCOPE MUTABLE TYPE IDENTIFIER ", " i IDENTIFIER REGISTER SCOPE MUTABLE",
"--- ------- ------- ---------------- ----------------", "--- ---------------- -------- ------- -------",
]; ];
/// Builder that constructs a human-readable representation of a chunk. /// Builder that constructs a human-readable representation of a chunk.
@ -209,13 +209,7 @@ impl<'a> Disassembler<'a> {
self.push_header(line); self.push_header(line);
} }
for (index, (instruction, position)) in self for (index, (instruction, position)) in self.chunk.instructions().iter().enumerate() {
.chunk
.instructions()
.iter()
.zip(self.chunk.positions().iter())
.enumerate()
{
let position = position.to_string(); let position = position.to_string();
let operation = instruction.operation().to_string(); let operation = instruction.operation().to_string();
let info = instruction.disassembly_info(); let info = instruction.disassembly_info();
@ -235,7 +229,7 @@ impl<'a> Disassembler<'a> {
index, index,
Local { Local {
identifier_index, identifier_index,
r#type, register_index,
scope, scope,
is_mutable, is_mutable,
}, },
@ -247,10 +241,10 @@ impl<'a> Disassembler<'a> {
.get(*identifier_index as usize) .get(*identifier_index as usize)
.map(|value| value.to_string()) .map(|value| value.to_string())
.unwrap_or_else(|| "unknown".to_string()); .unwrap_or_else(|| "unknown".to_string());
let type_display = r#type.to_string(); let register_display = format!("R{register_index}");
let scope = scope.to_string(); let scope = scope.to_string();
let local_display = format!( let local_display = format!(
"{index:^3} {scope:^7} {is_mutable:^7} {type_display:^16} {identifier_display:^16}" "{index:^3} {identifier_display:^16} {register_display:^8} {scope:^7} {is_mutable:^7}"
); );
self.push_details(&local_display); self.push_details(&local_display);

View File

@ -1,14 +1,12 @@
//! Top-level Dust errors with source code annotations. //! Top-level error for the Dust language API that can create detailed reports with source code
//! annotations.
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use annotate_snippets::{Level, Renderer, Snippet}; use annotate_snippets::{Level, Renderer, Snippet};
use crate::{CompileError, Span, VmError}; use crate::{CompileError, Span, VmError};
/// A top-level error that can occur during the execution of Dust code. /// A top-level error that can occur during the interpretation of Dust code.
///
/// This error can display nicely formatted messages with source code annotations.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum DustError<'src> { pub enum DustError<'src> {
Compile { Compile {
@ -30,7 +28,7 @@ impl<'src> DustError<'src> {
DustError::Runtime { error, source } DustError::Runtime { error, source }
} }
pub fn create_report(&self) -> String { pub fn report(&self) -> String {
let (position, title, description, details) = self.error_data(); let (position, title, description, details) = self.error_data();
let label = format!("{}: {}", title, description); let label = format!("{}: {}", title, description);
let details = details.unwrap_or_else(|| "While parsing this code".to_string()); let details = details.unwrap_or_else(|| "While parsing this code".to_string());
@ -74,7 +72,7 @@ impl<'src> DustError<'src> {
impl Display for DustError<'_> { impl Display for DustError<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.create_report()) write!(f, "{}", self.report())
} }
} }

View File

@ -1,14 +1,14 @@
use crate::{Argument, Destination, Instruction, Operation}; use crate::{Argument, Instruction, Operation};
pub struct Add { pub struct Add {
pub destination: Destination, pub destination: u8,
pub left: Argument, pub left: Argument,
pub right: Argument, pub right: Argument,
} }
impl From<&Instruction> for Add { impl From<&Instruction> for Add {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
Add { Add {
@ -21,16 +21,12 @@ impl From<&Instruction> for Add {
impl From<Add> for Instruction { impl From<Add> for Instruction {
fn from(add: Add) -> Self { fn from(add: Add) -> Self {
let (a, a_options) = add.destination.as_index_and_a_options(); let operation = Operation::Add;
let a = add.destination;
let (b, b_options) = add.left.as_index_and_b_options(); let (b, b_options) = add.left.as_index_and_b_options();
let (c, c_options) = add.right.as_index_and_c_options(); let (c, c_options) = add.right.as_index_and_c_options();
let metadata = operation as u8 | b_options.bits() | c_options.bits();
Instruction { Instruction { metadata, a, b, c }
operation: Operation::ADD,
options: a_options | b_options | c_options,
a,
b,
c,
}
} }
} }

View File

@ -1,32 +1,32 @@
use crate::{Argument, Destination, Instruction, Operation}; use crate::{Argument, Instruction, Operation};
pub struct Call { pub struct Call {
pub destination: Destination, pub destination: u8,
pub function: Argument, pub function: Argument,
pub argument_count: u16, pub argument_count: u8,
} }
impl From<&Instruction> for Call { impl From<&Instruction> for Call {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let function = instruction.b_as_argument();
let argument_count = instruction.c;
Call { Call {
destination: instruction.a_as_destination(), destination,
function: instruction.b_as_argument(), function,
argument_count: instruction.c, argument_count,
} }
} }
} }
impl From<Call> for Instruction { impl From<Call> for Instruction {
fn from(call: Call) -> Self { fn from(call: Call) -> Self {
let (a, a_options) = call.destination.as_index_and_a_options(); let a = call.destination;
let (b, b_options) = call.function.as_index_and_b_options(); let (b, b_options) = call.function.as_index_and_b_options();
let c = call.argument_count;
let metadata = Operation::Call as u8 | b_options.bits();
Instruction { Instruction { metadata, a, b, c }
operation: Operation::CALL,
options: a_options | b_options,
a,
b,
c: call.argument_count,
}
} }
} }

View File

@ -1,14 +1,14 @@
use crate::{Destination, Instruction, NativeFunction, Operation}; use crate::{Instruction, NativeFunction, Operation};
pub struct CallNative { pub struct CallNative {
pub destination: Destination, pub destination: u8,
pub function: NativeFunction, pub function: NativeFunction,
pub argument_count: u16, pub argument_count: u8,
} }
impl From<&Instruction> for CallNative { impl From<&Instruction> for CallNative {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let function = NativeFunction::from(instruction.b); let function = NativeFunction::from(instruction.b);
CallNative { CallNative {
@ -21,15 +21,11 @@ impl From<&Instruction> for CallNative {
impl From<CallNative> for Instruction { impl From<CallNative> for Instruction {
fn from(call_native: CallNative) -> Self { fn from(call_native: CallNative) -> Self {
let (a, a_options) = call_native.destination.as_index_and_a_options(); let metadata = Operation::CallNative as u8;
let b = call_native.function as u16; let a = call_native.destination;
let b = call_native.function as u8;
let c = call_native.argument_count;
Instruction { Instruction { metadata, a, b, c }
operation: Operation::CALL_NATIVE,
options: a_options,
a,
b,
c: call_native.argument_count,
}
} }
} }

View File

@ -1,10 +1,8 @@
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionOptions;
pub struct Close { pub struct Close {
pub from: u16, pub from: u8,
pub to: u16, pub to: u8,
} }
impl From<&Instruction> for Close { impl From<&Instruction> for Close {
@ -18,12 +16,9 @@ impl From<&Instruction> for Close {
impl From<Close> for Instruction { impl From<Close> for Instruction {
fn from(close: Close) -> Self { fn from(close: Close) -> Self {
Instruction { let metadata = Operation::Close as u8;
operation: Operation::CLOSE, let (a, b, c) = (0, close.from, close.to);
options: InstructionOptions::empty(),
a: 0, Instruction { metadata, a, b, c }
b: close.from,
c: close.to,
}
} }
} }

View File

@ -1,14 +1,14 @@
use crate::{Argument, Destination, Instruction, Operation}; use crate::{Argument, Instruction, Operation};
pub struct Divide { pub struct Divide {
pub destination: Destination, pub destination: u8,
pub left: Argument, pub left: Argument,
pub right: Argument, pub right: Argument,
} }
impl From<&Instruction> for Divide { impl From<&Instruction> for Divide {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
Divide { Divide {
@ -21,16 +21,11 @@ impl From<&Instruction> for Divide {
impl From<Divide> for Instruction { impl From<Divide> for Instruction {
fn from(divide: Divide) -> Self { fn from(divide: Divide) -> Self {
let (a, a_options) = divide.destination.as_index_and_a_options(); let a = divide.destination;
let (b, b_options) = divide.left.as_index_and_b_options(); let (b, b_options) = divide.left.as_index_and_b_options();
let (c, c_options) = divide.right.as_index_and_c_options(); let (c, c_options) = divide.right.as_index_and_c_options();
let metadata = Operation::Divide as u8 | b_options.bits() | c_options.bits();
Instruction { Instruction { metadata, a, b, c }
operation: Operation::DIVIDE,
options: a_options | b_options | c_options,
a,
b,
c,
}
} }
} }

View File

@ -1,9 +1,9 @@
use crate::{Argument, Destination, Instruction, Operation}; use crate::{Argument, Instruction, Operation};
use super::InstructionOptions; use super::InstructionOptions;
pub struct Equal { pub struct Equal {
pub destination: Destination, pub destination: u8,
pub value: bool, pub value: bool,
pub left: Argument, pub left: Argument,
pub right: Argument, pub right: Argument,
@ -11,8 +11,8 @@ pub struct Equal {
impl From<&Instruction> for Equal { impl From<&Instruction> for Equal {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let value = instruction.options.d(); let value = instruction.d();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
Equal { Equal {
@ -26,7 +26,7 @@ impl From<&Instruction> for Equal {
impl From<Equal> for Instruction { impl From<Equal> for Instruction {
fn from(equal: Equal) -> Self { fn from(equal: Equal) -> Self {
let (a, a_options) = equal.destination.as_index_and_a_options(); let a = equal.destination;
let (b, b_options) = equal.left.as_index_and_b_options(); let (b, b_options) = equal.left.as_index_and_b_options();
let (c, c_options) = equal.right.as_index_and_c_options(); let (c, c_options) = equal.right.as_index_and_c_options();
let d_options = if equal.value { let d_options = if equal.value {
@ -34,13 +34,9 @@ impl From<Equal> for Instruction {
} else { } else {
InstructionOptions::empty() InstructionOptions::empty()
}; };
let metadata =
Operation::Equal as u8 | b_options.bits() | c_options.bits() | d_options.bits();
Instruction { Instruction { metadata, a, b, c }
operation: Operation::EQUAL,
options: a_options | b_options | c_options | d_options,
a,
b,
c,
}
} }
} }

View File

@ -1,31 +1,29 @@
use crate::{Destination, Instruction, Operation}; use crate::{Instruction, Operation};
pub struct GetLocal { pub struct GetLocal {
pub destination: Destination, pub destination: u8,
pub local_index: u16, pub local_index: u8,
} }
impl From<&Instruction> for GetLocal { impl From<&Instruction> for GetLocal {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let local_index = instruction.b;
GetLocal { GetLocal {
destination, destination,
local_index: instruction.b, local_index,
} }
} }
} }
impl From<GetLocal> for Instruction { impl From<GetLocal> for Instruction {
fn from(get_local: GetLocal) -> Self { fn from(get_local: GetLocal) -> Self {
let (a, a_options) = get_local.destination.as_index_and_a_options(); let a = get_local.destination;
let b = get_local.local_index;
let c = 0;
let metadata = Operation::GetLocal as u8;
Instruction { Instruction { metadata, a, b, c }
operation: Operation::GET_LOCAL,
options: a_options,
a,
b: get_local.local_index,
c: 0,
}
} }
} }

View File

@ -1,9 +1,7 @@
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionOptions;
pub struct Jump { pub struct Jump {
pub offset: u16, pub offset: u8,
pub is_positive: bool, pub is_positive: bool,
} }
@ -18,12 +16,11 @@ impl From<&Instruction> for Jump {
impl From<Jump> for Instruction { impl From<Jump> for Instruction {
fn from(jump: Jump) -> Self { fn from(jump: Jump) -> Self {
Instruction { let metadata = Operation::Jump as u8;
operation: Operation::JUMP, let a = 0;
options: InstructionOptions::empty(), let b = jump.offset;
a: 0, let c = jump.is_positive as u8;
b: jump.offset,
c: jump.is_positive as u16, Instruction { metadata, a, b, c }
}
} }
} }

View File

@ -1,9 +1,9 @@
use crate::{Argument, Destination, Instruction, Operation}; use crate::{Argument, Instruction, Operation};
use super::InstructionOptions; use super::InstructionOptions;
pub struct Less { pub struct Less {
pub destination: Destination, pub destination: u8,
pub value: bool, pub value: bool,
pub left: Argument, pub left: Argument,
pub right: Argument, pub right: Argument,
@ -11,8 +11,8 @@ pub struct Less {
impl From<&Instruction> for Less { impl From<&Instruction> for Less {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let value = instruction.options.d(); let value = instruction.d();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
Less { Less {
@ -26,7 +26,7 @@ impl From<&Instruction> for Less {
impl From<Less> for Instruction { impl From<Less> for Instruction {
fn from(less: Less) -> Self { fn from(less: Less) -> Self {
let (a, a_options) = less.destination.as_index_and_a_options(); let a = less.destination;
let (b, b_options) = less.left.as_index_and_b_options(); let (b, b_options) = less.left.as_index_and_b_options();
let (c, c_options) = less.right.as_index_and_c_options(); let (c, c_options) = less.right.as_index_and_c_options();
let d_options = if less.value { let d_options = if less.value {
@ -34,13 +34,9 @@ impl From<Less> for Instruction {
} else { } else {
InstructionOptions::empty() InstructionOptions::empty()
}; };
let metadata =
Operation::Less as u8 | b_options.bits() | c_options.bits() | d_options.bits();
Instruction { Instruction { metadata, a, b, c }
operation: Operation::LESS,
options: a_options | b_options | c_options | d_options,
a,
b,
c,
}
} }
} }

View File

@ -1,9 +1,9 @@
use crate::{Argument, Destination, Instruction, Operation}; use crate::{Argument, Instruction, Operation};
use super::InstructionOptions; use super::InstructionOptions;
pub struct LessEqual { pub struct LessEqual {
pub destination: Destination, pub destination: u8,
pub value: bool, pub value: bool,
pub left: Argument, pub left: Argument,
pub right: Argument, pub right: Argument,
@ -11,8 +11,8 @@ pub struct LessEqual {
impl From<&Instruction> for LessEqual { impl From<&Instruction> for LessEqual {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let value = instruction.options.d(); let value = instruction.d();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
LessEqual { LessEqual {
@ -26,7 +26,7 @@ impl From<&Instruction> for LessEqual {
impl From<LessEqual> for Instruction { impl From<LessEqual> for Instruction {
fn from(less_equal: LessEqual) -> Self { fn from(less_equal: LessEqual) -> Self {
let (a, a_options) = less_equal.destination.as_index_and_a_options(); let a = less_equal.destination;
let (b, b_options) = less_equal.left.as_index_and_b_options(); let (b, b_options) = less_equal.left.as_index_and_b_options();
let (c, c_options) = less_equal.right.as_index_and_c_options(); let (c, c_options) = less_equal.right.as_index_and_c_options();
let d_options = if less_equal.value { let d_options = if less_equal.value {
@ -34,13 +34,9 @@ impl From<LessEqual> for Instruction {
} else { } else {
InstructionOptions::empty() InstructionOptions::empty()
}; };
let metadata =
Operation::LessEqual as u8 | b_options.bits() | c_options.bits() | d_options.bits();
Instruction { Instruction { metadata, a, b, c }
operation: Operation::LESS_EQUAL,
options: a_options | b_options | c_options | d_options,
a,
b,
c,
}
} }
} }

View File

@ -1,14 +1,14 @@
use crate::{Destination, Instruction, Operation}; use crate::{Instruction, Operation};
pub struct LoadBoolean { pub struct LoadBoolean {
pub destination: Destination, pub destination: u8,
pub value: bool, pub value: bool,
pub jump_next: bool, pub jump_next: bool,
} }
impl From<&Instruction> for LoadBoolean { impl From<&Instruction> for LoadBoolean {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let value = instruction.b != 0; let value = instruction.b != 0;
let jump_next = instruction.c != 0; let jump_next = instruction.c != 0;
@ -22,16 +22,11 @@ impl From<&Instruction> for LoadBoolean {
impl From<LoadBoolean> for Instruction { impl From<LoadBoolean> for Instruction {
fn from(load_boolean: LoadBoolean) -> Self { fn from(load_boolean: LoadBoolean) -> Self {
let (a, options) = load_boolean.destination.as_index_and_a_options(); let metadata = Operation::LoadBoolean as u8;
let b = load_boolean.value as u16; let a = load_boolean.destination;
let c = load_boolean.jump_next as u16; let b = load_boolean.value as u8;
let c = load_boolean.jump_next as u8;
Instruction { Instruction { metadata, a, b, c }
operation: Operation::LOAD_BOOLEAN,
options,
a,
b,
c,
}
} }
} }

View File

@ -1,14 +1,14 @@
use crate::{Destination, Instruction, Operation}; use crate::{Instruction, Operation};
pub struct LoadConstant { pub struct LoadConstant {
pub destination: Destination, pub destination: u8,
pub constant_index: u16, pub constant_index: u8,
pub jump_next: bool, pub jump_next: bool,
} }
impl From<&Instruction> for LoadConstant { impl From<&Instruction> for LoadConstant {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let constant_index = instruction.b; let constant_index = instruction.b;
let jump_next = instruction.c != 0; let jump_next = instruction.c != 0;
@ -22,16 +22,11 @@ impl From<&Instruction> for LoadConstant {
impl From<LoadConstant> for Instruction { impl From<LoadConstant> for Instruction {
fn from(load_constant: LoadConstant) -> Self { fn from(load_constant: LoadConstant) -> Self {
let (a, options) = load_constant.destination.as_index_and_a_options(); let metadata = Operation::LoadConstant as u8;
let a = load_constant.destination;
let b = load_constant.constant_index; let b = load_constant.constant_index;
let c = load_constant.jump_next as u16; let c = load_constant.jump_next as u8;
Instruction { Instruction { metadata, a, b, c }
operation: Operation::LOAD_CONSTANT,
options,
a,
b,
c,
}
} }
} }

View File

@ -1,13 +1,13 @@
use crate::{Destination, Instruction, Operation}; use crate::{Instruction, Operation};
pub struct LoadList { pub struct LoadList {
pub destination: Destination, pub destination: u8,
pub start_register: u16, pub start_register: u8,
} }
impl From<&Instruction> for LoadList { impl From<&Instruction> for LoadList {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let start_register = instruction.b; let start_register = instruction.b;
LoadList { LoadList {
@ -19,15 +19,11 @@ impl From<&Instruction> for LoadList {
impl From<LoadList> for Instruction { impl From<LoadList> for Instruction {
fn from(load_list: LoadList) -> Self { fn from(load_list: LoadList) -> Self {
let (a, options) = load_list.destination.as_index_and_a_options(); let metadata = Operation::LoadList as u8;
let a = load_list.destination;
let b = load_list.start_register; let b = load_list.start_register;
let c = 0;
Instruction { Instruction { metadata, a, b, c }
operation: Operation::LOAD_LIST,
options,
a,
b,
c: 0,
}
} }
} }

View File

@ -1,12 +1,12 @@
use crate::{Destination, Instruction, Operation}; use crate::{Instruction, Operation};
pub struct LoadSelf { pub struct LoadSelf {
pub destination: Destination, pub destination: u8,
} }
impl From<&Instruction> for LoadSelf { impl From<&Instruction> for LoadSelf {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
LoadSelf { destination } LoadSelf { destination }
} }
@ -14,14 +14,11 @@ impl From<&Instruction> for LoadSelf {
impl From<LoadSelf> for Instruction { impl From<LoadSelf> for Instruction {
fn from(load_self: LoadSelf) -> Self { fn from(load_self: LoadSelf) -> Self {
let (a, options) = load_self.destination.as_index_and_a_options(); let metadata = Operation::LoadSelf as u8;
let a = load_self.destination;
let b = 0;
let c = 0;
Instruction { Instruction { metadata, a, b, c }
operation: Operation::LOAD_SELF,
options,
a,
b: 0,
c: 0,
}
} }
} }

View File

@ -1,31 +1,32 @@
//! An operation and its arguments for the Dust virtual machine. //! Instructions for the Dust virtual machine.
//! //!
//! Each instruction is 64 bits and holds ten distinct fields: //! Each instruction is 32 bits and uses up to seven distinct fields:
//! //!
//! Bit | Description //! Bit | Description
//! ----- | ----------- //! ----- | -----------
//! 0-8 | Operation code //! 0-4 | Operation code
//! 9 | Flag for whether A is a local //! 5 | Flag indicating if the B argument is a constant
//! 10 | Flag for whether B argument is a constant //! 6 | Flag indicating if the C argument is a constant
//! 11 | Flag for whether C argument is a local //! 7 | D field (boolean)
//! 12 | Flag for whether B argument is a constant //! 8-15 | A field (unsigned 8-bit integer)
//! 13 | Flag for whether C argument is a local //! 16-23 | B field (unsigned 8-bit integer)
//! 14 | D Argument //! 24-31 | C field (unsigned 8-bit integer)
//! 15-16 | Unused
//! 17-32 | A argument
//! 33-48 | B argument
//! 49-63 | C argument
//! //!
//! Be careful when working with instructions directly. When modifying an instruction, be sure to //! **Be careful when working with instructions directly**. When modifying an instruction's fields,
//! account for the fact that setting the A, B, or C arguments to 0 will have no effect. It is //! you may also need to modify its flags. It is usually best to remove instructions and insert new
//! usually best to remove instructions and insert new ones in their place instead of mutating them. //! ones in their place instead of mutating them.
//!
//! # Examples
//!
//! ## Creating Instructions
//! //!
//! For each operation, there are two ways to create an instruction: //! For each operation, there are two ways to create an instruction:
//! //!
//! - 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`
//! //!
//! # Examples //! Both produce the same result, but the first is more concise. The structs are more useful when
//! reading instructions, as shown below.
//! //!
//! ``` //! ```
//! # use dust_lang::instruction::{Instruction, Move}; //! # use dust_lang::instruction::{Instruction, Move};
@ -35,24 +36,63 @@
//! assert_eq!(move_1, move_2); //! assert_eq!(move_1, move_2);
//! ``` //! ```
//! //!
//! Use the `Destination` and `Argument` enums to create instructions. This is easier to read and //! Use the [`Argument`][] type when creating instructions. In addition to being easy to read and
//! enforces consistency in how the `Instruction` methods are called. //! write, this ensures that the instruction has the correct flags to represent the arguments.
//! //!
//! ``` //! ```
//! # use dust_lang::instruction::{Instruction, Add, Destination, Argument}; //! # use dust_lang::instruction::{Instruction, Add, u8, Argument};
//! let add_1 = Instruction::add( //! let add_1 = Instruction::add(
//! Destination::Register(0), //! u8::Register(0),
//! Argument::Local(1), //! Argument::Local(1),
//! Argument::Constant(2) //! Argument::Constant(2)
//! ); //! );
//! let add_2 = Instruction::from(Add { //! let add_2 = Instruction::from(Add {
//! destination: Destination::Register(0), //! destination: u8::Register(0),
//! left: Argument::Local(1), //! left: Argument::Local(1),
//! right: Argument::Constant(2), //! right: Argument::Constant(2),
//! }); //! });
//! //!
//! assert_eq!(add_1, add_2); //! assert_eq!(add_1, add_2);
//! ``` //! ```
//!
//! ## Reading Instructions
//!
//! To read an instruction, check its `operation` field, then convert the instruction to the struct
//! that corresponds to that operation. Like the example above, this removes the burden of dealing
//! with the options and handles the process of reading the options and the instruction's fields
//! into `u8` or `Argument` enums when appropriate.
//!
//! ```
//! # use dust_lang::instruction::{Instruction, Add, u8, Argument, Operation};
//! # let mystery_instruction = Instruction::add(
//! u8::Local(1),
//! Argument::Local(1),
//! Argument::Constant(2)
//! );
//!
//! // Let's read an instruction and see if it performs addition-assignment, like in one of the
//! // following examples:
//! // - `a += 2`
//! // - `a = a + 2`
//! // - `a = 2 + a`
//!
//! let operation = mystery_instruction.operation();
//!
//! match operation {
//! Operation::ADD => {
//! let Add { destination, left, right } = Add::from(&mystery_instruction);
//! let is_add_assign =
//! left == Argument::Register(destination)
//! || right == Argument::Register(destination);
//!
//! assert!(is_add_assign);
//! }
//! // Handle other operations...
//! _ => {
//! panic!("Unknown operation code: {operation}");
//! }
//! }
//! ```
mod add; mod add;
mod call; mod call;
mod call_native; mod call_native;
@ -117,23 +157,32 @@ use crate::NativeFunction;
/// See the [module-level documentation](index.html) for more information. /// See the [module-level documentation](index.html) for more information.
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Instruction { pub struct Instruction {
operation: Operation, pub metadata: u8,
options: InstructionOptions, pub a: u8,
a: u16, pub b: u8,
b: u16, pub c: u8,
c: u16,
} }
impl Instruction { impl Instruction {
pub fn r#move(from: u16, to: u16) -> Instruction { pub fn operation(&self) -> Operation {
let operation_bits = self.metadata & 0b0001_1111;
Operation::from(operation_bits)
}
pub fn d(&self) -> bool {
self.metadata & 0b1000_0000 != 0
}
pub fn r#move(from: u8, to: u8) -> Instruction {
Instruction::from(Move { from, to }) Instruction::from(Move { from, to })
} }
pub fn close(from: u16, to: u16) -> Instruction { pub fn close(from: u8, to: u8) -> Instruction {
Instruction::from(Close { from, to }) Instruction::from(Close { from, to })
} }
pub fn load_boolean(destination: Destination, value: bool, jump_next: bool) -> Instruction { pub fn load_boolean(destination: u8, value: bool, jump_next: bool) -> Instruction {
Instruction::from(LoadBoolean { Instruction::from(LoadBoolean {
destination, destination,
value, value,
@ -141,11 +190,7 @@ impl Instruction {
}) })
} }
pub fn load_constant( pub fn load_constant(destination: u8, constant_index: u8, jump_next: bool) -> Instruction {
destination: Destination,
constant_index: u16,
jump_next: bool,
) -> Instruction {
Instruction::from(LoadConstant { Instruction::from(LoadConstant {
destination, destination,
constant_index, constant_index,
@ -153,32 +198,32 @@ impl Instruction {
}) })
} }
pub fn load_list(destination: Destination, start_register: u16) -> Instruction { pub fn load_list(destination: u8, start_register: u8) -> Instruction {
Instruction::from(LoadList { Instruction::from(LoadList {
destination, destination,
start_register, start_register,
}) })
} }
pub fn load_self(destination: Destination) -> Instruction { pub fn load_self(destination: u8) -> Instruction {
Instruction::from(LoadSelf { destination }) Instruction::from(LoadSelf { destination })
} }
pub fn get_local(destination: Destination, local_index: u16) -> Instruction { pub fn get_local(destination: u8, local_index: u8) -> Instruction {
Instruction::from(GetLocal { Instruction::from(GetLocal {
destination, destination,
local_index, local_index,
}) })
} }
pub fn set_local(register: u16, local_index: u16) -> Instruction { pub fn set_local(register: u8, local_index: u8) -> Instruction {
Instruction::from(SetLocal { Instruction::from(SetLocal {
local_index, local_index,
register_index: register, register_index: register,
}) })
} }
pub fn add(destination: Destination, left: Argument, right: Argument) -> Instruction { pub fn add(destination: u8, left: Argument, right: Argument) -> Instruction {
Instruction::from(Add { Instruction::from(Add {
destination, destination,
left, left,
@ -186,7 +231,7 @@ impl Instruction {
}) })
} }
pub fn subtract(destination: Destination, left: Argument, right: Argument) -> Instruction { pub fn subtract(destination: u8, left: Argument, right: Argument) -> Instruction {
Instruction::from(Subtract { Instruction::from(Subtract {
destination, destination,
left, left,
@ -194,7 +239,7 @@ impl Instruction {
}) })
} }
pub fn multiply(destination: Destination, left: Argument, right: Argument) -> Instruction { pub fn multiply(destination: u8, left: Argument, right: Argument) -> Instruction {
Instruction::from(Multiply { Instruction::from(Multiply {
destination, destination,
left, left,
@ -202,7 +247,7 @@ impl Instruction {
}) })
} }
pub fn divide(destination: Destination, left: Argument, right: Argument) -> Instruction { pub fn divide(destination: u8, left: Argument, right: Argument) -> Instruction {
Instruction::from(Divide { Instruction::from(Divide {
destination, destination,
left, left,
@ -210,7 +255,7 @@ impl Instruction {
}) })
} }
pub fn modulo(destination: Destination, left: Argument, right: Argument) -> Instruction { pub fn modulo(destination: u8, left: Argument, right: Argument) -> Instruction {
Instruction::from(Modulo { Instruction::from(Modulo {
destination, destination,
left, left,
@ -225,7 +270,7 @@ impl Instruction {
}) })
} }
pub fn test_set(destination: Destination, argument: Argument, value: bool) -> Instruction { pub fn test_set(destination: u8, argument: Argument, value: bool) -> Instruction {
Instruction::from(TestSet { Instruction::from(TestSet {
destination, destination,
argument, argument,
@ -233,12 +278,7 @@ impl Instruction {
}) })
} }
pub fn equal( pub fn equal(destination: u8, value: bool, left: Argument, right: Argument) -> Instruction {
destination: Destination,
value: bool,
left: Argument,
right: Argument,
) -> Instruction {
Instruction::from(Equal { Instruction::from(Equal {
destination, destination,
value, value,
@ -247,12 +287,7 @@ impl Instruction {
}) })
} }
pub fn less( pub fn less(destination: u8, value: bool, left: Argument, right: Argument) -> Instruction {
destination: Destination,
value: bool,
left: Argument,
right: Argument,
) -> Instruction {
Instruction::from(Less { Instruction::from(Less {
destination, destination,
value, value,
@ -262,7 +297,7 @@ impl Instruction {
} }
pub fn less_equal( pub fn less_equal(
destination: Destination, destination: u8,
value: bool, value: bool,
left: Argument, left: Argument,
right: Argument, right: Argument,
@ -275,28 +310,28 @@ impl Instruction {
}) })
} }
pub fn negate(destination: Destination, argument: Argument) -> Instruction { pub fn negate(destination: u8, argument: Argument) -> Instruction {
Instruction::from(Negate { Instruction::from(Negate {
destination, destination,
argument, argument,
}) })
} }
pub fn not(destination: Destination, argument: Argument) -> Instruction { pub fn not(destination: u8, argument: Argument) -> Instruction {
Instruction::from(Not { Instruction::from(Not {
destination, destination,
argument, argument,
}) })
} }
pub fn jump(offset: u16, is_positive: bool) -> Instruction { pub fn jump(offset: u8, is_positive: bool) -> Instruction {
Instruction::from(Jump { Instruction::from(Jump {
offset, offset,
is_positive, is_positive,
}) })
} }
pub fn call(destination: Destination, function: Argument, argument_count: u16) -> Instruction { pub fn call(destination: u8, function: Argument, argument_count: u8) -> Instruction {
Instruction::from(Call { Instruction::from(Call {
destination, destination,
function, function,
@ -305,9 +340,9 @@ impl Instruction {
} }
pub fn call_native( pub fn call_native(
destination: Destination, destination: u8,
function: NativeFunction, function: NativeFunction,
argument_count: u16, argument_count: u8,
) -> Instruction { ) -> Instruction {
Instruction::from(CallNative { Instruction::from(CallNative {
destination, destination,
@ -323,55 +358,46 @@ impl Instruction {
} }
pub fn destination_as_argument(&self) -> Option<Argument> { pub fn destination_as_argument(&self) -> Option<Argument> {
match self.operation { match self.operation() {
Operation::LOAD_CONSTANT => Some(Argument::Constant(self.b)), Operation::LoadConstant => Some(Argument::Constant(self.b)),
Operation::GET_LOCAL => Some(Argument::Local(self.b)), Operation::LoadBoolean
Operation::LOAD_BOOLEAN | Operation::LoadList
| Operation::LOAD_LIST | Operation::LoadSelf
| Operation::LOAD_SELF | Operation::GetLocal
| Operation::ADD | Operation::Add
| Operation::SUBTRACT | Operation::Subtract
| Operation::MULTIPLY | Operation::Multiply
| Operation::DIVIDE | Operation::Divide
| Operation::MODULO | Operation::Modulo
| Operation::NEGATE | Operation::Negate
| Operation::NOT | Operation::Not
| Operation::CALL | Operation::Call
| Operation::CALL_NATIVE => Some(Argument::Register(self.a)), | Operation::CallNative => Some(Argument::Register(self.a)),
_ => None, _ => None,
} }
} }
pub fn a_as_destination(&self) -> Destination {
if self.options.a_is_local() {
Destination::Local(self.a)
} else {
Destination::Register(self.a)
}
}
pub fn b_as_argument(&self) -> Argument { pub fn b_as_argument(&self) -> Argument {
if self.options.b_is_constant() { let b_is_constant = self.metadata & 0b0010_0000 != 0;
if b_is_constant {
Argument::Constant(self.b) Argument::Constant(self.b)
} else if self.options.b_is_local() {
Argument::Local(self.b)
} else { } else {
Argument::Register(self.b) Argument::Register(self.b)
} }
} }
pub fn b_and_c_as_arguments(&self) -> (Argument, Argument) { pub fn b_and_c_as_arguments(&self) -> (Argument, Argument) {
let left = if self.options.b_is_constant() { let b_is_constant = self.metadata & 0b0010_0000 != 0;
let c_is_constant = self.metadata & 0b0100_0000 != 0;
let left = if b_is_constant {
Argument::Constant(self.b) Argument::Constant(self.b)
} else if self.options.b_is_local() {
Argument::Local(self.b)
} else { } else {
Argument::Register(self.b) Argument::Register(self.b)
}; };
let right = if self.options.c_is_constant() { let right = if c_is_constant {
Argument::Constant(self.c) Argument::Constant(self.c)
} else if self.options.c_is_local() {
Argument::Local(self.c)
} else { } else {
Argument::Register(self.c) Argument::Register(self.c)
}; };
@ -380,58 +406,53 @@ impl Instruction {
} }
pub fn yields_value(&self) -> bool { pub fn yields_value(&self) -> bool {
match self.operation { match self.operation() {
Operation::LOAD_BOOLEAN Operation::LoadBoolean
| Operation::LOAD_CONSTANT | Operation::LoadConstant
| Operation::LOAD_LIST | Operation::LoadList
| Operation::LOAD_SELF | Operation::LoadMap
| Operation::GET_LOCAL | Operation::LoadSelf
| Operation::ADD | Operation::GetLocal
| Operation::SUBTRACT | Operation::Add
| Operation::MULTIPLY | Operation::Subtract
| Operation::DIVIDE | Operation::Multiply
| Operation::MODULO | Operation::Divide
| Operation::NEGATE | Operation::Modulo
| Operation::NOT | Operation::Power
| Operation::CALL => true, | Operation::Negate
Operation::MOVE | Operation::Not
| Operation::CLOSE | Operation::Equal
| Operation::SET_LOCAL | Operation::Less
| Operation::EQUAL | Operation::LessEqual
| Operation::LESS | Operation::Call => true,
| Operation::LESS_EQUAL Operation::CallNative => {
| Operation::TEST
| Operation::TEST_SET
| Operation::JUMP
| Operation::RETURN => false,
Operation::CALL_NATIVE => {
let function = NativeFunction::from(self.b); let function = NativeFunction::from(self.b);
function.returns_value() function.returns_value()
} }
_ => { Operation::Move
if cfg!(debug_assertions) { | Operation::Close
panic!("Unknown operation {}", self.operation); | Operation::SetLocal
} else { | Operation::Test
false | Operation::TestSet
} | Operation::Jump
} | Operation::Return => false,
} }
} }
pub fn disassembly_info(&self) -> String { pub fn disassembly_info(&self) -> String {
match self.operation { match self.operation() {
Operation::MOVE => { Operation::Move => {
let Move { from, to } = Move::from(self); let Move { from, to } = Move::from(self);
format!("R{to} = R{from}") format!("R{to} = R{from}")
} }
Operation::CLOSE => { Operation::Close => {
let Close { from, to } = Close::from(self); let Close { from, to } = Close::from(self);
format!("R{from}..R{to}") format!("R{from}..R{to}")
} }
Operation::LOAD_BOOLEAN => { Operation::LoadBoolean => {
let LoadBoolean { let LoadBoolean {
destination, destination,
value, value,
@ -439,12 +460,12 @@ impl Instruction {
} = LoadBoolean::from(self); } = LoadBoolean::from(self);
if jump_next { if jump_next {
format!("{destination} = {value} && JUMP +1") format!("R{destination} = {value} && JUMP +1")
} else { } else {
format!("{destination} = {value}") format!("R{destination} = {value}")
} }
} }
Operation::LOAD_CONSTANT => { Operation::LoadConstant => {
let LoadConstant { let LoadConstant {
destination, destination,
constant_index, constant_index,
@ -452,87 +473,87 @@ impl Instruction {
} = LoadConstant::from(self); } = LoadConstant::from(self);
if jump_next { if jump_next {
format!("{destination} = C{constant_index} JUMP +1") format!("R{destination} = C{constant_index} JUMP +1")
} else { } else {
format!("{destination} = C{constant_index}") format!("R{destination} = C{constant_index}")
} }
} }
Operation::LOAD_LIST => { Operation::LoadList => {
let LoadList { let LoadList {
destination, destination,
start_register, start_register,
} = LoadList::from(self); } = LoadList::from(self);
let end_register = destination.index().saturating_sub(1); let end_register = destination.saturating_sub(1);
format!("{destination} = [R{start_register}..=R{end_register}]",) format!("R{destination} = [R{start_register}..=R{end_register}]",)
} }
Operation::LOAD_SELF => { Operation::LoadSelf => {
let LoadSelf { destination } = LoadSelf::from(self); let LoadSelf { destination } = LoadSelf::from(self);
format!("{destination} = self") format!("R{destination} = self")
} }
Operation::GET_LOCAL => { Operation::GetLocal => {
let GetLocal { let GetLocal {
destination, destination,
local_index, local_index,
} = GetLocal::from(self); } = GetLocal::from(self);
format!("{destination} = L{local_index}") format!("R{destination} = L{local_index}")
} }
Operation::SET_LOCAL => { Operation::SetLocal => {
let SetLocal { let SetLocal {
register_index: register, register_index,
local_index, local_index,
} = SetLocal::from(self); } = SetLocal::from(self);
format!("L{local_index} = R{register}") format!("L{local_index} = R{register_index}")
} }
Operation::ADD => { Operation::Add => {
let Add { let Add {
destination, destination,
left, left,
right, right,
} = Add::from(self); } = Add::from(self);
format!("{destination} = {left} + {right}") format!("R{destination} = {left} + {right}")
} }
Operation::SUBTRACT => { Operation::Subtract => {
let Subtract { let Subtract {
destination, destination,
left, left,
right, right,
} = Subtract::from(self); } = Subtract::from(self);
format!("{destination} = {left} - {right}") format!("R{destination} = {left} - {right}")
} }
Operation::MULTIPLY => { Operation::Multiply => {
let Multiply { let Multiply {
destination, destination,
left, left,
right, right,
} = Multiply::from(self); } = Multiply::from(self);
format!("{destination} = {left} * {right}") format!("R{destination} = {left} * {right}")
} }
Operation::DIVIDE => { Operation::Divide => {
let Divide { let Divide {
destination, destination,
left, left,
right, right,
} = Divide::from(self); } = Divide::from(self);
format!("{destination} = {left} / {right}") format!("R{destination} = {left} / {right}")
} }
Operation::MODULO => { Operation::Modulo => {
let Modulo { let Modulo {
destination, destination,
left, left,
right, right,
} = Modulo::from(self); } = Modulo::from(self);
format!("{destination} = {left} % {right}") format!("R{destination} = {left} % {right}")
} }
Operation::TEST => { Operation::Test => {
let Test { let Test {
argument, argument,
test_value: value, test_value: value,
@ -541,7 +562,7 @@ impl Instruction {
format!("if {bang}{argument} {{ JUMP +1 }}",) format!("if {bang}{argument} {{ JUMP +1 }}",)
} }
Operation::TEST_SET => { Operation::TestSet => {
let TestSet { let TestSet {
destination, destination,
argument, argument,
@ -549,9 +570,9 @@ impl Instruction {
} = TestSet::from(self); } = TestSet::from(self);
let bang = if value { "" } else { "!" }; let bang = if value { "" } else { "!" };
format!("if {bang}{argument} {{ JUMP +1 }} else {{ {destination} = {argument} }}") format!("if {bang}{argument} {{ JUMP +1 }} else {{ R{destination} = {argument} }}")
} }
Operation::EQUAL => { Operation::Equal => {
let Equal { let Equal {
destination, destination,
value, value,
@ -560,9 +581,9 @@ impl Instruction {
} = Equal::from(self); } = Equal::from(self);
let comparison_symbol = if value { "==" } else { "!=" }; let comparison_symbol = if value { "==" } else { "!=" };
format!("{destination} = {left} {comparison_symbol} {right}") format!("R{destination} = {left} {comparison_symbol} {right}")
} }
Operation::LESS => { Operation::Less => {
let Less { let Less {
destination, destination,
value, value,
@ -571,9 +592,9 @@ impl Instruction {
} = Less::from(self); } = Less::from(self);
let comparison_symbol = if value { "<" } else { ">=" }; let comparison_symbol = if value { "<" } else { ">=" };
format!("{destination} {left} {comparison_symbol} {right}") format!("R{destination} {left} {comparison_symbol} {right}")
} }
Operation::LESS_EQUAL => { Operation::LessEqual => {
let LessEqual { let LessEqual {
destination, destination,
value, value,
@ -582,25 +603,25 @@ impl Instruction {
} = LessEqual::from(self); } = LessEqual::from(self);
let comparison_symbol = if value { "<=" } else { ">" }; let comparison_symbol = if value { "<=" } else { ">" };
format!("{destination} {left} {comparison_symbol} {right}") format!("R{destination} {left} {comparison_symbol} {right}")
} }
Operation::NEGATE => { Operation::Negate => {
let Negate { let Negate {
destination, destination,
argument, argument,
} = Negate::from(self); } = Negate::from(self);
format!("{destination} = -{argument}") format!("R{destination} = -{argument}")
} }
Operation::NOT => { Operation::Not => {
let Not { let Not {
destination, destination,
argument, argument,
} = Not::from(self); } = Not::from(self);
format!("{destination} = !{argument}") format!("R{destination} = !{argument}")
} }
Operation::JUMP => { Operation::Jump => {
let Jump { let Jump {
offset, offset,
is_positive, is_positive,
@ -612,33 +633,33 @@ impl Instruction {
format!("JUMP -{offset}") format!("JUMP -{offset}")
} }
} }
Operation::CALL => { Operation::Call => {
let Call { let Call {
destination, destination,
function, function,
argument_count, argument_count,
} = Call::from(self); } = Call::from(self);
let arguments_start = destination.index().saturating_sub(argument_count); let arguments_start = destination.saturating_sub(argument_count);
let arguments_end = arguments_start + argument_count; let arguments_end = arguments_start + argument_count;
format!("{destination} = {function}(R{arguments_start}..R{arguments_end})") format!("R{destination} = {function}(R{arguments_start}..R{arguments_end})")
} }
Operation::CALL_NATIVE => { Operation::CallNative => {
let CallNative { let CallNative {
destination, destination,
function, function,
argument_count, argument_count,
} = CallNative::from(self); } = CallNative::from(self);
let arguments_start = destination.index().saturating_sub(argument_count); let arguments_start = destination.saturating_sub(argument_count);
let arguments_end = arguments_start + argument_count; let arguments_end = arguments_start + argument_count;
if function.returns_value() { if function.returns_value() {
format!("{destination} = {function}(R{arguments_start}..R{arguments_end})") format!("R{destination} = {function}(R{arguments_start}..R{arguments_end})")
} else { } else {
format!("{function}(R{arguments_start}..R{arguments_end})") format!("{function}(R{arguments_start}..R{arguments_end})")
} }
} }
Operation::RETURN => { Operation::Return => {
let Return { let Return {
should_return_value, should_return_value,
} = Return::from(self); } = Return::from(self);
@ -651,7 +672,7 @@ impl Instruction {
} }
_ => { _ => {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
panic!("Unknown operation {}", self.operation); panic!("Unknown operation {}", self.operation());
} else { } else {
"RETURN".to_string() "RETURN".to_string()
} }
@ -662,61 +683,20 @@ impl Instruction {
impl Debug for Instruction { impl Debug for Instruction {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{} {}", self.operation, self.disassembly_info()) write!(f, "{} {}", self.operation(), self.disassembly_info())
}
}
#[derive(Debug, Clone, Copy)]
pub enum Destination {
Local(u16),
Register(u16),
}
impl Destination {
pub fn index(&self) -> u16 {
match self {
Destination::Local(index) => *index,
Destination::Register(index) => *index,
}
}
pub fn is_local(&self) -> bool {
matches!(self, Destination::Local(_))
}
pub fn is_register(&self) -> bool {
matches!(self, Destination::Register(_))
}
pub fn as_index_and_a_options(&self) -> (u16, InstructionOptions) {
match self {
Destination::Local(index) => (*index, InstructionOptions::A_IS_LOCAL),
Destination::Register(index) => (*index, InstructionOptions::empty()),
}
}
}
impl Display for Destination {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Destination::Local(index) => write!(f, "L{index}"),
Destination::Register(index) => write!(f, "R{index}"),
}
} }
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum Argument { pub enum Argument {
Constant(u16), Constant(u8),
Local(u16), Register(u8),
Register(u16),
} }
impl Argument { impl Argument {
pub fn index(&self) -> u16 { pub fn index(&self) -> u8 {
match self { match self {
Argument::Constant(index) => *index, Argument::Constant(index) => *index,
Argument::Local(index) => *index,
Argument::Register(index) => *index, Argument::Register(index) => *index,
} }
} }
@ -725,26 +705,20 @@ impl Argument {
matches!(self, Argument::Constant(_)) matches!(self, Argument::Constant(_))
} }
pub fn is_local(&self) -> bool {
matches!(self, Argument::Local(_))
}
pub fn is_register(&self) -> bool { pub fn is_register(&self) -> bool {
matches!(self, Argument::Register(_)) matches!(self, Argument::Register(_))
} }
pub fn as_index_and_b_options(&self) -> (u16, InstructionOptions) { pub fn as_index_and_b_options(&self) -> (u8, InstructionOptions) {
match self { match self {
Argument::Constant(index) => (*index, InstructionOptions::B_IS_CONSTANT), Argument::Constant(index) => (*index, InstructionOptions::B_IS_CONSTANT),
Argument::Local(index) => (*index, InstructionOptions::B_IS_LOCAL),
Argument::Register(index) => (*index, InstructionOptions::empty()), Argument::Register(index) => (*index, InstructionOptions::empty()),
} }
} }
pub fn as_index_and_c_options(&self) -> (u16, InstructionOptions) { pub fn as_index_and_c_options(&self) -> (u8, InstructionOptions) {
match self { match self {
Argument::Constant(index) => (*index, InstructionOptions::C_IS_CONSTANT), Argument::Constant(index) => (*index, InstructionOptions::C_IS_CONSTANT),
Argument::Local(index) => (*index, InstructionOptions::C_IS_LOCAL),
Argument::Register(index) => (*index, InstructionOptions::empty()), Argument::Register(index) => (*index, InstructionOptions::empty()),
} }
} }
@ -754,8 +728,27 @@ impl Display for Argument {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Argument::Constant(index) => write!(f, "C{index}"), Argument::Constant(index) => write!(f, "C{index}"),
Argument::Local(index) => write!(f, "L{index}"),
Argument::Register(index) => write!(f, "R{index}"), Argument::Register(index) => write!(f, "R{index}"),
} }
} }
} }
#[cfg(test)]
mod tests {
use std::mem::offset_of;
use super::*;
#[test]
fn instruction_is_8_bytes() {
assert_eq!(size_of::<Instruction>(), 8);
}
#[test]
fn instruction_layout() {
assert_eq!(offset_of!(Instruction, a), 0);
assert_eq!(offset_of!(Instruction, b), 1);
assert_eq!(offset_of!(Instruction, c), 2);
assert_eq!(offset_of!(Instruction, metadata), 3);
}
}

View File

@ -1,14 +1,14 @@
use crate::{Argument, Destination, Instruction, Operation}; use crate::{Argument, Instruction, Operation};
pub struct Modulo { pub struct Modulo {
pub destination: Destination, pub destination: u8,
pub left: Argument, pub left: Argument,
pub right: Argument, pub right: Argument,
} }
impl From<&Instruction> for Modulo { impl From<&Instruction> for Modulo {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
Modulo { Modulo {
@ -21,16 +21,11 @@ impl From<&Instruction> for Modulo {
impl From<Modulo> for Instruction { impl From<Modulo> for Instruction {
fn from(modulo: Modulo) -> Self { fn from(modulo: Modulo) -> Self {
let (a, a_options) = modulo.destination.as_index_and_a_options(); let a = modulo.destination;
let (b, b_options) = modulo.left.as_index_and_b_options(); let (b, b_options) = modulo.left.as_index_and_b_options();
let (c, c_options) = modulo.right.as_index_and_c_options(); let (c, c_options) = modulo.right.as_index_and_c_options();
let metadata = Operation::Modulo as u8 | b_options.bits() | c_options.bits();
Instruction { Instruction { metadata, a, b, c }
operation: Operation::MODULO,
options: a_options | b_options | c_options,
a,
b,
c,
}
} }
} }

View File

@ -1,10 +1,8 @@
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionOptions;
pub struct Move { pub struct Move {
pub from: u16, pub from: u8,
pub to: u16, pub to: u8,
} }
impl From<&Instruction> for Move { impl From<&Instruction> for Move {
@ -18,12 +16,11 @@ impl From<&Instruction> for Move {
impl From<Move> for Instruction { impl From<Move> for Instruction {
fn from(r#move: Move) -> Self { fn from(r#move: Move) -> Self {
Instruction { let metadata = Operation::Move as u8;
operation: Operation::MOVE, let a = 0;
options: InstructionOptions::empty(), let b = r#move.from;
a: 0, let c = r#move.to;
b: r#move.from,
c: r#move.to, Instruction { metadata, a, b, c }
}
} }
} }

View File

@ -1,14 +1,14 @@
use crate::{Argument, Destination, Instruction, Operation}; use crate::{Argument, Instruction, Operation};
pub struct Multiply { pub struct Multiply {
pub destination: Destination, pub destination: u8,
pub left: Argument, pub left: Argument,
pub right: Argument, pub right: Argument,
} }
impl From<&Instruction> for Multiply { impl From<&Instruction> for Multiply {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
Multiply { Multiply {
@ -21,16 +21,11 @@ impl From<&Instruction> for Multiply {
impl From<Multiply> for Instruction { impl From<Multiply> for Instruction {
fn from(multiply: Multiply) -> Self { fn from(multiply: Multiply) -> Self {
let (a, a_options) = multiply.destination.as_index_and_a_options(); let a = multiply.destination;
let (b, b_options) = multiply.left.as_index_and_b_options(); let (b, b_options) = multiply.left.as_index_and_b_options();
let (c, c_options) = multiply.right.as_index_and_c_options(); let (c, c_options) = multiply.right.as_index_and_c_options();
let metadata = Operation::Multiply as u8 | b_options.bits() | c_options.bits();
Instruction { Instruction { metadata, a, b, c }
operation: Operation::MULTIPLY,
options: a_options | b_options | c_options,
a,
b,
c,
}
} }
} }

View File

@ -1,13 +1,13 @@
use crate::{Argument, Destination, Instruction, Operation}; use crate::{Argument, Instruction, Operation};
pub struct Negate { pub struct Negate {
pub destination: Destination, pub destination: u8,
pub argument: Argument, pub argument: Argument,
} }
impl From<&Instruction> for Negate { impl From<&Instruction> for Negate {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let argument = instruction.b_as_argument(); let argument = instruction.b_as_argument();
Negate { Negate {
@ -19,15 +19,11 @@ impl From<&Instruction> for Negate {
impl From<Negate> for Instruction { impl From<Negate> for Instruction {
fn from(negate: Negate) -> Self { fn from(negate: Negate) -> Self {
let (a, a_options) = negate.destination.as_index_and_a_options(); let a = negate.destination;
let (b, b_options) = negate.argument.as_index_and_b_options(); let (b, b_options) = negate.argument.as_index_and_b_options();
let c = 0;
let metadata = Operation::Negate as u8 | b_options.bits();
Instruction { Instruction { metadata, a, b, c }
operation: Operation::NEGATE,
options: a_options | b_options,
a,
b,
c: 0,
}
} }
} }

View File

@ -1,13 +1,13 @@
use crate::{Argument, Destination, Instruction, Operation}; use crate::{Argument, Instruction, Operation};
pub struct Not { pub struct Not {
pub destination: Destination, pub destination: u8,
pub argument: Argument, pub argument: Argument,
} }
impl From<&Instruction> for Not { impl From<&Instruction> for Not {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let argument = instruction.b_as_argument(); let argument = instruction.b_as_argument();
Not { Not {
@ -19,15 +19,11 @@ impl From<&Instruction> for Not {
impl From<Not> for Instruction { impl From<Not> for Instruction {
fn from(not: Not) -> Self { fn from(not: Not) -> Self {
let (a, a_options) = not.destination.as_index_and_a_options(); let a = not.destination;
let (b, b_options) = not.argument.as_index_and_b_options(); let (b, b_options) = not.argument.as_index_and_b_options();
let c = 0;
let metadata = Operation::Not as u8 | b_options.bits();
Instruction { Instruction { metadata, a, b, c }
operation: Operation::NOT,
options: a_options | b_options,
a,
b,
c: 0,
}
} }
} }

View File

@ -4,75 +4,141 @@ use std::fmt::{self, Debug, Display, Formatter};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub const MOVE_BYTE: u8 = 0;
pub const CLOSE_BYTE: u8 = 1;
pub const LOAD_BOOLEAN_BYTE: u8 = 2;
pub const LOAD_CONSTANT_BYTE: u8 = 3;
pub const LOAD_LIST_BYTE: u8 = 4;
pub const LOAD_MAP_BYTE: u8 = 5;
pub const LOAD_SELF_BYTE: u8 = 6;
pub const GET_LOCAL_BYTE: u8 = 7;
pub const SET_LOCAL_BYTE: u8 = 8;
pub const ADD_BYTE: u8 = 9;
pub const SUBTRACT_BYTE: u8 = 10;
pub const MULTIPLY_BYTE: u8 = 11;
pub const DIVIDE_BYTE: u8 = 12;
pub const MODULO_BYTE: u8 = 13;
pub const POWER_BYTE: u8 = 14;
pub const TEST_BYTE: u8 = 15;
pub const TEST_SET_BYTE: u8 = 16;
pub const EQUAL_BYTE: u8 = 17;
pub const LESS_BYTE: u8 = 18;
pub const LESS_EQUAL_BYTE: u8 = 19;
pub const NEGATE_BYTE: u8 = 20;
pub const NOT_BYTE: u8 = 21;
pub const CALL_BYTE: u8 = 22;
pub const CALL_NATIVE_BYTE: u8 = 23;
pub const JUMP_BYTE: u8 = 24;
pub const RETURN_BYTE: u8 = 25;
/// Part of an [Instruction][crate::Instruction] that is encoded as a single byte.
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Operation(u8); #[repr(u8)]
pub enum Operation {
Move = MOVE_BYTE,
Close = CLOSE_BYTE,
LoadBoolean = LOAD_BOOLEAN_BYTE,
LoadConstant = LOAD_CONSTANT_BYTE,
LoadList = LOAD_LIST_BYTE,
LoadMap = LOAD_MAP_BYTE,
LoadSelf = LOAD_SELF_BYTE,
GetLocal = GET_LOCAL_BYTE,
SetLocal = SET_LOCAL_BYTE,
Add = ADD_BYTE,
Subtract = SUBTRACT_BYTE,
Multiply = MULTIPLY_BYTE,
Divide = DIVIDE_BYTE,
Modulo = MODULO_BYTE,
Power = POWER_BYTE,
Test = TEST_BYTE,
TestSet = TEST_SET_BYTE,
Equal = EQUAL_BYTE,
Less = LESS_BYTE,
LessEqual = LESS_EQUAL_BYTE,
Negate = NEGATE_BYTE,
Not = NOT_BYTE,
Call = CALL_BYTE,
CallNative = CALL_NATIVE_BYTE,
Jump = JUMP_BYTE,
Return = RETURN_BYTE,
}
impl Operation { impl From<u8> for Operation {
pub const MOVE: Operation = Operation(0); fn from(byte: u8) -> Self {
pub const CLOSE: Operation = Operation(1); match byte {
pub const LOAD_BOOLEAN: Operation = Operation(2); MOVE_BYTE => Self::Move,
pub const LOAD_CONSTANT: Operation = Operation(3); CLOSE_BYTE => Self::Close,
pub const LOAD_LIST: Operation = Operation(4); LOAD_BOOLEAN_BYTE => Self::LoadBoolean,
pub const LOAD_SELF: Operation = Operation(5); LOAD_CONSTANT_BYTE => Self::LoadConstant,
pub const GET_LOCAL: Operation = Operation(6); LOAD_LIST_BYTE => Self::LoadList,
pub const SET_LOCAL: Operation = Operation(7); LOAD_MAP_BYTE => Self::LoadMap,
pub const ADD: Operation = Operation(8); LOAD_SELF_BYTE => Self::LoadSelf,
pub const SUBTRACT: Operation = Operation(9); GET_LOCAL_BYTE => Self::GetLocal,
pub const MULTIPLY: Operation = Operation(10); SET_LOCAL_BYTE => Self::SetLocal,
pub const DIVIDE: Operation = Operation(11); ADD_BYTE => Self::Add,
pub const MODULO: Operation = Operation(12); SUBTRACT_BYTE => Self::Subtract,
pub const TEST: Operation = Operation(13); MULTIPLY_BYTE => Self::Multiply,
pub const TEST_SET: Operation = Operation(14); DIVIDE_BYTE => Self::Divide,
pub const EQUAL: Operation = Operation(15); MODULO_BYTE => Self::Modulo,
pub const LESS: Operation = Operation(16); POWER_BYTE => Self::Power,
pub const LESS_EQUAL: Operation = Operation(17); TEST_BYTE => Self::Test,
pub const NEGATE: Operation = Operation(18); TEST_SET_BYTE => Self::TestSet,
pub const NOT: Operation = Operation(19); EQUAL_BYTE => Self::Equal,
pub const CALL: Operation = Operation(20); LESS_BYTE => Self::Less,
pub const CALL_NATIVE: Operation = Operation(21); LESS_EQUAL_BYTE => Self::LessEqual,
pub const JUMP: Operation = Operation(22); NEGATE_BYTE => Self::Negate,
pub const RETURN: Operation = Operation(23); NOT_BYTE => Self::Not,
CALL_BYTE => Self::Call,
pub fn name(self) -> &'static str { CALL_NATIVE_BYTE => Self::CallNative,
match self { JUMP_BYTE => Self::Jump,
Self::MOVE => "MOVE", RETURN_BYTE => Self::Return,
Self::CLOSE => "CLOSE",
Self::LOAD_BOOLEAN => "LOAD_BOOLEAN",
Self::LOAD_CONSTANT => "LOAD_CONSTANT",
Self::LOAD_LIST => "LOAD_LIST",
Self::LOAD_SELF => "LOAD_SELF",
Self::GET_LOCAL => "GET_LOCAL",
Self::SET_LOCAL => "SET_LOCAL",
Self::ADD => "ADD",
Self::SUBTRACT => "SUBTRACT",
Self::MULTIPLY => "MULTIPLY",
Self::DIVIDE => "DIVIDE",
Self::MODULO => "MODULO",
Self::TEST => "TEST",
Self::TEST_SET => "TEST_SET",
Self::EQUAL => "EQUAL",
Self::LESS => "LESS",
Self::LESS_EQUAL => "LESS_EQUAL",
Self::NEGATE => "NEGATE",
Self::NOT => "NOT",
Self::CALL => "CALL",
Self::CALL_NATIVE => "CALL_NATIVE",
Self::JUMP => "JUMP",
Self::RETURN => "RETURN",
_ => { _ => {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
panic!("Unknown operation code {}", self.0); panic!("Invalid operation byte: {}", byte)
} else { } else {
"RETURN" Self::Return
} }
} }
} }
} }
} }
impl Operation {
pub fn name(self) -> &'static str {
match self {
Self::Move => "MOVE",
Self::Close => "CLOSE",
Self::LoadBoolean => "LOAD_BOOLEAN",
Self::LoadConstant => "LOAD_CONSTANT",
Self::LoadList => "LOAD_LIST",
Self::LoadMap => "LOAD_MAP",
Self::LoadSelf => "LOAD_SELF",
Self::GetLocal => "GET_LOCAL",
Self::SetLocal => "SET_LOCAL",
Self::Add => "ADD",
Self::Subtract => "SUBTRACT",
Self::Multiply => "MULTIPLY",
Self::Divide => "DIVIDE",
Self::Modulo => "MODULO",
Self::Power => "POWER",
Self::Test => "TEST",
Self::TestSet => "TEST_SET",
Self::Equal => "EQUAL",
Self::Less => "LESS",
Self::LessEqual => "LESS_EQUAL",
Self::Negate => "NEGATE",
Self::Not => "NOT",
Self::Call => "CALL",
Self::CallNative => "CALL_NATIVE",
Self::Jump => "JUMP",
Self::Return => "RETURN",
}
}
}
impl Debug for Operation { impl Debug for Operation {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Operation({})", self.name()) write!(f, "{}", self.name())
} }
} }
@ -81,3 +147,56 @@ impl Display for Operation {
write!(f, "{}", self.name()) write!(f, "{}", self.name())
} }
} }
#[cfg(test)]
mod tests {
use super::*;
const ALL_OPERATIONS: [Operation; 26] = [
Operation::Move,
Operation::Close,
Operation::LoadBoolean,
Operation::LoadConstant,
Operation::LoadList,
Operation::LoadMap,
Operation::LoadSelf,
Operation::GetLocal,
Operation::SetLocal,
Operation::Add,
Operation::Subtract,
Operation::Multiply,
Operation::Divide,
Operation::Modulo,
Operation::Power,
Operation::Test,
Operation::TestSet,
Operation::Equal,
Operation::Less,
Operation::LessEqual,
Operation::Negate,
Operation::Not,
Operation::Call,
Operation::CallNative,
Operation::Jump,
Operation::Return,
];
#[test]
fn operations_are_unique() {
for (i, operation) in ALL_OPERATIONS.into_iter().enumerate() {
assert_eq!(i, operation as usize);
}
}
#[test]
fn operation_uses_five_bits() {
for operation in ALL_OPERATIONS {
assert_eq!(operation as u8 & 0b1110_0000, 0);
}
}
#[test]
fn operation_is_one_byte() {
assert_eq!(size_of::<Operation>(), 1);
}
}

View File

@ -1,42 +1,33 @@
//! Byte that uses its bits as boolean flags to represent information about an instruction's
//! arguments. Additionally, one bit is used as the instruction's `D` field.
//!
//! See the [instruction documentation](crate::instruction) for more information.
use bitflags::bitflags; use bitflags::bitflags;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
bitflags! { bitflags! {
/// Byte that uses its bits as boolean flags to represent an instruction's options and D field.
///
/// See the [instruction documentation](crate::instruction) for more information.
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct InstructionOptions: u8 { pub struct InstructionOptions: u8 {
const A_IS_LOCAL = 0b00000001; const B_IS_CONSTANT = 0b0010_0000;
const B_IS_CONSTANT = 0b00000010; const C_IS_CONSTANT = 0b0100_0000;
const B_IS_LOCAL = 0b00000100;
const C_IS_CONSTANT = 0b00001000; const D_IS_TRUE = 0b1000_0000;
const C_IS_LOCAL = 0b00010000;
const D_IS_TRUE = 0b00100000;
} }
} }
impl InstructionOptions { impl InstructionOptions {
pub fn a_is_local(self) -> bool {
self.contains(Self::A_IS_LOCAL)
}
pub fn b_is_constant(self) -> bool { pub fn b_is_constant(self) -> bool {
self.contains(Self::B_IS_CONSTANT) self.contains(Self::B_IS_CONSTANT)
} }
pub fn b_is_local(self) -> bool {
self.contains(Self::B_IS_LOCAL)
}
pub fn c_is_constant(self) -> bool { pub fn c_is_constant(self) -> bool {
self.contains(Self::C_IS_CONSTANT) self.contains(Self::C_IS_CONSTANT)
} }
pub fn c_is_local(self) -> bool {
self.contains(Self::C_IS_LOCAL)
}
pub fn d(self) -> bool { pub fn d(self) -> bool {
self.contains(Self::D_IS_TRUE) self.contains(Self::D_IS_TRUE)
} }

View File

@ -1,7 +1,5 @@
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionOptions;
pub struct Return { pub struct Return {
pub should_return_value: bool, pub should_return_value: bool,
} }
@ -18,14 +16,11 @@ impl From<&Instruction> for Return {
impl From<Return> for Instruction { impl From<Return> for Instruction {
fn from(r#return: Return) -> Self { fn from(r#return: Return) -> Self {
let b = r#return.should_return_value as u16; let metadata = Operation::Return as u8;
let a = 0;
let b = r#return.should_return_value as u8;
let c = 0;
Instruction { Instruction { metadata, a, b, c }
operation: Operation::RETURN,
options: InstructionOptions::empty(),
a: 0,
b,
c: 0,
}
} }
} }

View File

@ -1,10 +1,8 @@
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionOptions;
pub struct SetLocal { pub struct SetLocal {
pub register_index: u16, pub register_index: u8,
pub local_index: u16, pub local_index: u8,
} }
impl From<&Instruction> for SetLocal { impl From<&Instruction> for SetLocal {
@ -21,15 +19,11 @@ impl From<&Instruction> for SetLocal {
impl From<SetLocal> for Instruction { impl From<SetLocal> for Instruction {
fn from(set_local: SetLocal) -> Self { fn from(set_local: SetLocal) -> Self {
let metadata = Operation::SetLocal as u8;
let a = 0;
let b = set_local.register_index; let b = set_local.register_index;
let c = set_local.local_index; let c = set_local.local_index;
Instruction { Instruction { metadata, a, b, c }
operation: Operation::SET_LOCAL,
options: InstructionOptions::empty(),
a: 0,
b,
c,
}
} }
} }

View File

@ -1,14 +1,14 @@
use crate::{Argument, Destination, Instruction, Operation}; use crate::{Argument, Instruction, Operation};
pub struct Subtract { pub struct Subtract {
pub destination: Destination, pub destination: u8,
pub left: Argument, pub left: Argument,
pub right: Argument, pub right: Argument,
} }
impl From<&Instruction> for Subtract { impl From<&Instruction> for Subtract {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
Subtract { Subtract {
@ -21,16 +21,11 @@ impl From<&Instruction> for Subtract {
impl From<Subtract> for Instruction { impl From<Subtract> for Instruction {
fn from(subtract: Subtract) -> Self { fn from(subtract: Subtract) -> Self {
let (a, a_options) = subtract.destination.as_index_and_a_options(); let a = subtract.destination;
let (b, b_options) = subtract.left.as_index_and_b_options(); let (b, b_options) = subtract.left.as_index_and_b_options();
let (c, c_options) = subtract.right.as_index_and_c_options(); let (c, c_options) = subtract.right.as_index_and_c_options();
let metadata = Operation::Subtract as u8 | b_options.bits() | c_options.bits();
Instruction { Instruction { metadata, a, b, c }
operation: Operation::SUBTRACT,
options: a_options | b_options | c_options,
a,
b,
c,
}
} }
} }

View File

@ -19,15 +19,11 @@ impl From<&Instruction> for Test {
impl From<Test> for Instruction { impl From<Test> for Instruction {
fn from(test: Test) -> Self { fn from(test: Test) -> Self {
let a = 0;
let (b, options) = test.argument.as_index_and_b_options(); let (b, options) = test.argument.as_index_and_b_options();
let c = test.test_value as u16; let c = test.test_value as u8;
let metadata = Operation::Test as u8 | options.bits();
Instruction { Instruction { metadata, a, b, c }
operation: Operation::TEST,
options,
a: 0,
b,
c,
}
} }
} }

View File

@ -1,14 +1,14 @@
use crate::{Argument, Destination, Instruction, Operation}; use crate::{Argument, Instruction, Operation};
pub struct TestSet { pub struct TestSet {
pub destination: Destination, pub destination: u8,
pub argument: Argument, pub argument: Argument,
pub test_value: bool, pub test_value: bool,
} }
impl From<&Instruction> for TestSet { impl From<&Instruction> for TestSet {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_as_destination(); let destination = instruction.a;
let argument = instruction.b_as_argument(); let argument = instruction.b_as_argument();
let test_value = instruction.c != 0; let test_value = instruction.c != 0;
@ -22,16 +22,11 @@ impl From<&Instruction> for TestSet {
impl From<TestSet> for Instruction { impl From<TestSet> for Instruction {
fn from(test_set: TestSet) -> Self { fn from(test_set: TestSet) -> Self {
let (a, a_options) = test_set.destination.as_index_and_a_options(); let a = test_set.destination;
let (b, b_options) = test_set.argument.as_index_and_b_options(); let (b, b_options) = test_set.argument.as_index_and_b_options();
let c = test_set.test_value as u16; let c = test_set.test_value as u8;
let metadata = Operation::TestSet as u8 | b_options.bits();
Instruction { Instruction { metadata, a, b, c }
operation: Operation::TEST_SET,
options: a_options | b_options,
a,
b,
c,
}
} }
} }

View File

@ -35,7 +35,6 @@ pub mod dust_error;
pub mod instruction; pub mod instruction;
pub mod lexer; pub mod lexer;
pub mod native_function; pub mod native_function;
pub mod optimize;
pub mod scope; pub mod scope;
pub mod token; pub mod token;
pub mod r#type; pub mod r#type;
@ -46,10 +45,9 @@ 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};
pub use crate::instruction::{Argument, Destination, Instruction, Operation}; pub use crate::instruction::{Argument, Instruction, Operation};
pub use crate::lexer::{lex, LexError, Lexer}; pub use crate::lexer::{lex, LexError, Lexer};
pub use crate::native_function::{NativeFunction, NativeFunctionError}; pub use crate::native_function::{NativeFunction, NativeFunctionError};
pub use crate::optimize::{optimize_control_flow, optimize_set_local};
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict}; pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
pub use crate::scope::Scope; pub use crate::scope::Scope;
pub use crate::token::{write_token_list, Token, TokenKind, TokenOwned}; pub use crate::token::{write_token_list, Token, TokenKind, TokenOwned};

View File

@ -3,7 +3,7 @@ use std::io::{self, stdout, Write};
use crate::{ConcreteValue, Instruction, NativeFunctionError, Value, Vm, VmError}; use crate::{ConcreteValue, Instruction, NativeFunctionError, Value, Vm, VmError};
pub fn panic<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<Value>, VmError> { pub fn panic<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<Value>, VmError> {
let argument_count = instruction.c(); let argument_count = instruction.c;
let message = if argument_count == 0 { let message = if argument_count == 0 {
None None
} else { } else {
@ -34,7 +34,7 @@ pub fn panic<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<Valu
} }
pub fn to_string<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<Value>, VmError> { pub fn to_string<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<Value>, VmError> {
let argument_count = instruction.c(); let argument_count = instruction.c;
if argument_count != 1 { if argument_count != 1 {
return Err(VmError::NativeFunction( return Err(VmError::NativeFunction(
@ -63,7 +63,7 @@ pub fn to_string<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<
} }
pub fn read_line<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<Value>, VmError> { pub fn read_line<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<Value>, VmError> {
let argument_count = instruction.c(); let argument_count = instruction.c;
if argument_count != 0 { if argument_count != 0 {
return Err(VmError::NativeFunction( return Err(VmError::NativeFunction(
@ -91,8 +91,8 @@ pub fn read_line<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<
} }
pub fn write<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<Value>, VmError> { pub fn write<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<Value>, VmError> {
let to_register = instruction.a(); let to_register = instruction.a;
let argument_count = instruction.c(); let argument_count = instruction.c;
let mut stdout = stdout(); let mut stdout = stdout();
let map_err = |io_error: io::Error| { let map_err = |io_error: io::Error| {
VmError::NativeFunction(NativeFunctionError::Io { VmError::NativeFunction(NativeFunctionError::Io {
@ -120,8 +120,8 @@ pub fn write<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<Valu
} }
pub fn write_line<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<Value>, VmError> { pub fn write_line<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<Value>, VmError> {
let to_register = instruction.a(); let to_register = instruction.a;
let argument_count = instruction.c(); let argument_count = instruction.c;
let mut stdout = stdout(); let mut stdout = stdout();
let map_err = |io_error: io::Error| { let map_err = |io_error: io::Error| {
VmError::NativeFunction(NativeFunctionError::Io { VmError::NativeFunction(NativeFunctionError::Io {

View File

@ -75,8 +75,8 @@ macro_rules! define_native_function {
} }
} }
impl From<u16> for NativeFunction { impl From<u8> for NativeFunction {
fn from(bytes: u16) -> Self { fn from(bytes: u8) -> Self {
match bytes { match bytes {
$( $(
$bytes => NativeFunction::$name, $bytes => NativeFunction::$name,

View File

@ -278,13 +278,13 @@ 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<SmallVec<[u16; 4]>>, pub type_parameters: Option<SmallVec<[u8; 4]>>,
pub value_parameters: Option<SmallVec<[(u16, Type); 4]>>, pub value_parameters: Option<SmallVec<[(u8, Type); 4]>>,
pub return_type: Type, pub return_type: Type,
} }
impl FunctionType { impl FunctionType {
pub fn new<T: Into<SmallVec<[u16; 4]>>, U: Into<SmallVec<[(u16, Type); 4]>>>( pub fn new<T: Into<SmallVec<[u8; 4]>>, U: Into<SmallVec<[(u8, Type); 4]>>>(
type_parameters: Option<T>, type_parameters: Option<T>,
value_parameters: Option<U>, value_parameters: Option<U>,
return_type: Type, return_type: Type,

View File

@ -8,8 +8,7 @@ use smallvec::{smallvec, SmallVec};
use crate::{ use crate::{
compile, instruction::*, AbstractValue, AnnotatedError, Argument, Chunk, ConcreteValue, compile, instruction::*, AbstractValue, AnnotatedError, Argument, Chunk, ConcreteValue,
Destination, DustError, Instruction, NativeFunctionError, Operation, Span, Value, ValueError, DustError, Instruction, NativeFunctionError, Operation, Span, Value, ValueError, ValueRef,
ValueRef,
}; };
pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> { pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
@ -28,10 +27,9 @@ pub struct Vm<'a> {
chunk: &'a Chunk, chunk: &'a Chunk,
parent: Option<&'a Vm<'a>>, parent: Option<&'a Vm<'a>>,
local_definitions: SmallVec<[Option<u16>; 16]>,
ip: usize, ip: usize,
last_assigned_register: Option<u16>, last_assigned_register: Option<u8>,
source: &'a str, source: &'a str,
} }
@ -41,7 +39,6 @@ impl<'a> Vm<'a> {
chunk, chunk,
stack: smallvec![Register::Empty; chunk.stack_size()], stack: smallvec![Register::Empty; chunk.stack_size()],
parent, parent,
local_definitions: smallvec![None; chunk.locals().len()],
ip: 0, ip: 0,
last_assigned_register: None, last_assigned_register: None,
source, source,
@ -53,7 +50,10 @@ impl<'a> Vm<'a> {
} }
pub fn current_position(&self) -> Span { pub fn current_position(&self) -> Span {
self.chunk.positions()[self.ip.saturating_sub(1)] let index = self.ip.saturating_sub(1);
let (_, position) = self.chunk.instructions()[index];
position
} }
pub fn run(&mut self) -> Result<Option<ConcreteValue>, VmError> { pub fn run(&mut self) -> Result<Option<ConcreteValue>, VmError> {
@ -100,11 +100,10 @@ impl<'a> Vm<'a> {
value, value,
jump_next, jump_next,
} = LoadBoolean::from(&instruction); } = LoadBoolean::from(&instruction);
let register_index = self.get_destination(destination)?;
let boolean = ConcreteValue::Boolean(value).to_value(); let boolean = ConcreteValue::Boolean(value).to_value();
let register = Register::Value(boolean); let register = Register::Value(boolean);
self.set_register(register_index, register)?; self.set_register(destination, register)?;
if jump_next { if jump_next {
self.jump(1, true); self.jump(1, true);
@ -116,10 +115,9 @@ impl<'a> Vm<'a> {
constant_index, constant_index,
jump_next, jump_next,
} = LoadConstant::from(&instruction); } = LoadConstant::from(&instruction);
let register_index = self.get_destination(destination)?;
let register = Register::Pointer(Pointer::Constant(constant_index)); let register = Register::Pointer(Pointer::Constant(constant_index));
self.set_register(register_index, register)?; self.set_register(destination, register)?;
if jump_next { if jump_next {
self.jump(1, true); self.jump(1, true);
@ -130,10 +128,9 @@ impl<'a> Vm<'a> {
destination, destination,
start_register, start_register,
} = LoadList::from(&instruction); } = LoadList::from(&instruction);
let register_index = self.get_destination(destination)?;
let mut pointers = Vec::new(); let mut pointers = Vec::new();
for register in start_register..register_index { for register in start_register..destination {
if let Some(Register::Empty) = self.stack.get(register as usize) { if let Some(Register::Empty) = self.stack.get(register as usize) {
continue; continue;
} }
@ -146,47 +143,33 @@ impl<'a> Vm<'a> {
let register = let register =
Register::Value(AbstractValue::List { items: pointers }.to_value()); Register::Value(AbstractValue::List { items: pointers }.to_value());
self.set_register(register_index, register)?; self.set_register(destination, register)?;
} }
Operation::LoadSelf => { Operation::LoadSelf => {
let LoadSelf { destination } = LoadSelf::from(&instruction); let LoadSelf { destination } = LoadSelf::from(&instruction);
let register_index = self.get_destination(destination)?;
let register = Register::Value(AbstractValue::FunctionSelf.to_value()); let register = Register::Value(AbstractValue::FunctionSelf.to_value());
self.set_register(register_index, register)?; self.set_register(destination, register)?;
}
Operation::DefineLocal => {
let DefineLocal {
register,
local_index,
is_mutable,
} = DefineLocal::from(&instruction);
self.local_definitions[local_index as usize] = Some(register);
} }
Operation::GetLocal => { Operation::GetLocal => {
let GetLocal { let GetLocal {
destination, destination,
local_index, local_index,
} = GetLocal::from(&instruction); } = GetLocal::from(&instruction);
let register_index = self.get_destination(destination)?; let local_register = self.get_local_register(local_index)?;
let local_register = self.local_definitions[local_index as usize].ok_or(
VmError::UndefinedLocal {
local_index,
position: self.current_position(),
},
)?;
let register = Register::Pointer(Pointer::Stack(local_register)); let register = Register::Pointer(Pointer::Stack(local_register));
self.set_register(register_index, register)?; self.set_register(destination, register)?;
} }
Operation::SetLocal => { Operation::SetLocal => {
let SetLocal { let SetLocal {
register_index: register, register_index,
local_index, local_index,
} = SetLocal::from(&instruction); } = SetLocal::from(&instruction);
let local_register_index = self.get_local_register(local_index)?;
let register = Register::Pointer(Pointer::Stack(register_index));
self.local_definitions[local_index as usize] = Some(register); self.set_register(local_register_index, register);
} }
Operation::Add => { Operation::Add => {
let Add { let Add {
@ -194,26 +177,20 @@ impl<'a> Vm<'a> {
left, left,
right, right,
} = Add::from(&instruction); } = Add::from(&instruction);
let register_index = self.get_destination(destination)?;
let left = self.get_argument(left)?; let left = self.get_argument(left)?;
let right = self.get_argument(right)?; let right = self.get_argument(right)?;
let sum_result = left.add(right); let sum_result = left.add(right);
let sum = match sum_result {
Ok(sum) => sum,
Err(error) => {
return Err(VmError::Value {
error,
position: self.current_position(),
});
}
};
assert!( self.set_register(destination, Register::Value(sum))?;
sum_result.is_ok(),
"VM Error: {}",
DustError::runtime(
VmError::Value {
error: sum_result.unwrap_err(),
position: self.current_position()
},
self.source
)
);
let sum = sum_result.unwrap();
self.set_register(register_index, Register::Value(sum))?;
} }
Operation::Subtract => { Operation::Subtract => {
let Subtract { let Subtract {
@ -221,15 +198,20 @@ impl<'a> Vm<'a> {
left, left,
right, right,
} = Subtract::from(&instruction); } = Subtract::from(&instruction);
let register_index = self.get_destination(destination)?;
let left = self.get_argument(left)?; let left = self.get_argument(left)?;
let right = self.get_argument(right)?; let right = self.get_argument(right)?;
let difference = left.subtract(right).map_err(|error| VmError::Value { let subtraction_result = left.subtract(right);
let difference = match subtraction_result {
Ok(difference) => difference,
Err(error) => {
return Err(VmError::Value {
error, error,
position: self.current_position(), position: self.current_position(),
})?; });
}
};
self.set_register(register_index, Register::Value(difference))?; self.set_register(destination, Register::Value(difference))?;
} }
Operation::Multiply => { Operation::Multiply => {
let Multiply { let Multiply {
@ -237,15 +219,20 @@ impl<'a> Vm<'a> {
left, left,
right, right,
} = Multiply::from(&instruction); } = Multiply::from(&instruction);
let register_index = self.get_destination(destination)?;
let left = self.get_argument(left)?; let left = self.get_argument(left)?;
let right = self.get_argument(right)?; let right = self.get_argument(right)?;
let product = left.multiply(right).map_err(|error| VmError::Value { let multiplication_result = left.multiply(right);
let product = match multiplication_result {
Ok(product) => product,
Err(error) => {
return Err(VmError::Value {
error, error,
position: self.current_position(), position: self.current_position(),
})?; });
}
};
self.set_register(register_index, Register::Value(product))?; self.set_register(destination, Register::Value(product))?;
} }
Operation::Divide => { Operation::Divide => {
let Divide { let Divide {
@ -253,15 +240,20 @@ impl<'a> Vm<'a> {
left, left,
right, right,
} = Divide::from(&instruction); } = Divide::from(&instruction);
let register_index = self.get_destination(destination)?;
let left = self.get_argument(left)?; let left = self.get_argument(left)?;
let right = self.get_argument(right)?; let right = self.get_argument(right)?;
let quotient = left.divide(right).map_err(|error| VmError::Value { let division_result = left.divide(right);
let quotient = match division_result {
Ok(quotient) => quotient,
Err(error) => {
return Err(VmError::Value {
error, error,
position: self.current_position(), position: self.current_position(),
})?; });
}
};
self.set_register(register_index, Register::Value(quotient))?; self.set_register(destination, Register::Value(quotient))?;
} }
Operation::Modulo => { Operation::Modulo => {
let Modulo { let Modulo {
@ -269,15 +261,20 @@ impl<'a> Vm<'a> {
left, left,
right, right,
} = Modulo::from(&instruction); } = Modulo::from(&instruction);
let register_index = self.get_destination(destination)?;
let left = self.get_argument(left)?; let left = self.get_argument(left)?;
let right = self.get_argument(right)?; let right = self.get_argument(right)?;
let remainder = left.modulo(right).map_err(|error| VmError::Value { let modulo_result = left.modulo(right);
let remainder = match modulo_result {
Ok(remainder) => remainder,
Err(error) => {
return Err(VmError::Value {
error, error,
position: self.current_position(), position: self.current_position(),
})?; });
}
};
self.set_register(register_index, Register::Value(remainder))?; self.set_register(destination, Register::Value(remainder))?;
} }
Operation::Test => { Operation::Test => {
let Test { let Test {
@ -305,7 +302,6 @@ impl<'a> Vm<'a> {
argument, argument,
test_value, test_value,
} = TestSet::from(&instruction); } = TestSet::from(&instruction);
let register_index = self.get_destination(destination)?;
let value = self.get_argument(argument)?; let value = self.get_argument(argument)?;
let boolean = if let ValueRef::Concrete(ConcreteValue::Boolean(boolean)) = value let boolean = if let ValueRef::Concrete(ConcreteValue::Boolean(boolean)) = value
{ {
@ -322,24 +318,20 @@ impl<'a> Vm<'a> {
} else { } else {
let pointer = match argument { let pointer = match argument {
Argument::Constant(constant_index) => Pointer::Constant(constant_index), Argument::Constant(constant_index) => Pointer::Constant(constant_index),
Argument::Local(local_index) => {
let register_index = self.local_definitions[local_index as usize]
.ok_or(VmError::UndefinedLocal {
local_index,
position: self.current_position(),
})?;
Pointer::Stack(register_index)
}
Argument::Register(register_index) => Pointer::Stack(register_index), Argument::Register(register_index) => Pointer::Stack(register_index),
}; };
let register = Register::Pointer(pointer); let register = Register::Pointer(pointer);
self.set_register(register_index, register)?; self.set_register(destination, register)?;
} }
} }
Operation::Equal => { Operation::Equal => {
let Equal { value, left, right } = Equal::from(&instruction); let Equal {
destination,
value,
left,
right,
} = Equal::from(&instruction);
let left = self.get_argument(left)?; let left = self.get_argument(left)?;
let right = self.get_argument(right)?; let right = self.get_argument(right)?;
let equal_result = left.equal(right).map_err(|error| VmError::Value { let equal_result = left.equal(right).map_err(|error| VmError::Value {
@ -355,64 +347,79 @@ impl<'a> Vm<'a> {
position: self.current_position(), position: self.current_position(),
}); });
}; };
let comparison = is_equal == value;
let register =
Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
if is_equal == value { self.set_register(destination, register)?;
self.jump(1, true);
}
} }
Operation::Less => { Operation::Less => {
let Less { value, left, right } = Less::from(&instruction); let Less {
destination,
value,
left,
right,
} = Less::from(&instruction);
let left = self.get_argument(left)?; let left = self.get_argument(left)?;
let right = self.get_argument(right)?; let right = self.get_argument(right)?;
let less_result = left.less_than(right); let less_result = left.less_than(right);
let less_than_value = match less_result {
assert!( Ok(value) => value,
less_result.is_ok(), Err(error) => {
"VM Error: {}\nPosition: {}", return Err(VmError::Value {
less_result.unwrap_err(),
self.current_position()
);
let less_than_value = less_result.unwrap();
let less_than_boolean = less_than_value.as_boolean();
assert!(
less_than_boolean.is_some(),
"VM Error: Expected a boolean\nPosition: {}",
self.current_position()
);
let is_less_than = less_than_boolean.unwrap();
if is_less_than == &value {
self.jump(1, true);
}
}
Operation::LessEqual => {
let LessEqual { value, left, right } = LessEqual::from(&instruction);
let left = self.get_argument(left)?;
let right = self.get_argument(right)?;
let less_or_equal_result =
left.less_than_or_equal(right)
.map_err(|error| VmError::Value {
error, error,
position: self.current_position(), position: self.current_position(),
})?; });
let is_less_than_or_equal = }
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = };
less_or_equal_result let is_less_than = match less_than_value {
{ Value::Concrete(ConcreteValue::Boolean(boolean)) => boolean,
boolean _ => {
} else {
return Err(VmError::ExpectedBoolean { return Err(VmError::ExpectedBoolean {
found: less_or_equal_result, found: less_than_value,
position: self.current_position(), position: self.current_position(),
}); });
};
if is_less_than_or_equal == value {
self.jump(1, true);
} }
};
let comparison = is_less_than == value;
let register =
Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
self.set_register(destination, register)?;
}
Operation::LessEqual => {
let LessEqual {
destination,
value,
left,
right,
} = LessEqual::from(&instruction);
let left = self.get_argument(left)?;
let right = self.get_argument(right)?;
let less_or_equal_result = left.less_than_or_equal(right);
let less_or_equal_value = match less_or_equal_result {
Ok(value) => value,
Err(error) => {
return Err(VmError::Value {
error,
position: self.current_position(),
});
}
};
let is_less_than_or_equal = match less_or_equal_value {
Value::Concrete(ConcreteValue::Boolean(boolean)) => boolean,
_ => {
return Err(VmError::ExpectedBoolean {
found: less_or_equal_value,
position: self.current_position(),
});
}
};
let comparison = is_less_than_or_equal == value;
let register =
Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
self.set_register(destination, register)?;
} }
Operation::Negate => { Operation::Negate => {
let Negate { let Negate {
@ -424,10 +431,9 @@ impl<'a> Vm<'a> {
error, error,
position: self.current_position(), position: self.current_position(),
})?; })?;
let register_index = self.get_destination(destination)?;
let register = Register::Value(negated); let register = Register::Value(negated);
self.set_register(register_index, register)?; self.set_register(destination, register)?;
} }
Operation::Not => { Operation::Not => {
let Not { let Not {
@ -439,10 +445,9 @@ impl<'a> Vm<'a> {
error, error,
position: self.current_position(), position: self.current_position(),
})?; })?;
let register_index = self.get_destination(destination)?;
let register = Register::Value(not); let register = Register::Value(not);
self.set_register(register_index, register)?; self.set_register(destination, register)?;
} }
Operation::Jump => { Operation::Jump => {
let Jump { let Jump {
@ -458,7 +463,6 @@ impl<'a> Vm<'a> {
function, function,
argument_count, argument_count,
} = Call::from(&instruction); } = Call::from(&instruction);
let register_index = self.get_destination(destination)?;
let function = self.get_argument(function)?; let function = self.get_argument(function)?;
let chunk = if let ValueRef::Concrete(ConcreteValue::Function(chunk)) = function let chunk = if let ValueRef::Concrete(ConcreteValue::Function(chunk)) = function
{ {
@ -472,17 +476,15 @@ impl<'a> Vm<'a> {
}); });
}; };
let mut function_vm = Vm::new(self.source, chunk, Some(self)); let mut function_vm = Vm::new(self.source, chunk, Some(self));
let first_argument_index = register_index - argument_count; let first_argument_index = destination - argument_count;
for (argument_index, argument_register_index) in for (argument_index, argument_register_index) in
(first_argument_index..register_index).enumerate() (first_argument_index..destination).enumerate()
{ {
function_vm.set_register( function_vm.set_register(
argument_index as u16, argument_index as u8,
Register::Pointer(Pointer::ParentStack(argument_register_index)), Register::Pointer(Pointer::ParentStack(argument_register_index)),
)?; )?;
function_vm.local_definitions[argument_index] = Some(argument_index as u16);
} }
let return_value = function_vm.run()?; let return_value = function_vm.run()?;
@ -490,7 +492,7 @@ impl<'a> Vm<'a> {
if let Some(concrete_value) = return_value { if let Some(concrete_value) = return_value {
let register = Register::Value(concrete_value.to_value()); let register = Register::Value(concrete_value.to_value());
self.set_register(register_index, register)?; self.set_register(destination, register)?;
} }
} }
Operation::CallNative => { Operation::CallNative => {
@ -502,10 +504,9 @@ impl<'a> Vm<'a> {
let return_value = function.call(self, instruction)?; let return_value = function.call(self, instruction)?;
if let Some(value) = return_value { if let Some(value) = return_value {
let register_index = self.get_destination(destination)?;
let register = Register::Value(value); let register = Register::Value(value);
self.set_register(register_index, register)?; self.set_register(destination, register)?;
} }
} }
Operation::Return => { Operation::Return => {
@ -529,6 +530,7 @@ impl<'a> Vm<'a> {
}) })
}; };
} }
_ => unreachable!(),
} }
} }
} }
@ -565,7 +567,7 @@ impl<'a> Vm<'a> {
} }
} }
fn open_register(&self, register_index: u16) -> Result<ValueRef, VmError> { fn open_register(&self, register_index: u8) -> Result<ValueRef, VmError> {
let register_index = register_index as usize; let register_index = register_index as usize;
assert!( assert!(
@ -589,7 +591,7 @@ impl<'a> Vm<'a> {
pub(crate) fn open_register_allow_empty( pub(crate) fn open_register_allow_empty(
&self, &self,
register_index: u16, register_index: u8,
) -> Result<Option<ValueRef>, VmError> { ) -> Result<Option<ValueRef>, VmError> {
let register_index = register_index as usize; let register_index = register_index as usize;
let register = let register =
@ -628,16 +630,6 @@ impl<'a> Vm<'a> {
self.ip = new_ip; self.ip = new_ip;
} }
/// DRY helper to get a register index from a Destination
fn get_destination(&self, destination: Destination) -> Result<u16, VmError> {
let index = match destination {
Destination::Register(register_index) => register_index,
Destination::Local(local_index) => self.get_local_register(local_index)?,
};
Ok(index)
}
/// DRY helper to get a value from an Argument /// DRY helper to get a value from an Argument
fn get_argument(&self, argument: Argument) -> Result<ValueRef, VmError> { fn get_argument(&self, argument: Argument) -> Result<ValueRef, VmError> {
let value_ref = match argument { let value_ref = match argument {
@ -645,18 +637,13 @@ impl<'a> Vm<'a> {
ValueRef::Concrete(self.get_constant(constant_index)) ValueRef::Concrete(self.get_constant(constant_index))
} }
Argument::Register(register_index) => self.open_register(register_index)?, Argument::Register(register_index) => self.open_register(register_index)?,
Argument::Local(local_index) => {
let register_index = self.get_local_register(local_index)?;
self.open_register(register_index)?
}
}; };
Ok(value_ref) Ok(value_ref)
} }
#[inline(always)] #[inline(always)]
fn set_register(&mut self, to_register: u16, register: Register) -> Result<(), VmError> { fn set_register(&mut self, to_register: u8, register: Register) -> Result<(), VmError> {
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;
@ -672,7 +659,7 @@ impl<'a> Vm<'a> {
} }
#[inline(always)] #[inline(always)]
fn get_constant(&self, constant_index: u16) -> &ConcreteValue { fn get_constant(&self, constant_index: u8) -> &ConcreteValue {
let constant_index = constant_index as usize; let constant_index = constant_index as usize;
let constants = self.chunk.constants(); let constants = self.chunk.constants();
@ -685,18 +672,18 @@ impl<'a> Vm<'a> {
} }
#[inline(always)] #[inline(always)]
fn get_local_register(&self, local_index: u16) -> Result<u16, VmError> { fn get_local_register(&self, local_index: u8) -> Result<u8, VmError> {
let local_index = local_index as usize; let local_index = local_index as usize;
let locals = self.chunk.locals();
assert!( assert!(
local_index < self.local_definitions.len(), local_index < locals.len(),
"VM Error: Local index out of bounds" "VM Error: Local index out of bounds"
); );
self.local_definitions[local_index].ok_or_else(|| VmError::UndefinedLocal { let register_index = locals[local_index].register_index;
local_index: local_index as u16,
position: self.current_position(), Ok(register_index)
})
} }
#[inline(always)] #[inline(always)]
@ -715,7 +702,7 @@ impl<'a> Vm<'a> {
) )
); );
let instruction = instructions[self.ip]; let (instruction, _) = instructions[self.ip];
self.ip += 1; self.ip += 1;
@ -742,10 +729,10 @@ impl Display for Register {
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Pointer { pub enum Pointer {
Stack(u16), Stack(u8),
Constant(u16), Constant(u8),
ParentStack(u16), ParentStack(u8),
ParentConstant(u16), ParentConstant(u8),
} }
impl Display for Pointer { impl Display for Pointer {
@ -789,7 +776,7 @@ pub enum VmError {
// Local errors // Local errors
UndefinedLocal { UndefinedLocal {
local_index: u16, local_index: u8,
position: Span, position: Span,
}, },

View File

@ -6,7 +6,7 @@ fn add_assign_expects_mutable_variable() {
assert_eq!( assert_eq!(
compile(source), compile(source),
Err(CreateReport::Compile { Err(DustError::Compile {
error: CompileError::ExpectedMutableVariable { error: CompileError::ExpectedMutableVariable {
found: Token::Integer("1").to_owned(), found: Token::Integer("1").to_owned(),
position: Span(0, 1) position: Span(0, 1)
@ -22,7 +22,7 @@ fn divide_assign_expects_mutable_variable() {
assert_eq!( assert_eq!(
compile(source), compile(source),
Err(CreateReport::Compile { Err(DustError::Compile {
error: CompileError::ExpectedMutableVariable { error: CompileError::ExpectedMutableVariable {
found: Token::Integer("1").to_owned(), found: Token::Integer("1").to_owned(),
position: Span(0, 1) position: Span(0, 1)
@ -38,7 +38,7 @@ fn multiply_assign_expects_mutable_variable() {
assert_eq!( assert_eq!(
compile(source), compile(source),
Err(CreateReport::Compile { Err(DustError::Compile {
error: CompileError::ExpectedMutableVariable { error: CompileError::ExpectedMutableVariable {
found: Token::Integer("1").to_owned(), found: Token::Integer("1").to_owned(),
position: Span(0, 1) position: Span(0, 1)
@ -54,7 +54,7 @@ fn subtract_assign_expects_mutable_variable() {
assert_eq!( assert_eq!(
compile(source), compile(source),
Err(CreateReport::Compile { Err(DustError::Compile {
error: CompileError::ExpectedMutableVariable { error: CompileError::ExpectedMutableVariable {
found: Token::Integer("1").to_owned(), found: Token::Integer("1").to_owned(),
position: Span(0, 1) position: Span(0, 1)
@ -70,7 +70,7 @@ fn modulo_assign_expects_mutable_variable() {
assert_eq!( assert_eq!(
compile(source), compile(source),
Err(CreateReport::Compile { Err(DustError::Compile {
error: CompileError::ExpectedMutableVariable { error: CompileError::ExpectedMutableVariable {
found: Token::Integer("1").to_owned(), found: Token::Integer("1").to_owned(),
position: Span(0, 1) position: Span(0, 1)

View File

@ -11,7 +11,7 @@ fn constant() {
FunctionType { FunctionType {
type_parameters: None, type_parameters: None,
value_parameters: None, value_parameters: None,
return_type: Box::new(Type::Integer) return_type: Type::Integer
}, },
vec![ vec![
( (