Continue rewrite of instructions and operations
This commit is contained in:
parent
cc069df7ee
commit
a9e867aaab
23
README.md
23
README.md
@ -137,20 +137,23 @@ The compiler always checks types on the fly, so there is no need for a separate
|
||||
|
||||
### 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:
|
||||
|
||||
Bit | Description
|
||||
----- | -----------
|
||||
0-8 | The operation code.
|
||||
9 | Boolean flag indicating whether the second argument is a constant
|
||||
10 | Boolean flag indicating whether the third argument is a constant
|
||||
11 | Boolean flag indicating whether the first argument is a local
|
||||
12 | Boolean flag indicating whether the second argument is a local
|
||||
13 | Boolean flag indicating whether the third argument is a local
|
||||
17-32 | First argument, usually the destination register or local where a value is stored
|
||||
33-48 | Second argument, a register, local, constant or boolean flag
|
||||
49-63 | Third argument, a register, local, constant or boolean flag
|
||||
0-5 | Operation code
|
||||
6-8 | Unused, reserved in case more operation codes are needed
|
||||
9 | Flag indicating that A is a local
|
||||
10 | Flag indicating that B is a constant
|
||||
11 | Flag indicating that B is a local
|
||||
12 | Flag indicating that C is a constant
|
||||
13 | Flag indicating that C is a local
|
||||
14 | D Argument (boolean value)
|
||||
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
|
||||
enough, even for programs that are very large. This also means that chunks can store up to 2^16
|
||||
|
@ -20,8 +20,7 @@ pub struct Chunk {
|
||||
name: Option<String>,
|
||||
r#type: FunctionType,
|
||||
|
||||
instructions: SmallVec<[Instruction; 32]>,
|
||||
positions: SmallVec<[Span; 32]>,
|
||||
instructions: SmallVec<[(Instruction, Span); 32]>,
|
||||
constants: SmallVec<[ConcreteValue; 16]>,
|
||||
locals: SmallVec<[Local; 8]>,
|
||||
}
|
||||
@ -31,7 +30,6 @@ impl Chunk {
|
||||
Self {
|
||||
name,
|
||||
instructions: SmallVec::new(),
|
||||
positions: SmallVec::new(),
|
||||
constants: SmallVec::new(),
|
||||
locals: SmallVec::new(),
|
||||
r#type: FunctionType {
|
||||
@ -45,8 +43,7 @@ impl Chunk {
|
||||
pub fn with_data(
|
||||
name: Option<String>,
|
||||
r#type: FunctionType,
|
||||
instructions: SmallVec<[Instruction; 32]>,
|
||||
positions: SmallVec<[Span; 32]>,
|
||||
instructions: SmallVec<[(Instruction, Span); 32]>,
|
||||
constants: SmallVec<[ConcreteValue; 16]>,
|
||||
locals: SmallVec<[Local; 8]>,
|
||||
) -> Self {
|
||||
@ -54,7 +51,6 @@ impl Chunk {
|
||||
name,
|
||||
r#type,
|
||||
instructions,
|
||||
positions,
|
||||
constants,
|
||||
locals,
|
||||
}
|
||||
@ -80,14 +76,10 @@ impl Chunk {
|
||||
&self.constants
|
||||
}
|
||||
|
||||
pub fn instructions(&self) -> &SmallVec<[Instruction; 32]> {
|
||||
pub fn instructions(&self) -> &SmallVec<[(Instruction, Span); 32]> {
|
||||
&self.instructions
|
||||
}
|
||||
|
||||
pub fn positions(&self) -> &SmallVec<[Span; 32]> {
|
||||
&self.positions
|
||||
}
|
||||
|
||||
pub fn locals(&self) -> &SmallVec<[Local; 8]> {
|
||||
&self.locals
|
||||
}
|
||||
@ -96,9 +88,9 @@ impl Chunk {
|
||||
self.instructions()
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|instruction| {
|
||||
.find_map(|(instruction, _)| {
|
||||
if instruction.yields_value() {
|
||||
Some(instruction.a() as usize + 1)
|
||||
Some(instruction.a as usize + 1)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -145,10 +137,10 @@ impl PartialEq for Chunk {
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct Local {
|
||||
/// 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.
|
||||
pub r#type: Type,
|
||||
/// Stack index where the local's value is stored.
|
||||
pub register_index: u8,
|
||||
|
||||
/// Whether the local is mutable.
|
||||
pub is_mutable: bool,
|
||||
@ -159,11 +151,11 @@ pub struct Local {
|
||||
|
||||
impl Local {
|
||||
/// 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 {
|
||||
identifier_index,
|
||||
r#type,
|
||||
is_mutable: mutable,
|
||||
register_index,
|
||||
is_mutable,
|
||||
scope,
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
//! - [`compile`] borrows a string and returns a chunk, handling the entire compilation process and
|
||||
//! turning any resulting [`ComplileError`] into a [`DustError`].
|
||||
//! - [`Compiler`] uses a lexer to get tokens and assembles a chunk.
|
||||
mod optimize;
|
||||
|
||||
use std::{
|
||||
fmt::{self, Display, Formatter},
|
||||
mem::replace,
|
||||
@ -11,16 +13,17 @@ use std::{
|
||||
};
|
||||
|
||||
use colored::Colorize;
|
||||
use optimize::{optimize_control_flow, optimize_set_local};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::{
|
||||
instruction::{
|
||||
Call, CallNative, Close, DefineLocal, GetLocal, Jump, LoadBoolean, LoadConstant, LoadList,
|
||||
LoadSelf, Move, Negate, Not, Return, SetLocal, Test,
|
||||
Call, CallNative, Close, GetLocal, Jump, LoadConstant, LoadList, LoadSelf, Move, Negate,
|
||||
Not, Return, SetLocal, Test,
|
||||
},
|
||||
optimize_control_flow, optimize_set_local, AnnotatedError, Argument, Chunk, ConcreteValue,
|
||||
Destination, DustError, DustString, FunctionType, Instruction, LexError, Lexer, Local,
|
||||
NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type, TypeConflict,
|
||||
AnnotatedError, Argument, Chunk, ConcreteValue, DustError, DustString, FunctionType,
|
||||
Instruction, LexError, Lexer, Local, NativeFunction, Operation, Scope, Span, Token, TokenKind,
|
||||
TokenOwned, Type, TypeConflict,
|
||||
};
|
||||
|
||||
/// Compiles the input and returns a chunk.
|
||||
@ -32,7 +35,7 @@ use crate::{
|
||||
/// let source = "40 + 2 == 42";
|
||||
/// let chunk = compile(source).unwrap();
|
||||
///
|
||||
/// assert_eq!(chunk.len(), 6);
|
||||
/// assert_eq!(chunk.len(), 3);
|
||||
/// ```
|
||||
pub fn compile(source: &str) -> Result<Chunk, DustError> {
|
||||
let lexer = Lexer::new(source);
|
||||
@ -55,7 +58,7 @@ pub struct Compiler<'src> {
|
||||
self_name: Option<DustString>,
|
||||
instructions: SmallVec<[(Instruction, Type, Span); 32]>,
|
||||
constants: SmallVec<[ConcreteValue; 16]>,
|
||||
locals: SmallVec<[Local; 8]>,
|
||||
locals: SmallVec<[(Local, Type); 8]>,
|
||||
|
||||
lexer: Lexer<'src>,
|
||||
|
||||
@ -65,7 +68,7 @@ pub struct Compiler<'src> {
|
||||
previous_position: Span,
|
||||
|
||||
return_type: Option<Type>,
|
||||
minimum_register: u16,
|
||||
minimum_register: u8,
|
||||
block_index: u8,
|
||||
current_scope: Scope,
|
||||
}
|
||||
@ -99,8 +102,8 @@ impl<'src> Compiler<'src> {
|
||||
|
||||
pub fn finish(
|
||||
self,
|
||||
type_parameters: Option<SmallVec<[u16; 4]>>,
|
||||
value_parameters: Option<SmallVec<[(u16, Type); 4]>>,
|
||||
type_parameters: Option<SmallVec<[u8; 4]>>,
|
||||
value_parameters: Option<SmallVec<[(u8, Type); 4]>>,
|
||||
) -> Chunk {
|
||||
log::info!("End chunk");
|
||||
|
||||
@ -109,20 +112,14 @@ impl<'src> Compiler<'src> {
|
||||
value_parameters,
|
||||
return_type: self.return_type.unwrap_or(Type::None),
|
||||
};
|
||||
let (instructions, positions) = self
|
||||
let instructions = self
|
||||
.instructions
|
||||
.into_iter()
|
||||
.map(|(instruction, _, position)| (instruction, position))
|
||||
.unzip();
|
||||
.collect();
|
||||
let locals = self.locals.into_iter().map(|(local, _)| local).collect();
|
||||
|
||||
Chunk::with_data(
|
||||
self.self_name,
|
||||
r#type,
|
||||
instructions,
|
||||
positions,
|
||||
self.constants,
|
||||
self.locals,
|
||||
)
|
||||
Chunk::with_data(self.self_name, r#type, instructions, self.constants, locals)
|
||||
}
|
||||
|
||||
pub fn compile(&mut self) -> Result<(), CompileError> {
|
||||
@ -143,13 +140,13 @@ impl<'src> Compiler<'src> {
|
||||
matches!(self.current_token, Token::Eof)
|
||||
}
|
||||
|
||||
fn next_register(&self) -> u16 {
|
||||
fn next_register(&self) -> u8 {
|
||||
self.instructions
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|(instruction, _, _)| {
|
||||
if instruction.yields_value() {
|
||||
Some(instruction.a() + 1)
|
||||
Some(instruction.a + 1)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -176,7 +173,7 @@ impl<'src> Compiler<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_local(&self, index: u16) -> Result<&Local, CompileError> {
|
||||
fn get_local(&self, index: u8) -> Result<&(Local, Type), CompileError> {
|
||||
self.locals
|
||||
.get(index as usize)
|
||||
.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
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
.find_map(|(index, local)| {
|
||||
.find_map(|(index, (local, _))| {
|
||||
let constant = self.constants.get(local.identifier_index as usize)?;
|
||||
let identifier = if let ConcreteValue::String(identifier) = constant {
|
||||
identifier
|
||||
@ -199,7 +196,7 @@ impl<'src> Compiler<'src> {
|
||||
};
|
||||
|
||||
if identifier == identifier_text {
|
||||
Some(index as u16)
|
||||
Some(index as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -213,39 +210,44 @@ impl<'src> Compiler<'src> {
|
||||
fn declare_local(
|
||||
&mut self,
|
||||
identifier: &str,
|
||||
register_index: u8,
|
||||
r#type: Type,
|
||||
is_mutable: bool,
|
||||
scope: Scope,
|
||||
) -> (u16, u16) {
|
||||
) -> (u8, u8) {
|
||||
log::info!("Declaring local {identifier}");
|
||||
|
||||
let identifier = ConcreteValue::string(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
|
||||
.push(Local::new(identifier_index, r#type, is_mutable, scope));
|
||||
self.locals.push((
|
||||
Local::new(identifier_index, register_index, is_mutable, scope),
|
||||
r#type,
|
||||
));
|
||||
|
||||
(local_index, identifier_index)
|
||||
}
|
||||
|
||||
fn get_identifier(&self, local_index: u16) -> Option<String> {
|
||||
self.locals.get(local_index as usize).and_then(|local| {
|
||||
fn get_identifier(&self, local_index: u8) -> Option<String> {
|
||||
self.locals
|
||||
.get(local_index as usize)
|
||||
.and_then(|(local, _)| {
|
||||
self.constants
|
||||
.get(local.identifier_index as usize)
|
||||
.map(|value| value.to_string())
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
.constants
|
||||
.iter()
|
||||
.position(|constant| constant == &value)
|
||||
{
|
||||
index as u16
|
||||
index as u8
|
||||
} else {
|
||||
let index = self.constants.len() as u16;
|
||||
let index = self.constants.len() as u8;
|
||||
|
||||
self.constants.push(value);
|
||||
|
||||
@ -325,17 +327,30 @@ impl<'src> Compiler<'src> {
|
||||
.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 {
|
||||
if instruction.a() == register_index {
|
||||
if let Operation::LoadList = instruction.operation() {
|
||||
if !instruction.yields_value() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let operation = instruction.operation();
|
||||
|
||||
if let Operation::LoadList = operation {
|
||||
let LoadList { start_register, .. } = LoadList::from(instruction);
|
||||
let item_type = self.get_register_type(start_register)?;
|
||||
|
||||
return Ok(Type::List(Box::new(item_type)));
|
||||
}
|
||||
|
||||
if let Operation::LoadSelf = instruction.operation() {
|
||||
if let Operation::LoadSelf = operation {
|
||||
return Ok(Type::SelfChunk);
|
||||
}
|
||||
|
||||
@ -343,7 +358,6 @@ impl<'src> Compiler<'src> {
|
||||
return Ok(r#type.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(CompileError::CannotResolveRegisterType {
|
||||
register_index: register_index as usize,
|
||||
@ -391,14 +405,10 @@ impl<'src> Compiler<'src> {
|
||||
) -> Result<(), CompileError> {
|
||||
let r#type = constant.r#type();
|
||||
let constant_index = self.push_or_get_constant(constant);
|
||||
let destination = Destination::Register(self.next_register());
|
||||
let instruction = Instruction::from(LoadConstant {
|
||||
destination,
|
||||
constant_index,
|
||||
jump_next: false,
|
||||
});
|
||||
let destination = self.next_register();
|
||||
let load_constant = Instruction::load_constant(destination, constant_index, false);
|
||||
|
||||
self.emit_instruction(instruction, r#type, position);
|
||||
self.emit_instruction(load_constant, r#type, position);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -410,14 +420,10 @@ impl<'src> Compiler<'src> {
|
||||
self.advance()?;
|
||||
|
||||
let boolean = text.parse::<bool>().unwrap();
|
||||
let destination = Destination::Register(self.next_register());
|
||||
let instruction = Instruction::from(LoadBoolean {
|
||||
destination,
|
||||
value: boolean,
|
||||
jump_next: false,
|
||||
});
|
||||
let destination = self.next_register();
|
||||
let load_boolean = Instruction::load_boolean(destination, boolean, false);
|
||||
|
||||
self.emit_instruction(instruction, Type::Boolean, position);
|
||||
self.emit_instruction(load_boolean, Type::Boolean, position);
|
||||
|
||||
Ok(())
|
||||
} 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() {
|
||||
TokenKind::Bang => Instruction::from(Not {
|
||||
destination,
|
||||
@ -624,8 +630,13 @@ impl<'src> Compiler<'src> {
|
||||
position: self.previous_position,
|
||||
})?;
|
||||
let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?;
|
||||
let left_is_mutable_local = if let Argument::Local(local_index) = left {
|
||||
self.get_local(local_index)?.is_mutable
|
||||
let left_is_mutable_local = if let Operation::GetLocal = left_instruction.operation() {
|
||||
let GetLocal { local_index, .. } = GetLocal::from(&left_instruction);
|
||||
|
||||
self.locals
|
||||
.get(local_index as usize)
|
||||
.map(|(local, _)| local.is_mutable)
|
||||
.unwrap_or(false)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
@ -742,12 +753,11 @@ impl<'src> Compiler<'src> {
|
||||
|
||||
let destination = if is_assignment {
|
||||
match left {
|
||||
Argument::Register(register) => Destination::Register(register),
|
||||
Argument::Local(local_index) => Destination::Local(local_index),
|
||||
Argument::Constant(_) => Destination::Register(self.next_register()),
|
||||
Argument::Register(register) => register,
|
||||
Argument::Constant(_) => self.next_register(),
|
||||
}
|
||||
} else {
|
||||
Destination::Register(self.next_register())
|
||||
self.next_register()
|
||||
};
|
||||
let instruction = match operator {
|
||||
Token::Plus | Token::PlusEqual => Instruction::add(destination, left, right),
|
||||
@ -823,13 +833,14 @@ impl<'src> Compiler<'src> {
|
||||
.push((right_instruction, right_type, right_position));
|
||||
}
|
||||
|
||||
let destination = self.next_register();
|
||||
let comparison = match operator {
|
||||
Token::DoubleEqual => Instruction::equal(true, left, right),
|
||||
Token::BangEqual => Instruction::equal(false, left, right),
|
||||
Token::Less => Instruction::less(true, left, right),
|
||||
Token::LessEqual => Instruction::less_equal(true, left, right),
|
||||
Token::Greater => Instruction::less_equal(false, left, right),
|
||||
Token::GreaterEqual => Instruction::less(false, left, right),
|
||||
Token::DoubleEqual => Instruction::equal(destination, true, left, right),
|
||||
Token::BangEqual => Instruction::equal(destination, false, left, right),
|
||||
Token::Less => Instruction::less(destination, true, left, right),
|
||||
Token::LessEqual => Instruction::less_equal(destination, true, left, right),
|
||||
Token::Greater => Instruction::less_equal(destination, false, left, right),
|
||||
Token::GreaterEqual => Instruction::less(destination, false, left, right),
|
||||
_ => {
|
||||
return Err(CompileError::ExpectedTokenMultiple {
|
||||
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(jump, Type::None, operator_position);
|
||||
self.emit_instruction(load_true, Type::Boolean, operator_position);
|
||||
self.emit_instruction(load_false, Type::Boolean, operator_position);
|
||||
self.emit_instruction(comparison, Type::Boolean, operator_position);
|
||||
|
||||
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 is_local = matches!(argument, Argument::Local(_));
|
||||
let is_local = left_instruction.operation() == Operation::GetLocal;
|
||||
|
||||
if push_back || is_local {
|
||||
self.instructions
|
||||
@ -929,7 +920,7 @@ impl<'src> Compiler<'src> {
|
||||
|
||||
if is_logic_chain {
|
||||
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);
|
||||
|
||||
self.instructions
|
||||
@ -957,7 +948,7 @@ impl<'src> Compiler<'src> {
|
||||
} else if let Some(native_function) = NativeFunction::from_str(identifier) {
|
||||
return self.parse_native_call(native_function);
|
||||
} 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 });
|
||||
|
||||
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;
|
||||
|
||||
if !self.current_scope.contains(&local.scope) {
|
||||
@ -999,17 +992,16 @@ impl<'src> Compiler<'src> {
|
||||
});
|
||||
|
||||
self.emit_instruction(set_local, Type::None, start_position);
|
||||
optimize_set_local(&mut self.instructions);
|
||||
optimize_set_local(self);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let destination = Destination::Register(self.next_register());
|
||||
let destination = self.next_register();
|
||||
let get_local = Instruction::from(GetLocal {
|
||||
destination,
|
||||
local_index,
|
||||
});
|
||||
let r#type = self.get_local(local_index)?.r#type.clone();
|
||||
|
||||
self.emit_instruction(get_local, r#type, self.previous_position);
|
||||
|
||||
@ -1083,7 +1075,7 @@ impl<'src> Compiler<'src> {
|
||||
self.allow(Token::Comma)?;
|
||||
}
|
||||
|
||||
let destination = Destination::Register(self.next_register());
|
||||
let destination = self.next_register();
|
||||
let end = self.previous_position.1;
|
||||
let load_list = Instruction::from(LoadList {
|
||||
destination,
|
||||
@ -1099,22 +1091,18 @@ impl<'src> Compiler<'src> {
|
||||
self.advance()?;
|
||||
self.parse_expression()?;
|
||||
|
||||
if matches!(
|
||||
self.get_last_operations(),
|
||||
Some([
|
||||
Operation::Equal | Operation::Less | Operation::LessEqual,
|
||||
Operation::Jump,
|
||||
Operation::LoadBoolean,
|
||||
Operation::LoadBoolean,
|
||||
])
|
||||
) {
|
||||
self.instructions.pop();
|
||||
self.instructions.pop();
|
||||
self.instructions.pop();
|
||||
} else if let Some((instruction, _, _)) = self.instructions.last() {
|
||||
let test_register = instruction.a();
|
||||
if let Some((instruction, _, _)) = self.instructions.last() {
|
||||
let argument = match instruction.destination_as_argument() {
|
||||
Some(argument) => argument,
|
||||
None => {
|
||||
return Err(CompileError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: self.previous_position,
|
||||
});
|
||||
}
|
||||
};
|
||||
let test = Instruction::from(Test {
|
||||
argument: Argument::Register(test_register),
|
||||
argument,
|
||||
test_value: true,
|
||||
});
|
||||
|
||||
@ -1135,7 +1123,7 @@ impl<'src> Compiler<'src> {
|
||||
}
|
||||
|
||||
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_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_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();
|
||||
|
||||
if let Err(conflict) = if_block_type.check(&else_block_type) {
|
||||
@ -1178,7 +1166,7 @@ impl<'src> Compiler<'src> {
|
||||
if let Some(skippable) =
|
||||
self.get_last_jumpable_mut_between(1, if_block_distance as usize)
|
||||
{
|
||||
skippable.set_c_to_boolean(true);
|
||||
skippable.c = true as u8;
|
||||
} else {
|
||||
if_block_distance += 1;
|
||||
let jump = Instruction::from(Jump {
|
||||
@ -1230,7 +1218,7 @@ impl<'src> Compiler<'src> {
|
||||
fn parse_while(&mut self) -> Result<(), CompileError> {
|
||||
self.advance()?;
|
||||
|
||||
let expression_start = self.instructions.len() as u16;
|
||||
let expression_start = self.instructions.len() as u8;
|
||||
|
||||
self.parse_expression()?;
|
||||
|
||||
@ -1252,8 +1240,8 @@ impl<'src> Compiler<'src> {
|
||||
|
||||
self.parse_block()?;
|
||||
|
||||
let block_end = self.instructions.len() as u16;
|
||||
let jump_distance = block_end - block_start as u16 + 1;
|
||||
let block_end = self.instructions.len() as u8;
|
||||
let jump_distance = block_end - block_start as u8 + 1;
|
||||
let jump = Instruction::from(Jump {
|
||||
offset: jump_distance,
|
||||
is_positive: true,
|
||||
@ -1303,7 +1291,7 @@ impl<'src> Compiler<'src> {
|
||||
let argument_count = destination - start_register;
|
||||
let return_type = function.r#type().return_type;
|
||||
let call_native = Instruction::from(CallNative {
|
||||
destination: Destination::Register(destination),
|
||||
destination,
|
||||
function,
|
||||
argument_count,
|
||||
});
|
||||
@ -1417,21 +1405,20 @@ impl<'src> Compiler<'src> {
|
||||
self.expect(Token::Equal)?;
|
||||
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 {
|
||||
r#type
|
||||
} 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(())
|
||||
}
|
||||
@ -1453,7 +1440,7 @@ impl<'src> Compiler<'src> {
|
||||
|
||||
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)? {
|
||||
let is_mutable = function_compiler.allow(Token::Mut)?;
|
||||
@ -1478,8 +1465,10 @@ impl<'src> Compiler<'src> {
|
||||
|
||||
function_compiler.advance()?;
|
||||
|
||||
let local_register_index = function_compiler.next_register();
|
||||
let (_, identifier_index) = function_compiler.declare_local(
|
||||
parameter,
|
||||
local_register_index,
|
||||
r#type.clone(),
|
||||
is_mutable,
|
||||
function_compiler.current_scope,
|
||||
@ -1526,7 +1515,7 @@ impl<'src> Compiler<'src> {
|
||||
let function =
|
||||
ConcreteValue::function(function_compiler.finish(None, value_parameters.clone()));
|
||||
let constant_index = self.push_or_get_constant(function);
|
||||
let register = self.next_register();
|
||||
let destination = self.next_register();
|
||||
let function_type = FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters,
|
||||
@ -1536,30 +1525,23 @@ impl<'src> Compiler<'src> {
|
||||
if let Some((identifier, position)) = identifier_info {
|
||||
let (local_index, _) = self.declare_local(
|
||||
identifier,
|
||||
destination,
|
||||
Type::function(function_type.clone()),
|
||||
false,
|
||||
self.current_scope,
|
||||
);
|
||||
let load_constant = Instruction::from(LoadConstant {
|
||||
destination: Destination::Register(register),
|
||||
constant_index,
|
||||
jump_next: false,
|
||||
});
|
||||
let define_local = Instruction::from(DefineLocal {
|
||||
local_index,
|
||||
register,
|
||||
is_mutable: false,
|
||||
});
|
||||
let load_constant = Instruction::load_constant(destination, constant_index, false);
|
||||
let set_local = Instruction::set_local(destination, local_index);
|
||||
|
||||
self.emit_instruction(
|
||||
load_constant,
|
||||
Type::function(function_type),
|
||||
Span(function_start, function_end),
|
||||
);
|
||||
self.emit_instruction(define_local, Type::None, position);
|
||||
self.emit_instruction(set_local, Type::None, position);
|
||||
} else {
|
||||
let load_constant = Instruction::from(LoadConstant {
|
||||
destination: Destination::Register(register),
|
||||
destination,
|
||||
constant_index,
|
||||
jump_next: false,
|
||||
});
|
||||
@ -1637,9 +1619,9 @@ impl<'src> Compiler<'src> {
|
||||
}
|
||||
|
||||
let end = self.current_position.1;
|
||||
let register = self.next_register();
|
||||
let destination = self.next_register();
|
||||
let call = Instruction::from(Call {
|
||||
destination: Destination::Register(register),
|
||||
destination,
|
||||
function,
|
||||
argument_count,
|
||||
});
|
@ -2,7 +2,7 @@
|
||||
|
||||
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>(
|
||||
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;
|
||||
|
||||
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_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.
|
||||
@ -85,9 +84,9 @@ pub fn optimize_control_flow(instructions: &mut [(Instruction, Type, Span)]) {
|
||||
/// The instructions must be in the following order:
|
||||
/// - `Add`, `Subtract`, `Multiply`, `Divide` or `Modulo`
|
||||
/// - `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!(
|
||||
get_last_operations(instructions),
|
||||
compiler.get_last_operations(),
|
||||
Some([
|
||||
Operation::Add
|
||||
| Operation::Subtract
|
||||
@ -97,17 +96,17 @@ pub fn optimize_set_local(instructions: &mut SmallVec<[(Instruction, Type, Span)
|
||||
Operation::SetLocal,
|
||||
])
|
||||
) {
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
log::debug!("Condensing math and SetLocal to math instruction");
|
||||
|
||||
let set_local = SetLocal::from(&instructions.pop().unwrap().0);
|
||||
let math_instruction = instructions.last_mut().unwrap().0;
|
||||
let math_instruction_new = *math_instruction
|
||||
.clone()
|
||||
.set_a(set_local.local_index)
|
||||
.set_a_is_local(true);
|
||||
let set_local = SetLocal::from(&compiler.instructions.pop().unwrap().0);
|
||||
let (local, _) = compiler.get_local(set_local.local_index)?;
|
||||
let local_register_index = local.register_index;
|
||||
let math_instruction = &mut compiler.instructions.last_mut().unwrap().0;
|
||||
|
||||
instructions.last_mut().unwrap().0 = math_instruction_new;
|
||||
math_instruction.a = local_register_index;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -61,8 +61,8 @@ const CONSTANT_HEADER: [&str; 4] = [
|
||||
const LOCAL_HEADER: [&str; 4] = [
|
||||
"Locals",
|
||||
"------",
|
||||
" i SCOPE MUTABLE TYPE IDENTIFIER ",
|
||||
"--- ------- ------- ---------------- ----------------",
|
||||
" i IDENTIFIER REGISTER SCOPE MUTABLE",
|
||||
"--- ---------------- -------- ------- -------",
|
||||
];
|
||||
|
||||
/// Builder that constructs a human-readable representation of a chunk.
|
||||
@ -209,13 +209,7 @@ impl<'a> Disassembler<'a> {
|
||||
self.push_header(line);
|
||||
}
|
||||
|
||||
for (index, (instruction, position)) in self
|
||||
.chunk
|
||||
.instructions()
|
||||
.iter()
|
||||
.zip(self.chunk.positions().iter())
|
||||
.enumerate()
|
||||
{
|
||||
for (index, (instruction, position)) in self.chunk.instructions().iter().enumerate() {
|
||||
let position = position.to_string();
|
||||
let operation = instruction.operation().to_string();
|
||||
let info = instruction.disassembly_info();
|
||||
@ -235,7 +229,7 @@ impl<'a> Disassembler<'a> {
|
||||
index,
|
||||
Local {
|
||||
identifier_index,
|
||||
r#type,
|
||||
register_index,
|
||||
scope,
|
||||
is_mutable,
|
||||
},
|
||||
@ -247,10 +241,10 @@ impl<'a> Disassembler<'a> {
|
||||
.get(*identifier_index as usize)
|
||||
.map(|value| value.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 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);
|
||||
|
@ -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 annotate_snippets::{Level, Renderer, Snippet};
|
||||
|
||||
use crate::{CompileError, Span, VmError};
|
||||
|
||||
/// A top-level error that can occur during the execution of Dust code.
|
||||
///
|
||||
/// This error can display nicely formatted messages with source code annotations.
|
||||
/// A top-level error that can occur during the interpretation of Dust code.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DustError<'src> {
|
||||
Compile {
|
||||
@ -30,7 +28,7 @@ impl<'src> DustError<'src> {
|
||||
DustError::Runtime { error, source }
|
||||
}
|
||||
|
||||
pub fn create_report(&self) -> String {
|
||||
pub fn report(&self) -> String {
|
||||
let (position, title, description, details) = self.error_data();
|
||||
let label = format!("{}: {}", title, description);
|
||||
let details = details.unwrap_or_else(|| "While parsing this code".to_string());
|
||||
@ -74,7 +72,7 @@ impl<'src> DustError<'src> {
|
||||
|
||||
impl Display for DustError<'_> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.create_report())
|
||||
write!(f, "{}", self.report())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
use crate::{Argument, Destination, Instruction, Operation};
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Add {
|
||||
pub destination: Destination,
|
||||
pub destination: u8,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Add {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let destination = instruction.a;
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Add {
|
||||
@ -21,16 +21,12 @@ impl From<&Instruction> for Add {
|
||||
|
||||
impl From<Add> for Instruction {
|
||||
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 (c, c_options) = add.right.as_index_and_c_options();
|
||||
let metadata = operation as u8 | b_options.bits() | c_options.bits();
|
||||
|
||||
Instruction {
|
||||
operation: Operation::ADD,
|
||||
options: a_options | b_options | c_options,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,32 @@
|
||||
use crate::{Argument, Destination, Instruction, Operation};
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Call {
|
||||
pub destination: Destination,
|
||||
pub destination: u8,
|
||||
pub function: Argument,
|
||||
pub argument_count: u16,
|
||||
pub argument_count: u8,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Call {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a;
|
||||
let function = instruction.b_as_argument();
|
||||
let argument_count = instruction.c;
|
||||
|
||||
Call {
|
||||
destination: instruction.a_as_destination(),
|
||||
function: instruction.b_as_argument(),
|
||||
argument_count: instruction.c,
|
||||
destination,
|
||||
function,
|
||||
argument_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Call> for Instruction {
|
||||
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 c = call.argument_count;
|
||||
let metadata = Operation::Call as u8 | b_options.bits();
|
||||
|
||||
Instruction {
|
||||
operation: Operation::CALL,
|
||||
options: a_options | b_options,
|
||||
a,
|
||||
b,
|
||||
c: call.argument_count,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
use crate::{Destination, Instruction, NativeFunction, Operation};
|
||||
use crate::{Instruction, NativeFunction, Operation};
|
||||
|
||||
pub struct CallNative {
|
||||
pub destination: Destination,
|
||||
pub destination: u8,
|
||||
pub function: NativeFunction,
|
||||
pub argument_count: u16,
|
||||
pub argument_count: u8,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for CallNative {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let destination = instruction.a;
|
||||
let function = NativeFunction::from(instruction.b);
|
||||
|
||||
CallNative {
|
||||
@ -21,15 +21,11 @@ impl From<&Instruction> for CallNative {
|
||||
|
||||
impl From<CallNative> for Instruction {
|
||||
fn from(call_native: CallNative) -> Self {
|
||||
let (a, a_options) = call_native.destination.as_index_and_a_options();
|
||||
let b = call_native.function as u16;
|
||||
let metadata = Operation::CallNative as u8;
|
||||
let a = call_native.destination;
|
||||
let b = call_native.function as u8;
|
||||
let c = call_native.argument_count;
|
||||
|
||||
Instruction {
|
||||
operation: Operation::CALL_NATIVE,
|
||||
options: a_options,
|
||||
a,
|
||||
b,
|
||||
c: call_native.argument_count,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,8 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionOptions;
|
||||
|
||||
pub struct Close {
|
||||
pub from: u16,
|
||||
pub to: u16,
|
||||
pub from: u8,
|
||||
pub to: u8,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Close {
|
||||
@ -18,12 +16,9 @@ impl From<&Instruction> for Close {
|
||||
|
||||
impl From<Close> for Instruction {
|
||||
fn from(close: Close) -> Self {
|
||||
Instruction {
|
||||
operation: Operation::CLOSE,
|
||||
options: InstructionOptions::empty(),
|
||||
a: 0,
|
||||
b: close.from,
|
||||
c: close.to,
|
||||
}
|
||||
let metadata = Operation::Close as u8;
|
||||
let (a, b, c) = (0, close.from, close.to);
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
use crate::{Argument, Destination, Instruction, Operation};
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Divide {
|
||||
pub destination: Destination,
|
||||
pub destination: u8,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Divide {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let destination = instruction.a;
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Divide {
|
||||
@ -21,16 +21,11 @@ impl From<&Instruction> for Divide {
|
||||
|
||||
impl From<Divide> for Instruction {
|
||||
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 (c, c_options) = divide.right.as_index_and_c_options();
|
||||
let metadata = Operation::Divide as u8 | b_options.bits() | c_options.bits();
|
||||
|
||||
Instruction {
|
||||
operation: Operation::DIVIDE,
|
||||
options: a_options | b_options | c_options,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::{Argument, Destination, Instruction, Operation};
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
use super::InstructionOptions;
|
||||
|
||||
pub struct Equal {
|
||||
pub destination: Destination,
|
||||
pub destination: u8,
|
||||
pub value: bool,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
@ -11,8 +11,8 @@ pub struct Equal {
|
||||
|
||||
impl From<&Instruction> for Equal {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let value = instruction.options.d();
|
||||
let destination = instruction.a;
|
||||
let value = instruction.d();
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Equal {
|
||||
@ -26,7 +26,7 @@ impl From<&Instruction> for Equal {
|
||||
|
||||
impl From<Equal> for Instruction {
|
||||
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 (c, c_options) = equal.right.as_index_and_c_options();
|
||||
let d_options = if equal.value {
|
||||
@ -34,13 +34,9 @@ impl From<Equal> for Instruction {
|
||||
} else {
|
||||
InstructionOptions::empty()
|
||||
};
|
||||
let metadata =
|
||||
Operation::Equal as u8 | b_options.bits() | c_options.bits() | d_options.bits();
|
||||
|
||||
Instruction {
|
||||
operation: Operation::EQUAL,
|
||||
options: a_options | b_options | c_options | d_options,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,29 @@
|
||||
use crate::{Destination, Instruction, Operation};
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct GetLocal {
|
||||
pub destination: Destination,
|
||||
pub local_index: u16,
|
||||
pub destination: u8,
|
||||
pub local_index: u8,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for GetLocal {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let destination = instruction.a;
|
||||
let local_index = instruction.b;
|
||||
|
||||
GetLocal {
|
||||
destination,
|
||||
local_index: instruction.b,
|
||||
local_index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GetLocal> for Instruction {
|
||||
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 {
|
||||
operation: Operation::GET_LOCAL,
|
||||
options: a_options,
|
||||
a,
|
||||
b: get_local.local_index,
|
||||
c: 0,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionOptions;
|
||||
|
||||
pub struct Jump {
|
||||
pub offset: u16,
|
||||
pub offset: u8,
|
||||
pub is_positive: bool,
|
||||
}
|
||||
|
||||
@ -18,12 +16,11 @@ impl From<&Instruction> for Jump {
|
||||
|
||||
impl From<Jump> for Instruction {
|
||||
fn from(jump: Jump) -> Self {
|
||||
Instruction {
|
||||
operation: Operation::JUMP,
|
||||
options: InstructionOptions::empty(),
|
||||
a: 0,
|
||||
b: jump.offset,
|
||||
c: jump.is_positive as u16,
|
||||
}
|
||||
let metadata = Operation::Jump as u8;
|
||||
let a = 0;
|
||||
let b = jump.offset;
|
||||
let c = jump.is_positive as u8;
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::{Argument, Destination, Instruction, Operation};
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
use super::InstructionOptions;
|
||||
|
||||
pub struct Less {
|
||||
pub destination: Destination,
|
||||
pub destination: u8,
|
||||
pub value: bool,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
@ -11,8 +11,8 @@ pub struct Less {
|
||||
|
||||
impl From<&Instruction> for Less {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let value = instruction.options.d();
|
||||
let destination = instruction.a;
|
||||
let value = instruction.d();
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Less {
|
||||
@ -26,7 +26,7 @@ impl From<&Instruction> for Less {
|
||||
|
||||
impl From<Less> for Instruction {
|
||||
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 (c, c_options) = less.right.as_index_and_c_options();
|
||||
let d_options = if less.value {
|
||||
@ -34,13 +34,9 @@ impl From<Less> for Instruction {
|
||||
} else {
|
||||
InstructionOptions::empty()
|
||||
};
|
||||
let metadata =
|
||||
Operation::Less as u8 | b_options.bits() | c_options.bits() | d_options.bits();
|
||||
|
||||
Instruction {
|
||||
operation: Operation::LESS,
|
||||
options: a_options | b_options | c_options | d_options,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::{Argument, Destination, Instruction, Operation};
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
use super::InstructionOptions;
|
||||
|
||||
pub struct LessEqual {
|
||||
pub destination: Destination,
|
||||
pub destination: u8,
|
||||
pub value: bool,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
@ -11,8 +11,8 @@ pub struct LessEqual {
|
||||
|
||||
impl From<&Instruction> for LessEqual {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let value = instruction.options.d();
|
||||
let destination = instruction.a;
|
||||
let value = instruction.d();
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
LessEqual {
|
||||
@ -26,7 +26,7 @@ impl From<&Instruction> for LessEqual {
|
||||
|
||||
impl From<LessEqual> for Instruction {
|
||||
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 (c, c_options) = less_equal.right.as_index_and_c_options();
|
||||
let d_options = if less_equal.value {
|
||||
@ -34,13 +34,9 @@ impl From<LessEqual> for Instruction {
|
||||
} else {
|
||||
InstructionOptions::empty()
|
||||
};
|
||||
let metadata =
|
||||
Operation::LessEqual as u8 | b_options.bits() | c_options.bits() | d_options.bits();
|
||||
|
||||
Instruction {
|
||||
operation: Operation::LESS_EQUAL,
|
||||
options: a_options | b_options | c_options | d_options,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
use crate::{Destination, Instruction, Operation};
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct LoadBoolean {
|
||||
pub destination: Destination,
|
||||
pub destination: u8,
|
||||
pub value: bool,
|
||||
pub jump_next: bool,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for LoadBoolean {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let destination = instruction.a;
|
||||
let value = instruction.b != 0;
|
||||
let jump_next = instruction.c != 0;
|
||||
|
||||
@ -22,16 +22,11 @@ impl From<&Instruction> for LoadBoolean {
|
||||
|
||||
impl From<LoadBoolean> for Instruction {
|
||||
fn from(load_boolean: LoadBoolean) -> Self {
|
||||
let (a, options) = load_boolean.destination.as_index_and_a_options();
|
||||
let b = load_boolean.value as u16;
|
||||
let c = load_boolean.jump_next as u16;
|
||||
let metadata = Operation::LoadBoolean as u8;
|
||||
let a = load_boolean.destination;
|
||||
let b = load_boolean.value as u8;
|
||||
let c = load_boolean.jump_next as u8;
|
||||
|
||||
Instruction {
|
||||
operation: Operation::LOAD_BOOLEAN,
|
||||
options,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
use crate::{Destination, Instruction, Operation};
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct LoadConstant {
|
||||
pub destination: Destination,
|
||||
pub constant_index: u16,
|
||||
pub destination: u8,
|
||||
pub constant_index: u8,
|
||||
pub jump_next: bool,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for LoadConstant {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let destination = instruction.a;
|
||||
let constant_index = instruction.b;
|
||||
let jump_next = instruction.c != 0;
|
||||
|
||||
@ -22,16 +22,11 @@ impl From<&Instruction> for LoadConstant {
|
||||
|
||||
impl From<LoadConstant> for Instruction {
|
||||
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 c = load_constant.jump_next as u16;
|
||||
let c = load_constant.jump_next as u8;
|
||||
|
||||
Instruction {
|
||||
operation: Operation::LOAD_CONSTANT,
|
||||
options,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
use crate::{Destination, Instruction, Operation};
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct LoadList {
|
||||
pub destination: Destination,
|
||||
pub start_register: u16,
|
||||
pub destination: u8,
|
||||
pub start_register: u8,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for LoadList {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let destination = instruction.a;
|
||||
let start_register = instruction.b;
|
||||
|
||||
LoadList {
|
||||
@ -19,15 +19,11 @@ impl From<&Instruction> for LoadList {
|
||||
|
||||
impl From<LoadList> for Instruction {
|
||||
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 c = 0;
|
||||
|
||||
Instruction {
|
||||
operation: Operation::LOAD_LIST,
|
||||
options,
|
||||
a,
|
||||
b,
|
||||
c: 0,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
use crate::{Destination, Instruction, Operation};
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct LoadSelf {
|
||||
pub destination: Destination,
|
||||
pub destination: u8,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for LoadSelf {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let destination = instruction.a;
|
||||
|
||||
LoadSelf { destination }
|
||||
}
|
||||
@ -14,14 +14,11 @@ impl From<&Instruction> for LoadSelf {
|
||||
|
||||
impl From<LoadSelf> for Instruction {
|
||||
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 {
|
||||
operation: Operation::LOAD_SELF,
|
||||
options,
|
||||
a,
|
||||
b: 0,
|
||||
c: 0,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
//! ----- | -----------
|
||||
//! 0-8 | Operation code
|
||||
//! 9 | Flag for whether A is a local
|
||||
//! 10 | Flag for whether B argument is a constant
|
||||
//! 11 | Flag for whether C argument is a local
|
||||
//! 12 | Flag for whether B argument is a constant
|
||||
//! 13 | Flag for whether C argument is a local
|
||||
//! 14 | D Argument
|
||||
//! 15-16 | Unused
|
||||
//! 17-32 | A argument
|
||||
//! 33-48 | B argument
|
||||
//! 49-63 | C argument
|
||||
//! 0-4 | Operation code
|
||||
//! 5 | Flag indicating if the B argument is a constant
|
||||
//! 6 | Flag indicating if the C argument is a constant
|
||||
//! 7 | D field (boolean)
|
||||
//! 8-15 | A field (unsigned 8-bit integer)
|
||||
//! 16-23 | B field (unsigned 8-bit integer)
|
||||
//! 24-31 | C field (unsigned 8-bit integer)
|
||||
//!
|
||||
//! Be careful when working with instructions directly. When modifying an instruction, be sure to
|
||||
//! account for the fact that setting the A, B, or C arguments to 0 will have no effect. It is
|
||||
//! usually best to remove instructions and insert new ones in their place instead of mutating them.
|
||||
//! **Be careful when working with instructions directly**. When modifying an instruction's fields,
|
||||
//! you may also need to modify its flags. It is usually best to remove instructions and insert new
|
||||
//! ones in their place instead of mutating them.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ## Creating Instructions
|
||||
//!
|
||||
//! For each operation, there are two ways to create an instruction:
|
||||
//!
|
||||
//! - Use the associated function on `Instruction`
|
||||
//! - 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};
|
||||
@ -35,24 +36,63 @@
|
||||
//! assert_eq!(move_1, move_2);
|
||||
//! ```
|
||||
//!
|
||||
//! Use the `Destination` and `Argument` enums to create instructions. This is easier to read and
|
||||
//! enforces consistency in how the `Instruction` methods are called.
|
||||
//! Use the [`Argument`][] type when creating instructions. In addition to being easy to read and
|
||||
//! 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(
|
||||
//! Destination::Register(0),
|
||||
//! u8::Register(0),
|
||||
//! Argument::Local(1),
|
||||
//! Argument::Constant(2)
|
||||
//! );
|
||||
//! let add_2 = Instruction::from(Add {
|
||||
//! destination: Destination::Register(0),
|
||||
//! destination: u8::Register(0),
|
||||
//! left: Argument::Local(1),
|
||||
//! right: Argument::Constant(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 call;
|
||||
mod call_native;
|
||||
@ -117,23 +157,32 @@ use crate::NativeFunction;
|
||||
/// See the [module-level documentation](index.html) for more information.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct Instruction {
|
||||
operation: Operation,
|
||||
options: InstructionOptions,
|
||||
a: u16,
|
||||
b: u16,
|
||||
c: u16,
|
||||
pub metadata: u8,
|
||||
pub a: u8,
|
||||
pub b: u8,
|
||||
pub c: u8,
|
||||
}
|
||||
|
||||
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 })
|
||||
}
|
||||
|
||||
pub fn close(from: u16, to: u16) -> Instruction {
|
||||
pub fn close(from: u8, to: u8) -> Instruction {
|
||||
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 {
|
||||
destination,
|
||||
value,
|
||||
@ -141,11 +190,7 @@ impl Instruction {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn load_constant(
|
||||
destination: Destination,
|
||||
constant_index: u16,
|
||||
jump_next: bool,
|
||||
) -> Instruction {
|
||||
pub fn load_constant(destination: u8, constant_index: u8, jump_next: bool) -> Instruction {
|
||||
Instruction::from(LoadConstant {
|
||||
destination,
|
||||
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 {
|
||||
destination,
|
||||
start_register,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn load_self(destination: Destination) -> Instruction {
|
||||
pub fn load_self(destination: u8) -> Instruction {
|
||||
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 {
|
||||
destination,
|
||||
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 {
|
||||
local_index,
|
||||
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 {
|
||||
destination,
|
||||
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 {
|
||||
destination,
|
||||
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 {
|
||||
destination,
|
||||
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 {
|
||||
destination,
|
||||
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 {
|
||||
destination,
|
||||
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 {
|
||||
destination,
|
||||
argument,
|
||||
@ -233,12 +278,7 @@ impl Instruction {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn equal(
|
||||
destination: Destination,
|
||||
value: bool,
|
||||
left: Argument,
|
||||
right: Argument,
|
||||
) -> Instruction {
|
||||
pub fn equal(destination: u8, value: bool, left: Argument, right: Argument) -> Instruction {
|
||||
Instruction::from(Equal {
|
||||
destination,
|
||||
value,
|
||||
@ -247,12 +287,7 @@ impl Instruction {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn less(
|
||||
destination: Destination,
|
||||
value: bool,
|
||||
left: Argument,
|
||||
right: Argument,
|
||||
) -> Instruction {
|
||||
pub fn less(destination: u8, value: bool, left: Argument, right: Argument) -> Instruction {
|
||||
Instruction::from(Less {
|
||||
destination,
|
||||
value,
|
||||
@ -262,7 +297,7 @@ impl Instruction {
|
||||
}
|
||||
|
||||
pub fn less_equal(
|
||||
destination: Destination,
|
||||
destination: u8,
|
||||
value: bool,
|
||||
left: 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 {
|
||||
destination,
|
||||
argument,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn not(destination: Destination, argument: Argument) -> Instruction {
|
||||
pub fn not(destination: u8, argument: Argument) -> Instruction {
|
||||
Instruction::from(Not {
|
||||
destination,
|
||||
argument,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn jump(offset: u16, is_positive: bool) -> Instruction {
|
||||
pub fn jump(offset: u8, is_positive: bool) -> Instruction {
|
||||
Instruction::from(Jump {
|
||||
offset,
|
||||
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 {
|
||||
destination,
|
||||
function,
|
||||
@ -305,9 +340,9 @@ impl Instruction {
|
||||
}
|
||||
|
||||
pub fn call_native(
|
||||
destination: Destination,
|
||||
destination: u8,
|
||||
function: NativeFunction,
|
||||
argument_count: u16,
|
||||
argument_count: u8,
|
||||
) -> Instruction {
|
||||
Instruction::from(CallNative {
|
||||
destination,
|
||||
@ -323,55 +358,46 @@ impl Instruction {
|
||||
}
|
||||
|
||||
pub fn destination_as_argument(&self) -> Option<Argument> {
|
||||
match self.operation {
|
||||
Operation::LOAD_CONSTANT => Some(Argument::Constant(self.b)),
|
||||
Operation::GET_LOCAL => Some(Argument::Local(self.b)),
|
||||
Operation::LOAD_BOOLEAN
|
||||
| Operation::LOAD_LIST
|
||||
| Operation::LOAD_SELF
|
||||
| Operation::ADD
|
||||
| Operation::SUBTRACT
|
||||
| Operation::MULTIPLY
|
||||
| Operation::DIVIDE
|
||||
| Operation::MODULO
|
||||
| Operation::NEGATE
|
||||
| Operation::NOT
|
||||
| Operation::CALL
|
||||
| Operation::CALL_NATIVE => Some(Argument::Register(self.a)),
|
||||
match self.operation() {
|
||||
Operation::LoadConstant => Some(Argument::Constant(self.b)),
|
||||
Operation::LoadBoolean
|
||||
| Operation::LoadList
|
||||
| Operation::LoadSelf
|
||||
| Operation::GetLocal
|
||||
| Operation::Add
|
||||
| Operation::Subtract
|
||||
| Operation::Multiply
|
||||
| Operation::Divide
|
||||
| Operation::Modulo
|
||||
| Operation::Negate
|
||||
| Operation::Not
|
||||
| Operation::Call
|
||||
| Operation::CallNative => Some(Argument::Register(self.a)),
|
||||
_ => 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 {
|
||||
if self.options.b_is_constant() {
|
||||
let b_is_constant = self.metadata & 0b0010_0000 != 0;
|
||||
|
||||
if b_is_constant {
|
||||
Argument::Constant(self.b)
|
||||
} else if self.options.b_is_local() {
|
||||
Argument::Local(self.b)
|
||||
} else {
|
||||
Argument::Register(self.b)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
} else if self.options.b_is_local() {
|
||||
Argument::Local(self.b)
|
||||
} else {
|
||||
Argument::Register(self.b)
|
||||
};
|
||||
let right = if self.options.c_is_constant() {
|
||||
let right = if c_is_constant {
|
||||
Argument::Constant(self.c)
|
||||
} else if self.options.c_is_local() {
|
||||
Argument::Local(self.c)
|
||||
} else {
|
||||
Argument::Register(self.c)
|
||||
};
|
||||
@ -380,58 +406,53 @@ impl Instruction {
|
||||
}
|
||||
|
||||
pub fn yields_value(&self) -> bool {
|
||||
match self.operation {
|
||||
Operation::LOAD_BOOLEAN
|
||||
| Operation::LOAD_CONSTANT
|
||||
| Operation::LOAD_LIST
|
||||
| Operation::LOAD_SELF
|
||||
| Operation::GET_LOCAL
|
||||
| Operation::ADD
|
||||
| Operation::SUBTRACT
|
||||
| Operation::MULTIPLY
|
||||
| Operation::DIVIDE
|
||||
| Operation::MODULO
|
||||
| Operation::NEGATE
|
||||
| Operation::NOT
|
||||
| Operation::CALL => true,
|
||||
Operation::MOVE
|
||||
| Operation::CLOSE
|
||||
| Operation::SET_LOCAL
|
||||
| Operation::EQUAL
|
||||
| Operation::LESS
|
||||
| Operation::LESS_EQUAL
|
||||
| Operation::TEST
|
||||
| Operation::TEST_SET
|
||||
| Operation::JUMP
|
||||
| Operation::RETURN => false,
|
||||
Operation::CALL_NATIVE => {
|
||||
match self.operation() {
|
||||
Operation::LoadBoolean
|
||||
| Operation::LoadConstant
|
||||
| Operation::LoadList
|
||||
| Operation::LoadMap
|
||||
| Operation::LoadSelf
|
||||
| Operation::GetLocal
|
||||
| Operation::Add
|
||||
| Operation::Subtract
|
||||
| Operation::Multiply
|
||||
| Operation::Divide
|
||||
| Operation::Modulo
|
||||
| Operation::Power
|
||||
| Operation::Negate
|
||||
| Operation::Not
|
||||
| Operation::Equal
|
||||
| Operation::Less
|
||||
| Operation::LessEqual
|
||||
| Operation::Call => true,
|
||||
Operation::CallNative => {
|
||||
let function = NativeFunction::from(self.b);
|
||||
|
||||
function.returns_value()
|
||||
}
|
||||
_ => {
|
||||
if cfg!(debug_assertions) {
|
||||
panic!("Unknown operation {}", self.operation);
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
Operation::Move
|
||||
| Operation::Close
|
||||
| Operation::SetLocal
|
||||
| Operation::Test
|
||||
| Operation::TestSet
|
||||
| Operation::Jump
|
||||
| Operation::Return => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disassembly_info(&self) -> String {
|
||||
match self.operation {
|
||||
Operation::MOVE => {
|
||||
match self.operation() {
|
||||
Operation::Move => {
|
||||
let Move { from, to } = Move::from(self);
|
||||
|
||||
format!("R{to} = R{from}")
|
||||
}
|
||||
Operation::CLOSE => {
|
||||
Operation::Close => {
|
||||
let Close { from, to } = Close::from(self);
|
||||
|
||||
format!("R{from}..R{to}")
|
||||
}
|
||||
Operation::LOAD_BOOLEAN => {
|
||||
Operation::LoadBoolean => {
|
||||
let LoadBoolean {
|
||||
destination,
|
||||
value,
|
||||
@ -439,12 +460,12 @@ impl Instruction {
|
||||
} = LoadBoolean::from(self);
|
||||
|
||||
if jump_next {
|
||||
format!("{destination} = {value} && JUMP +1")
|
||||
format!("R{destination} = {value} && JUMP +1")
|
||||
} else {
|
||||
format!("{destination} = {value}")
|
||||
format!("R{destination} = {value}")
|
||||
}
|
||||
}
|
||||
Operation::LOAD_CONSTANT => {
|
||||
Operation::LoadConstant => {
|
||||
let LoadConstant {
|
||||
destination,
|
||||
constant_index,
|
||||
@ -452,87 +473,87 @@ impl Instruction {
|
||||
} = LoadConstant::from(self);
|
||||
|
||||
if jump_next {
|
||||
format!("{destination} = C{constant_index} JUMP +1")
|
||||
format!("R{destination} = C{constant_index} JUMP +1")
|
||||
} else {
|
||||
format!("{destination} = C{constant_index}")
|
||||
format!("R{destination} = C{constant_index}")
|
||||
}
|
||||
}
|
||||
Operation::LOAD_LIST => {
|
||||
Operation::LoadList => {
|
||||
let LoadList {
|
||||
destination,
|
||||
start_register,
|
||||
} = 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);
|
||||
|
||||
format!("{destination} = self")
|
||||
format!("R{destination} = self")
|
||||
}
|
||||
Operation::GET_LOCAL => {
|
||||
Operation::GetLocal => {
|
||||
let GetLocal {
|
||||
destination,
|
||||
local_index,
|
||||
} = GetLocal::from(self);
|
||||
|
||||
format!("{destination} = L{local_index}")
|
||||
format!("R{destination} = L{local_index}")
|
||||
}
|
||||
Operation::SET_LOCAL => {
|
||||
Operation::SetLocal => {
|
||||
let SetLocal {
|
||||
register_index: register,
|
||||
register_index,
|
||||
local_index,
|
||||
} = SetLocal::from(self);
|
||||
|
||||
format!("L{local_index} = R{register}")
|
||||
format!("L{local_index} = R{register_index}")
|
||||
}
|
||||
Operation::ADD => {
|
||||
Operation::Add => {
|
||||
let Add {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = Add::from(self);
|
||||
|
||||
format!("{destination} = {left} + {right}")
|
||||
format!("R{destination} = {left} + {right}")
|
||||
}
|
||||
Operation::SUBTRACT => {
|
||||
Operation::Subtract => {
|
||||
let Subtract {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = Subtract::from(self);
|
||||
|
||||
format!("{destination} = {left} - {right}")
|
||||
format!("R{destination} = {left} - {right}")
|
||||
}
|
||||
Operation::MULTIPLY => {
|
||||
Operation::Multiply => {
|
||||
let Multiply {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = Multiply::from(self);
|
||||
|
||||
format!("{destination} = {left} * {right}")
|
||||
format!("R{destination} = {left} * {right}")
|
||||
}
|
||||
Operation::DIVIDE => {
|
||||
Operation::Divide => {
|
||||
let Divide {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = Divide::from(self);
|
||||
|
||||
format!("{destination} = {left} / {right}")
|
||||
format!("R{destination} = {left} / {right}")
|
||||
}
|
||||
Operation::MODULO => {
|
||||
Operation::Modulo => {
|
||||
let Modulo {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = Modulo::from(self);
|
||||
|
||||
format!("{destination} = {left} % {right}")
|
||||
format!("R{destination} = {left} % {right}")
|
||||
}
|
||||
Operation::TEST => {
|
||||
Operation::Test => {
|
||||
let Test {
|
||||
argument,
|
||||
test_value: value,
|
||||
@ -541,7 +562,7 @@ impl Instruction {
|
||||
|
||||
format!("if {bang}{argument} {{ JUMP +1 }}",)
|
||||
}
|
||||
Operation::TEST_SET => {
|
||||
Operation::TestSet => {
|
||||
let TestSet {
|
||||
destination,
|
||||
argument,
|
||||
@ -549,9 +570,9 @@ impl Instruction {
|
||||
} = TestSet::from(self);
|
||||
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 {
|
||||
destination,
|
||||
value,
|
||||
@ -560,9 +581,9 @@ impl Instruction {
|
||||
} = Equal::from(self);
|
||||
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 {
|
||||
destination,
|
||||
value,
|
||||
@ -571,9 +592,9 @@ impl Instruction {
|
||||
} = Less::from(self);
|
||||
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 {
|
||||
destination,
|
||||
value,
|
||||
@ -582,25 +603,25 @@ impl Instruction {
|
||||
} = LessEqual::from(self);
|
||||
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 {
|
||||
destination,
|
||||
argument,
|
||||
} = Negate::from(self);
|
||||
|
||||
format!("{destination} = -{argument}")
|
||||
format!("R{destination} = -{argument}")
|
||||
}
|
||||
Operation::NOT => {
|
||||
Operation::Not => {
|
||||
let Not {
|
||||
destination,
|
||||
argument,
|
||||
} = Not::from(self);
|
||||
|
||||
format!("{destination} = !{argument}")
|
||||
format!("R{destination} = !{argument}")
|
||||
}
|
||||
Operation::JUMP => {
|
||||
Operation::Jump => {
|
||||
let Jump {
|
||||
offset,
|
||||
is_positive,
|
||||
@ -612,33 +633,33 @@ impl Instruction {
|
||||
format!("JUMP -{offset}")
|
||||
}
|
||||
}
|
||||
Operation::CALL => {
|
||||
Operation::Call => {
|
||||
let Call {
|
||||
destination,
|
||||
function,
|
||||
argument_count,
|
||||
} = 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;
|
||||
|
||||
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 {
|
||||
destination,
|
||||
function,
|
||||
argument_count,
|
||||
} = 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;
|
||||
|
||||
if function.returns_value() {
|
||||
format!("{destination} = {function}(R{arguments_start}..R{arguments_end})")
|
||||
format!("R{destination} = {function}(R{arguments_start}..R{arguments_end})")
|
||||
} else {
|
||||
format!("{function}(R{arguments_start}..R{arguments_end})")
|
||||
}
|
||||
}
|
||||
Operation::RETURN => {
|
||||
Operation::Return => {
|
||||
let Return {
|
||||
should_return_value,
|
||||
} = Return::from(self);
|
||||
@ -651,7 +672,7 @@ impl Instruction {
|
||||
}
|
||||
_ => {
|
||||
if cfg!(debug_assertions) {
|
||||
panic!("Unknown operation {}", self.operation);
|
||||
panic!("Unknown operation {}", self.operation());
|
||||
} else {
|
||||
"RETURN".to_string()
|
||||
}
|
||||
@ -662,61 +683,20 @@ impl Instruction {
|
||||
|
||||
impl Debug for Instruction {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
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}"),
|
||||
}
|
||||
write!(f, "{} {}", self.operation(), self.disassembly_info())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Argument {
|
||||
Constant(u16),
|
||||
Local(u16),
|
||||
Register(u16),
|
||||
Constant(u8),
|
||||
Register(u8),
|
||||
}
|
||||
|
||||
impl Argument {
|
||||
pub fn index(&self) -> u16 {
|
||||
pub fn index(&self) -> u8 {
|
||||
match self {
|
||||
Argument::Constant(index) => *index,
|
||||
Argument::Local(index) => *index,
|
||||
Argument::Register(index) => *index,
|
||||
}
|
||||
}
|
||||
@ -725,26 +705,20 @@ impl Argument {
|
||||
matches!(self, Argument::Constant(_))
|
||||
}
|
||||
|
||||
pub fn is_local(&self) -> bool {
|
||||
matches!(self, Argument::Local(_))
|
||||
}
|
||||
|
||||
pub fn is_register(&self) -> bool {
|
||||
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 {
|
||||
Argument::Constant(index) => (*index, InstructionOptions::B_IS_CONSTANT),
|
||||
Argument::Local(index) => (*index, InstructionOptions::B_IS_LOCAL),
|
||||
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 {
|
||||
Argument::Constant(index) => (*index, InstructionOptions::C_IS_CONSTANT),
|
||||
Argument::Local(index) => (*index, InstructionOptions::C_IS_LOCAL),
|
||||
Argument::Register(index) => (*index, InstructionOptions::empty()),
|
||||
}
|
||||
}
|
||||
@ -754,8 +728,27 @@ impl Display for Argument {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Argument::Constant(index) => write!(f, "C{index}"),
|
||||
Argument::Local(index) => write!(f, "L{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);
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
use crate::{Argument, Destination, Instruction, Operation};
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Modulo {
|
||||
pub destination: Destination,
|
||||
pub destination: u8,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Modulo {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let destination = instruction.a;
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Modulo {
|
||||
@ -21,16 +21,11 @@ impl From<&Instruction> for Modulo {
|
||||
|
||||
impl From<Modulo> for Instruction {
|
||||
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 (c, c_options) = modulo.right.as_index_and_c_options();
|
||||
let metadata = Operation::Modulo as u8 | b_options.bits() | c_options.bits();
|
||||
|
||||
Instruction {
|
||||
operation: Operation::MODULO,
|
||||
options: a_options | b_options | c_options,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,8 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionOptions;
|
||||
|
||||
pub struct Move {
|
||||
pub from: u16,
|
||||
pub to: u16,
|
||||
pub from: u8,
|
||||
pub to: u8,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Move {
|
||||
@ -18,12 +16,11 @@ impl From<&Instruction> for Move {
|
||||
|
||||
impl From<Move> for Instruction {
|
||||
fn from(r#move: Move) -> Self {
|
||||
Instruction {
|
||||
operation: Operation::MOVE,
|
||||
options: InstructionOptions::empty(),
|
||||
a: 0,
|
||||
b: r#move.from,
|
||||
c: r#move.to,
|
||||
}
|
||||
let metadata = Operation::Move as u8;
|
||||
let a = 0;
|
||||
let b = r#move.from;
|
||||
let c = r#move.to;
|
||||
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
use crate::{Argument, Destination, Instruction, Operation};
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Multiply {
|
||||
pub destination: Destination,
|
||||
pub destination: u8,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Multiply {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let destination = instruction.a;
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Multiply {
|
||||
@ -21,16 +21,11 @@ impl From<&Instruction> for Multiply {
|
||||
|
||||
impl From<Multiply> for Instruction {
|
||||
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 (c, c_options) = multiply.right.as_index_and_c_options();
|
||||
let metadata = Operation::Multiply as u8 | b_options.bits() | c_options.bits();
|
||||
|
||||
Instruction {
|
||||
operation: Operation::MULTIPLY,
|
||||
options: a_options | b_options | c_options,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
use crate::{Argument, Destination, Instruction, Operation};
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Negate {
|
||||
pub destination: Destination,
|
||||
pub destination: u8,
|
||||
pub argument: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Negate {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let destination = instruction.a;
|
||||
let argument = instruction.b_as_argument();
|
||||
|
||||
Negate {
|
||||
@ -19,15 +19,11 @@ impl From<&Instruction> for Negate {
|
||||
|
||||
impl From<Negate> for Instruction {
|
||||
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 c = 0;
|
||||
let metadata = Operation::Negate as u8 | b_options.bits();
|
||||
|
||||
Instruction {
|
||||
operation: Operation::NEGATE,
|
||||
options: a_options | b_options,
|
||||
a,
|
||||
b,
|
||||
c: 0,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
use crate::{Argument, Destination, Instruction, Operation};
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Not {
|
||||
pub destination: Destination,
|
||||
pub destination: u8,
|
||||
pub argument: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Not {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let destination = instruction.a;
|
||||
let argument = instruction.b_as_argument();
|
||||
|
||||
Not {
|
||||
@ -19,15 +19,11 @@ impl From<&Instruction> for Not {
|
||||
|
||||
impl From<Not> for Instruction {
|
||||
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 c = 0;
|
||||
let metadata = Operation::Not as u8 | b_options.bits();
|
||||
|
||||
Instruction {
|
||||
operation: Operation::NOT,
|
||||
options: a_options | b_options,
|
||||
a,
|
||||
b,
|
||||
c: 0,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -4,75 +4,141 @@ use std::fmt::{self, Debug, Display, Formatter};
|
||||
|
||||
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)]
|
||||
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 {
|
||||
pub const MOVE: Operation = Operation(0);
|
||||
pub const CLOSE: Operation = Operation(1);
|
||||
pub const LOAD_BOOLEAN: Operation = Operation(2);
|
||||
pub const LOAD_CONSTANT: Operation = Operation(3);
|
||||
pub const LOAD_LIST: Operation = Operation(4);
|
||||
pub const LOAD_SELF: Operation = Operation(5);
|
||||
pub const GET_LOCAL: Operation = Operation(6);
|
||||
pub const SET_LOCAL: Operation = Operation(7);
|
||||
pub const ADD: Operation = Operation(8);
|
||||
pub const SUBTRACT: Operation = Operation(9);
|
||||
pub const MULTIPLY: Operation = Operation(10);
|
||||
pub const DIVIDE: Operation = Operation(11);
|
||||
pub const MODULO: Operation = Operation(12);
|
||||
pub const TEST: Operation = Operation(13);
|
||||
pub const TEST_SET: Operation = Operation(14);
|
||||
pub const EQUAL: Operation = Operation(15);
|
||||
pub const LESS: Operation = Operation(16);
|
||||
pub const LESS_EQUAL: Operation = Operation(17);
|
||||
pub const NEGATE: Operation = Operation(18);
|
||||
pub const NOT: Operation = Operation(19);
|
||||
pub const CALL: Operation = Operation(20);
|
||||
pub const CALL_NATIVE: Operation = Operation(21);
|
||||
pub const JUMP: Operation = Operation(22);
|
||||
pub const RETURN: Operation = Operation(23);
|
||||
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::MOVE => "MOVE",
|
||||
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",
|
||||
impl From<u8> for Operation {
|
||||
fn from(byte: u8) -> Self {
|
||||
match byte {
|
||||
MOVE_BYTE => Self::Move,
|
||||
CLOSE_BYTE => Self::Close,
|
||||
LOAD_BOOLEAN_BYTE => Self::LoadBoolean,
|
||||
LOAD_CONSTANT_BYTE => Self::LoadConstant,
|
||||
LOAD_LIST_BYTE => Self::LoadList,
|
||||
LOAD_MAP_BYTE => Self::LoadMap,
|
||||
LOAD_SELF_BYTE => Self::LoadSelf,
|
||||
GET_LOCAL_BYTE => Self::GetLocal,
|
||||
SET_LOCAL_BYTE => Self::SetLocal,
|
||||
ADD_BYTE => Self::Add,
|
||||
SUBTRACT_BYTE => Self::Subtract,
|
||||
MULTIPLY_BYTE => Self::Multiply,
|
||||
DIVIDE_BYTE => Self::Divide,
|
||||
MODULO_BYTE => Self::Modulo,
|
||||
POWER_BYTE => Self::Power,
|
||||
TEST_BYTE => Self::Test,
|
||||
TEST_SET_BYTE => Self::TestSet,
|
||||
EQUAL_BYTE => Self::Equal,
|
||||
LESS_BYTE => Self::Less,
|
||||
LESS_EQUAL_BYTE => Self::LessEqual,
|
||||
NEGATE_BYTE => Self::Negate,
|
||||
NOT_BYTE => Self::Not,
|
||||
CALL_BYTE => Self::Call,
|
||||
CALL_NATIVE_BYTE => Self::CallNative,
|
||||
JUMP_BYTE => Self::Jump,
|
||||
RETURN_BYTE => Self::Return,
|
||||
_ => {
|
||||
if cfg!(debug_assertions) {
|
||||
panic!("Unknown operation code {}", self.0);
|
||||
panic!("Invalid operation byte: {}", byte)
|
||||
} 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 {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
|
@ -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 serde::{Deserialize, Serialize};
|
||||
|
||||
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)]
|
||||
pub struct InstructionOptions: u8 {
|
||||
const A_IS_LOCAL = 0b00000001;
|
||||
const B_IS_CONSTANT = 0b0010_0000;
|
||||
|
||||
const B_IS_CONSTANT = 0b00000010;
|
||||
const B_IS_LOCAL = 0b00000100;
|
||||
const C_IS_CONSTANT = 0b0100_0000;
|
||||
|
||||
const C_IS_CONSTANT = 0b00001000;
|
||||
const C_IS_LOCAL = 0b00010000;
|
||||
|
||||
const D_IS_TRUE = 0b00100000;
|
||||
const D_IS_TRUE = 0b1000_0000;
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionOptions {
|
||||
pub fn a_is_local(self) -> bool {
|
||||
self.contains(Self::A_IS_LOCAL)
|
||||
}
|
||||
|
||||
pub fn b_is_constant(self) -> bool {
|
||||
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 {
|
||||
self.contains(Self::C_IS_CONSTANT)
|
||||
}
|
||||
|
||||
pub fn c_is_local(self) -> bool {
|
||||
self.contains(Self::C_IS_LOCAL)
|
||||
}
|
||||
|
||||
pub fn d(self) -> bool {
|
||||
self.contains(Self::D_IS_TRUE)
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionOptions;
|
||||
|
||||
pub struct Return {
|
||||
pub should_return_value: bool,
|
||||
}
|
||||
@ -18,14 +16,11 @@ impl From<&Instruction> for Return {
|
||||
|
||||
impl From<Return> for Instruction {
|
||||
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 {
|
||||
operation: Operation::RETURN,
|
||||
options: InstructionOptions::empty(),
|
||||
a: 0,
|
||||
b,
|
||||
c: 0,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,8 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
use super::InstructionOptions;
|
||||
|
||||
pub struct SetLocal {
|
||||
pub register_index: u16,
|
||||
pub local_index: u16,
|
||||
pub register_index: u8,
|
||||
pub local_index: u8,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for SetLocal {
|
||||
@ -21,15 +19,11 @@ impl From<&Instruction> for SetLocal {
|
||||
|
||||
impl From<SetLocal> for Instruction {
|
||||
fn from(set_local: SetLocal) -> Self {
|
||||
let metadata = Operation::SetLocal as u8;
|
||||
let a = 0;
|
||||
let b = set_local.register_index;
|
||||
let c = set_local.local_index;
|
||||
|
||||
Instruction {
|
||||
operation: Operation::SET_LOCAL,
|
||||
options: InstructionOptions::empty(),
|
||||
a: 0,
|
||||
b,
|
||||
c,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
use crate::{Argument, Destination, Instruction, Operation};
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Subtract {
|
||||
pub destination: Destination,
|
||||
pub destination: u8,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Subtract {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let destination = instruction.a;
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Subtract {
|
||||
@ -21,16 +21,11 @@ impl From<&Instruction> for Subtract {
|
||||
|
||||
impl From<Subtract> for Instruction {
|
||||
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 (c, c_options) = subtract.right.as_index_and_c_options();
|
||||
let metadata = Operation::Subtract as u8 | b_options.bits() | c_options.bits();
|
||||
|
||||
Instruction {
|
||||
operation: Operation::SUBTRACT,
|
||||
options: a_options | b_options | c_options,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -19,15 +19,11 @@ impl From<&Instruction> for Test {
|
||||
|
||||
impl From<Test> for Instruction {
|
||||
fn from(test: Test) -> Self {
|
||||
let a = 0;
|
||||
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 {
|
||||
operation: Operation::TEST,
|
||||
options,
|
||||
a: 0,
|
||||
b,
|
||||
c,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
use crate::{Argument, Destination, Instruction, Operation};
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct TestSet {
|
||||
pub destination: Destination,
|
||||
pub destination: u8,
|
||||
pub argument: Argument,
|
||||
pub test_value: bool,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for TestSet {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let destination = instruction.a_as_destination();
|
||||
let destination = instruction.a;
|
||||
let argument = instruction.b_as_argument();
|
||||
let test_value = instruction.c != 0;
|
||||
|
||||
@ -22,16 +22,11 @@ impl From<&Instruction> for TestSet {
|
||||
|
||||
impl From<TestSet> for Instruction {
|
||||
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 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 {
|
||||
operation: Operation::TEST_SET,
|
||||
options: a_options | b_options,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
}
|
||||
Instruction { metadata, a, b, c }
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,6 @@ pub mod dust_error;
|
||||
pub mod instruction;
|
||||
pub mod lexer;
|
||||
pub mod native_function;
|
||||
pub mod optimize;
|
||||
pub mod scope;
|
||||
pub mod token;
|
||||
pub mod r#type;
|
||||
@ -46,10 +45,9 @@ pub use crate::chunk::{Chunk, Local};
|
||||
pub use crate::compiler::{compile, CompileError, Compiler};
|
||||
pub use crate::disassembler::Disassembler;
|
||||
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::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::scope::Scope;
|
||||
pub use crate::token::{write_token_list, Token, TokenKind, TokenOwned};
|
||||
|
@ -3,7 +3,7 @@ use std::io::{self, stdout, Write};
|
||||
use crate::{ConcreteValue, Instruction, NativeFunctionError, Value, Vm, 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 {
|
||||
None
|
||||
} 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> {
|
||||
let argument_count = instruction.c();
|
||||
let argument_count = instruction.c;
|
||||
|
||||
if argument_count != 1 {
|
||||
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> {
|
||||
let argument_count = instruction.c();
|
||||
let argument_count = instruction.c;
|
||||
|
||||
if argument_count != 0 {
|
||||
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> {
|
||||
let to_register = instruction.a();
|
||||
let argument_count = instruction.c();
|
||||
let to_register = instruction.a;
|
||||
let argument_count = instruction.c;
|
||||
let mut stdout = stdout();
|
||||
let map_err = |io_error: io::Error| {
|
||||
VmError::NativeFunction(NativeFunctionError::Io {
|
||||
@ -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> {
|
||||
let to_register = instruction.a();
|
||||
let argument_count = instruction.c();
|
||||
let to_register = instruction.a;
|
||||
let argument_count = instruction.c;
|
||||
let mut stdout = stdout();
|
||||
let map_err = |io_error: io::Error| {
|
||||
VmError::NativeFunction(NativeFunctionError::Io {
|
||||
|
@ -75,8 +75,8 @@ macro_rules! define_native_function {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u16> for NativeFunction {
|
||||
fn from(bytes: u16) -> Self {
|
||||
impl From<u8> for NativeFunction {
|
||||
fn from(bytes: u8) -> Self {
|
||||
match bytes {
|
||||
$(
|
||||
$bytes => NativeFunction::$name,
|
||||
|
@ -278,13 +278,13 @@ impl Ord for Type {
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct FunctionType {
|
||||
pub type_parameters: Option<SmallVec<[u16; 4]>>,
|
||||
pub value_parameters: Option<SmallVec<[(u16, Type); 4]>>,
|
||||
pub type_parameters: Option<SmallVec<[u8; 4]>>,
|
||||
pub value_parameters: Option<SmallVec<[(u8, Type); 4]>>,
|
||||
pub return_type: Type,
|
||||
}
|
||||
|
||||
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>,
|
||||
value_parameters: Option<U>,
|
||||
return_type: Type,
|
||||
|
@ -8,8 +8,7 @@ use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::{
|
||||
compile, instruction::*, AbstractValue, AnnotatedError, Argument, Chunk, ConcreteValue,
|
||||
Destination, DustError, Instruction, NativeFunctionError, Operation, Span, Value, ValueError,
|
||||
ValueRef,
|
||||
DustError, Instruction, NativeFunctionError, Operation, Span, Value, ValueError, ValueRef,
|
||||
};
|
||||
|
||||
pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
|
||||
@ -28,10 +27,9 @@ pub struct Vm<'a> {
|
||||
|
||||
chunk: &'a Chunk,
|
||||
parent: Option<&'a Vm<'a>>,
|
||||
local_definitions: SmallVec<[Option<u16>; 16]>,
|
||||
|
||||
ip: usize,
|
||||
last_assigned_register: Option<u16>,
|
||||
last_assigned_register: Option<u8>,
|
||||
source: &'a str,
|
||||
}
|
||||
|
||||
@ -41,7 +39,6 @@ impl<'a> Vm<'a> {
|
||||
chunk,
|
||||
stack: smallvec![Register::Empty; chunk.stack_size()],
|
||||
parent,
|
||||
local_definitions: smallvec![None; chunk.locals().len()],
|
||||
ip: 0,
|
||||
last_assigned_register: None,
|
||||
source,
|
||||
@ -53,7 +50,10 @@ impl<'a> Vm<'a> {
|
||||
}
|
||||
|
||||
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> {
|
||||
@ -100,11 +100,10 @@ impl<'a> Vm<'a> {
|
||||
value,
|
||||
jump_next,
|
||||
} = LoadBoolean::from(&instruction);
|
||||
let register_index = self.get_destination(destination)?;
|
||||
let boolean = ConcreteValue::Boolean(value).to_value();
|
||||
let register = Register::Value(boolean);
|
||||
|
||||
self.set_register(register_index, register)?;
|
||||
self.set_register(destination, register)?;
|
||||
|
||||
if jump_next {
|
||||
self.jump(1, true);
|
||||
@ -116,10 +115,9 @@ impl<'a> Vm<'a> {
|
||||
constant_index,
|
||||
jump_next,
|
||||
} = LoadConstant::from(&instruction);
|
||||
let register_index = self.get_destination(destination)?;
|
||||
let register = Register::Pointer(Pointer::Constant(constant_index));
|
||||
|
||||
self.set_register(register_index, register)?;
|
||||
self.set_register(destination, register)?;
|
||||
|
||||
if jump_next {
|
||||
self.jump(1, true);
|
||||
@ -130,10 +128,9 @@ impl<'a> Vm<'a> {
|
||||
destination,
|
||||
start_register,
|
||||
} = LoadList::from(&instruction);
|
||||
let register_index = self.get_destination(destination)?;
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
@ -146,47 +143,33 @@ impl<'a> Vm<'a> {
|
||||
let register =
|
||||
Register::Value(AbstractValue::List { items: pointers }.to_value());
|
||||
|
||||
self.set_register(register_index, register)?;
|
||||
self.set_register(destination, register)?;
|
||||
}
|
||||
Operation::LoadSelf => {
|
||||
let LoadSelf { destination } = LoadSelf::from(&instruction);
|
||||
let register_index = self.get_destination(destination)?;
|
||||
let register = Register::Value(AbstractValue::FunctionSelf.to_value());
|
||||
|
||||
self.set_register(register_index, register)?;
|
||||
}
|
||||
Operation::DefineLocal => {
|
||||
let DefineLocal {
|
||||
register,
|
||||
local_index,
|
||||
is_mutable,
|
||||
} = DefineLocal::from(&instruction);
|
||||
|
||||
self.local_definitions[local_index as usize] = Some(register);
|
||||
self.set_register(destination, register)?;
|
||||
}
|
||||
Operation::GetLocal => {
|
||||
let GetLocal {
|
||||
destination,
|
||||
local_index,
|
||||
} = GetLocal::from(&instruction);
|
||||
let register_index = self.get_destination(destination)?;
|
||||
let local_register = self.local_definitions[local_index as usize].ok_or(
|
||||
VmError::UndefinedLocal {
|
||||
local_index,
|
||||
position: self.current_position(),
|
||||
},
|
||||
)?;
|
||||
let local_register = self.get_local_register(local_index)?;
|
||||
let register = Register::Pointer(Pointer::Stack(local_register));
|
||||
|
||||
self.set_register(register_index, register)?;
|
||||
self.set_register(destination, register)?;
|
||||
}
|
||||
Operation::SetLocal => {
|
||||
let SetLocal {
|
||||
register_index: register,
|
||||
register_index,
|
||||
local_index,
|
||||
} = 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 => {
|
||||
let Add {
|
||||
@ -194,26 +177,20 @@ impl<'a> Vm<'a> {
|
||||
left,
|
||||
right,
|
||||
} = Add::from(&instruction);
|
||||
let register_index = self.get_destination(destination)?;
|
||||
let left = self.get_argument(left)?;
|
||||
let right = self.get_argument(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!(
|
||||
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))?;
|
||||
self.set_register(destination, Register::Value(sum))?;
|
||||
}
|
||||
Operation::Subtract => {
|
||||
let Subtract {
|
||||
@ -221,15 +198,20 @@ impl<'a> Vm<'a> {
|
||||
left,
|
||||
right,
|
||||
} = Subtract::from(&instruction);
|
||||
let register_index = self.get_destination(destination)?;
|
||||
let left = self.get_argument(left)?;
|
||||
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,
|
||||
position: self.current_position(),
|
||||
})?;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
self.set_register(register_index, Register::Value(difference))?;
|
||||
self.set_register(destination, Register::Value(difference))?;
|
||||
}
|
||||
Operation::Multiply => {
|
||||
let Multiply {
|
||||
@ -237,15 +219,20 @@ impl<'a> Vm<'a> {
|
||||
left,
|
||||
right,
|
||||
} = Multiply::from(&instruction);
|
||||
let register_index = self.get_destination(destination)?;
|
||||
let left = self.get_argument(left)?;
|
||||
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,
|
||||
position: self.current_position(),
|
||||
})?;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
self.set_register(register_index, Register::Value(product))?;
|
||||
self.set_register(destination, Register::Value(product))?;
|
||||
}
|
||||
Operation::Divide => {
|
||||
let Divide {
|
||||
@ -253,15 +240,20 @@ impl<'a> Vm<'a> {
|
||||
left,
|
||||
right,
|
||||
} = Divide::from(&instruction);
|
||||
let register_index = self.get_destination(destination)?;
|
||||
let left = self.get_argument(left)?;
|
||||
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,
|
||||
position: self.current_position(),
|
||||
})?;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
self.set_register(register_index, Register::Value(quotient))?;
|
||||
self.set_register(destination, Register::Value(quotient))?;
|
||||
}
|
||||
Operation::Modulo => {
|
||||
let Modulo {
|
||||
@ -269,15 +261,20 @@ impl<'a> Vm<'a> {
|
||||
left,
|
||||
right,
|
||||
} = Modulo::from(&instruction);
|
||||
let register_index = self.get_destination(destination)?;
|
||||
let left = self.get_argument(left)?;
|
||||
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,
|
||||
position: self.current_position(),
|
||||
})?;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
self.set_register(register_index, Register::Value(remainder))?;
|
||||
self.set_register(destination, Register::Value(remainder))?;
|
||||
}
|
||||
Operation::Test => {
|
||||
let Test {
|
||||
@ -305,7 +302,6 @@ impl<'a> Vm<'a> {
|
||||
argument,
|
||||
test_value,
|
||||
} = TestSet::from(&instruction);
|
||||
let register_index = self.get_destination(destination)?;
|
||||
let value = self.get_argument(argument)?;
|
||||
let boolean = if let ValueRef::Concrete(ConcreteValue::Boolean(boolean)) = value
|
||||
{
|
||||
@ -322,24 +318,20 @@ impl<'a> Vm<'a> {
|
||||
} else {
|
||||
let pointer = match argument {
|
||||
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),
|
||||
};
|
||||
let register = Register::Pointer(pointer);
|
||||
|
||||
self.set_register(register_index, register)?;
|
||||
self.set_register(destination, register)?;
|
||||
}
|
||||
}
|
||||
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 right = self.get_argument(right)?;
|
||||
let equal_result = left.equal(right).map_err(|error| VmError::Value {
|
||||
@ -355,64 +347,79 @@ impl<'a> Vm<'a> {
|
||||
position: self.current_position(),
|
||||
});
|
||||
};
|
||||
let comparison = is_equal == value;
|
||||
let register =
|
||||
Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
|
||||
|
||||
if is_equal == value {
|
||||
self.jump(1, true);
|
||||
}
|
||||
self.set_register(destination, register)?;
|
||||
}
|
||||
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 right = self.get_argument(right)?;
|
||||
let less_result = left.less_than(right);
|
||||
|
||||
assert!(
|
||||
less_result.is_ok(),
|
||||
"VM Error: {}\nPosition: {}",
|
||||
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 {
|
||||
let less_than_value = match less_result {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
return Err(VmError::Value {
|
||||
error,
|
||||
position: self.current_position(),
|
||||
})?;
|
||||
let is_less_than_or_equal =
|
||||
if let Value::Concrete(ConcreteValue::Boolean(boolean)) =
|
||||
less_or_equal_result
|
||||
{
|
||||
boolean
|
||||
} else {
|
||||
});
|
||||
}
|
||||
};
|
||||
let is_less_than = match less_than_value {
|
||||
Value::Concrete(ConcreteValue::Boolean(boolean)) => boolean,
|
||||
_ => {
|
||||
return Err(VmError::ExpectedBoolean {
|
||||
found: less_or_equal_result,
|
||||
found: less_than_value,
|
||||
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 => {
|
||||
let Negate {
|
||||
@ -424,10 +431,9 @@ impl<'a> Vm<'a> {
|
||||
error,
|
||||
position: self.current_position(),
|
||||
})?;
|
||||
let register_index = self.get_destination(destination)?;
|
||||
let register = Register::Value(negated);
|
||||
|
||||
self.set_register(register_index, register)?;
|
||||
self.set_register(destination, register)?;
|
||||
}
|
||||
Operation::Not => {
|
||||
let Not {
|
||||
@ -439,10 +445,9 @@ impl<'a> Vm<'a> {
|
||||
error,
|
||||
position: self.current_position(),
|
||||
})?;
|
||||
let register_index = self.get_destination(destination)?;
|
||||
let register = Register::Value(not);
|
||||
|
||||
self.set_register(register_index, register)?;
|
||||
self.set_register(destination, register)?;
|
||||
}
|
||||
Operation::Jump => {
|
||||
let Jump {
|
||||
@ -458,7 +463,6 @@ impl<'a> Vm<'a> {
|
||||
function,
|
||||
argument_count,
|
||||
} = Call::from(&instruction);
|
||||
let register_index = self.get_destination(destination)?;
|
||||
let function = self.get_argument(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 first_argument_index = register_index - argument_count;
|
||||
let first_argument_index = destination - argument_count;
|
||||
|
||||
for (argument_index, argument_register_index) in
|
||||
(first_argument_index..register_index).enumerate()
|
||||
(first_argument_index..destination).enumerate()
|
||||
{
|
||||
function_vm.set_register(
|
||||
argument_index as u16,
|
||||
argument_index as u8,
|
||||
Register::Pointer(Pointer::ParentStack(argument_register_index)),
|
||||
)?;
|
||||
|
||||
function_vm.local_definitions[argument_index] = Some(argument_index as u16);
|
||||
}
|
||||
|
||||
let return_value = function_vm.run()?;
|
||||
@ -490,7 +492,7 @@ impl<'a> Vm<'a> {
|
||||
if let Some(concrete_value) = return_value {
|
||||
let register = Register::Value(concrete_value.to_value());
|
||||
|
||||
self.set_register(register_index, register)?;
|
||||
self.set_register(destination, register)?;
|
||||
}
|
||||
}
|
||||
Operation::CallNative => {
|
||||
@ -502,10 +504,9 @@ impl<'a> Vm<'a> {
|
||||
let return_value = function.call(self, instruction)?;
|
||||
|
||||
if let Some(value) = return_value {
|
||||
let register_index = self.get_destination(destination)?;
|
||||
let register = Register::Value(value);
|
||||
|
||||
self.set_register(register_index, register)?;
|
||||
self.set_register(destination, register)?;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
assert!(
|
||||
@ -589,7 +591,7 @@ impl<'a> Vm<'a> {
|
||||
|
||||
pub(crate) fn open_register_allow_empty(
|
||||
&self,
|
||||
register_index: u16,
|
||||
register_index: u8,
|
||||
) -> Result<Option<ValueRef>, VmError> {
|
||||
let register_index = register_index as usize;
|
||||
let register =
|
||||
@ -628,16 +630,6 @@ impl<'a> Vm<'a> {
|
||||
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
|
||||
fn get_argument(&self, argument: Argument) -> Result<ValueRef, VmError> {
|
||||
let value_ref = match argument {
|
||||
@ -645,18 +637,13 @@ impl<'a> Vm<'a> {
|
||||
ValueRef::Concrete(self.get_constant(constant_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)
|
||||
}
|
||||
|
||||
#[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);
|
||||
|
||||
let to_register = to_register as usize;
|
||||
@ -672,7 +659,7 @@ impl<'a> Vm<'a> {
|
||||
}
|
||||
|
||||
#[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 constants = self.chunk.constants();
|
||||
|
||||
@ -685,18 +672,18 @@ impl<'a> Vm<'a> {
|
||||
}
|
||||
|
||||
#[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 locals = self.chunk.locals();
|
||||
|
||||
assert!(
|
||||
local_index < self.local_definitions.len(),
|
||||
local_index < locals.len(),
|
||||
"VM Error: Local index out of bounds"
|
||||
);
|
||||
|
||||
self.local_definitions[local_index].ok_or_else(|| VmError::UndefinedLocal {
|
||||
local_index: local_index as u16,
|
||||
position: self.current_position(),
|
||||
})
|
||||
let register_index = locals[local_index].register_index;
|
||||
|
||||
Ok(register_index)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -715,7 +702,7 @@ impl<'a> Vm<'a> {
|
||||
)
|
||||
);
|
||||
|
||||
let instruction = instructions[self.ip];
|
||||
let (instruction, _) = instructions[self.ip];
|
||||
|
||||
self.ip += 1;
|
||||
|
||||
@ -742,10 +729,10 @@ impl Display for Register {
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum Pointer {
|
||||
Stack(u16),
|
||||
Constant(u16),
|
||||
ParentStack(u16),
|
||||
ParentConstant(u16),
|
||||
Stack(u8),
|
||||
Constant(u8),
|
||||
ParentStack(u8),
|
||||
ParentConstant(u8),
|
||||
}
|
||||
|
||||
impl Display for Pointer {
|
||||
@ -789,7 +776,7 @@ pub enum VmError {
|
||||
|
||||
// Local errors
|
||||
UndefinedLocal {
|
||||
local_index: u16,
|
||||
local_index: u8,
|
||||
position: Span,
|
||||
},
|
||||
|
||||
|
@ -6,7 +6,7 @@ fn add_assign_expects_mutable_variable() {
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(CreateReport::Compile {
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::ExpectedMutableVariable {
|
||||
found: Token::Integer("1").to_owned(),
|
||||
position: Span(0, 1)
|
||||
@ -22,7 +22,7 @@ fn divide_assign_expects_mutable_variable() {
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(CreateReport::Compile {
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::ExpectedMutableVariable {
|
||||
found: Token::Integer("1").to_owned(),
|
||||
position: Span(0, 1)
|
||||
@ -38,7 +38,7 @@ fn multiply_assign_expects_mutable_variable() {
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(CreateReport::Compile {
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::ExpectedMutableVariable {
|
||||
found: Token::Integer("1").to_owned(),
|
||||
position: Span(0, 1)
|
||||
@ -54,7 +54,7 @@ fn subtract_assign_expects_mutable_variable() {
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(CreateReport::Compile {
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::ExpectedMutableVariable {
|
||||
found: Token::Integer("1").to_owned(),
|
||||
position: Span(0, 1)
|
||||
@ -70,7 +70,7 @@ fn modulo_assign_expects_mutable_variable() {
|
||||
|
||||
assert_eq!(
|
||||
compile(source),
|
||||
Err(CreateReport::Compile {
|
||||
Err(DustError::Compile {
|
||||
error: CompileError::ExpectedMutableVariable {
|
||||
found: Token::Integer("1").to_owned(),
|
||||
position: Span(0, 1)
|
||||
|
@ -11,7 +11,7 @@ fn constant() {
|
||||
FunctionType {
|
||||
type_parameters: None,
|
||||
value_parameters: None,
|
||||
return_type: Box::new(Type::Integer)
|
||||
return_type: Type::Integer
|
||||
},
|
||||
vec![
|
||||
(
|
||||
|
Loading…
Reference in New Issue
Block a user