Refactor to use 64-bit instructions
This commit is contained in:
parent
c5e0d8ef1e
commit
a94cc11069
@ -13,9 +13,15 @@ use std::{
|
||||
use colored::Colorize;
|
||||
|
||||
use crate::{
|
||||
value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Instruction,
|
||||
LexError, Lexer, Local, NativeFunction, Operation, Optimizer, Scope, Span, Token, TokenKind,
|
||||
TokenOwned, Type, TypeConflict,
|
||||
instruction::{
|
||||
Add, Call, CallNative, Close, DefineLocal, Divide, Equal, GetLocal, Jump, Less, LessEqual,
|
||||
LoadBoolean, LoadConstant, LoadList, LoadSelf, Modulo, Move, Multiply, Negate, Not, Return,
|
||||
SetLocal, Subtract, Test,
|
||||
},
|
||||
value::ConcreteValue,
|
||||
AnnotatedError, Argument, Chunk, ChunkError, DustError, FunctionType, Instruction, LexError,
|
||||
Lexer, Local, NativeFunction, Operation, Optimizer, Scope, Span, Token, TokenKind, TokenOwned,
|
||||
Type, TypeConflict,
|
||||
};
|
||||
|
||||
/// Compiles the input and returns a chunk.
|
||||
@ -429,12 +435,12 @@ impl<'src> Compiler<'src> {
|
||||
position: Span,
|
||||
) -> Result<(), CompileError> {
|
||||
let constant_index = self.chunk.push_or_get_constant(constant);
|
||||
let register = self.next_register();
|
||||
let instruction = Instruction::builder(Operation::LoadConstant)
|
||||
.a(register)
|
||||
.b(constant_index)
|
||||
.c_to_boolean(false)
|
||||
.build();
|
||||
let destination = self.next_register();
|
||||
let instruction = Instruction::from(LoadConstant {
|
||||
destination,
|
||||
constant_index,
|
||||
jump_next: false,
|
||||
});
|
||||
|
||||
self.emit_instruction(instruction, position);
|
||||
|
||||
@ -448,12 +454,12 @@ impl<'src> Compiler<'src> {
|
||||
self.advance()?;
|
||||
|
||||
let boolean = text.parse::<bool>().unwrap();
|
||||
let register = self.next_register();
|
||||
let instruction = Instruction::builder(Operation::LoadBoolean)
|
||||
.a(register)
|
||||
.b_to_boolean(boolean)
|
||||
.c_to_boolean(false)
|
||||
.build();
|
||||
let destination = self.next_register();
|
||||
let instruction = Instruction::from(LoadBoolean {
|
||||
destination,
|
||||
value: boolean,
|
||||
jump_next: false,
|
||||
});
|
||||
|
||||
self.emit_instruction(instruction, position);
|
||||
|
||||
@ -611,37 +617,25 @@ impl<'src> Compiler<'src> {
|
||||
self.parse_expression()?;
|
||||
|
||||
let (previous_instruction, previous_position) = self.pop_last_instruction()?;
|
||||
let (push_back, is_constant, argument) = {
|
||||
match previous_instruction.operation() {
|
||||
Operation::GetLocal => (false, false, previous_instruction.a()),
|
||||
Operation::LoadConstant => (false, true, previous_instruction.a()),
|
||||
Operation::LoadBoolean => (true, false, previous_instruction.a()),
|
||||
Operation::Close => {
|
||||
return Err(CompileError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: self.previous_position,
|
||||
});
|
||||
}
|
||||
_ => (true, false, previous_instruction.a()),
|
||||
}
|
||||
let argument = if let Some(argument) = previous_instruction.destination_as_argument() {
|
||||
argument
|
||||
} else {
|
||||
return Err(CompileError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: previous_position,
|
||||
});
|
||||
};
|
||||
|
||||
if push_back {
|
||||
self.emit_instruction(previous_instruction, previous_position);
|
||||
}
|
||||
|
||||
let register = self.next_register();
|
||||
let destination = self.next_register();
|
||||
let instruction = match operator.kind() {
|
||||
TokenKind::Bang => Instruction::builder(Operation::Not)
|
||||
.a(register)
|
||||
.b(argument)
|
||||
.b_is_constant(is_constant)
|
||||
.build(),
|
||||
TokenKind::Minus => Instruction::builder(Operation::Negate)
|
||||
.a(register)
|
||||
.b(argument)
|
||||
.b_is_constant(is_constant)
|
||||
.build(),
|
||||
TokenKind::Bang => Instruction::from(Not {
|
||||
destination,
|
||||
argument,
|
||||
}),
|
||||
TokenKind::Minus => Instruction::from(Negate {
|
||||
destination,
|
||||
argument,
|
||||
}),
|
||||
_ => {
|
||||
return Err(CompileError::ExpectedTokenMultiple {
|
||||
expected: &[TokenKind::Bang, TokenKind::Minus],
|
||||
@ -660,48 +654,6 @@ impl<'src> Compiler<'src> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_binary_argument(
|
||||
&mut self,
|
||||
instruction: &Instruction,
|
||||
) -> Result<(bool, bool, bool, bool, u16), CompileError> {
|
||||
let mut push_back = false;
|
||||
let mut is_constant = false;
|
||||
let mut is_local = false;
|
||||
let mut is_mutable_local = false;
|
||||
let argument = match instruction.operation() {
|
||||
Operation::GetLocal => {
|
||||
let local_index = instruction.b();
|
||||
let local = self.get_local(local_index)?;
|
||||
is_local = true;
|
||||
is_mutable_local = local.is_mutable;
|
||||
|
||||
local_index
|
||||
}
|
||||
Operation::LoadConstant => {
|
||||
is_constant = true;
|
||||
|
||||
instruction.b()
|
||||
}
|
||||
Operation::Close => {
|
||||
return Err(CompileError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: self.previous_position,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
push_back = true;
|
||||
|
||||
if instruction.yields_value() {
|
||||
instruction.a()
|
||||
} else {
|
||||
self.next_register()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok((push_back, is_constant, is_local, is_mutable_local, argument))
|
||||
}
|
||||
|
||||
fn parse_math_binary(&mut self) -> Result<(), CompileError> {
|
||||
let (left_instruction, left_position) =
|
||||
self.chunk.instructions_mut().pop().ok_or_else(|| {
|
||||
@ -710,13 +662,17 @@ impl<'src> Compiler<'src> {
|
||||
position: self.previous_position,
|
||||
}
|
||||
})?;
|
||||
let (push_back_left, left_is_constant, left_is_local, left_is_mutable_local, left) =
|
||||
self.handle_binary_argument(&left_instruction)?;
|
||||
|
||||
if push_back_left {
|
||||
self.emit_instruction(left_instruction, left_position);
|
||||
}
|
||||
|
||||
let left = left_instruction.destination_as_argument().ok_or_else(|| {
|
||||
CompileError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: left_position,
|
||||
}
|
||||
})?;
|
||||
let left_is_mutable_local = if let Argument::Local(local_index) = left {
|
||||
self.get_local(local_index)?.is_mutable
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let operator = self.current_token;
|
||||
let operator_position = self.current_position;
|
||||
let rule = ParseRule::from(&operator);
|
||||
@ -740,29 +696,69 @@ impl<'src> Compiler<'src> {
|
||||
self.parse_sub_expression(&rule.precedence)?;
|
||||
|
||||
let (right_instruction, right_position) = self.pop_last_instruction()?;
|
||||
let (push_back_right, right_is_constant, right_is_local, _, right) =
|
||||
self.handle_binary_argument(&right_instruction)?;
|
||||
let right = right_instruction.destination_as_argument().ok_or_else(|| {
|
||||
CompileError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: right_position,
|
||||
}
|
||||
})?;
|
||||
|
||||
if push_back_right {
|
||||
self.emit_instruction(right_instruction, right_position);
|
||||
}
|
||||
|
||||
let register = if is_assignment {
|
||||
left
|
||||
let destination = if is_assignment {
|
||||
left.index()
|
||||
} else {
|
||||
self.next_register()
|
||||
};
|
||||
let operation = match operator {
|
||||
Token::Plus => Operation::Add,
|
||||
Token::PlusEqual => Operation::Add,
|
||||
Token::Minus => Operation::Subtract,
|
||||
Token::MinusEqual => Operation::Subtract,
|
||||
Token::Star => Operation::Multiply,
|
||||
Token::StarEqual => Operation::Multiply,
|
||||
Token::Slash => Operation::Divide,
|
||||
Token::SlashEqual => Operation::Divide,
|
||||
Token::Percent => Operation::Modulo,
|
||||
Token::PercentEqual => Operation::Modulo,
|
||||
let instruction = match operator {
|
||||
Token::Plus => Instruction::from(Add {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
Token::PlusEqual => Instruction::from(Add {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
Token::Minus => Instruction::from(Subtract {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
Token::MinusEqual => Instruction::from(Subtract {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
Token::Star => Instruction::from(Multiply {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
Token::StarEqual => Instruction::from(Multiply {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
Token::Slash => Instruction::from(Divide {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
Token::SlashEqual => Instruction::from(Divide {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
Token::Percent => Instruction::from(Modulo {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
Token::PercentEqual => Instruction::from(Modulo {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
_ => {
|
||||
return Err(CompileError::ExpectedTokenMultiple {
|
||||
expected: &[
|
||||
@ -783,14 +779,6 @@ impl<'src> Compiler<'src> {
|
||||
}
|
||||
};
|
||||
|
||||
let instruction = Instruction::builder(operation)
|
||||
.a(register)
|
||||
.b(left)
|
||||
.c(right)
|
||||
.b_is_constant(left_is_constant)
|
||||
.c_is_constant(right_is_constant)
|
||||
.build();
|
||||
|
||||
self.emit_instruction(instruction, operator_position);
|
||||
|
||||
if is_assignment {
|
||||
@ -818,8 +806,12 @@ impl<'src> Compiler<'src> {
|
||||
position: self.previous_position,
|
||||
}
|
||||
})?;
|
||||
let (push_back_left, left_is_constant, left_is_local, _, left) =
|
||||
self.handle_binary_argument(&left_instruction)?;
|
||||
let left = left_instruction.destination_as_argument().ok_or_else(|| {
|
||||
CompileError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: left_position,
|
||||
}
|
||||
})?;
|
||||
let operator = self.current_token;
|
||||
let operator_position = self.current_position;
|
||||
let rule = ParseRule::from(&operator);
|
||||
@ -834,15 +826,43 @@ impl<'src> Compiler<'src> {
|
||||
position: self.previous_position,
|
||||
}
|
||||
})?;
|
||||
let (push_back_right, right_is_constant, right_is_local, _, right) =
|
||||
self.handle_binary_argument(&right_instruction)?;
|
||||
let (operation, instruction_boolean) = match operator {
|
||||
Token::DoubleEqual => (Operation::Equal, true),
|
||||
Token::BangEqual => (Operation::Equal, false),
|
||||
Token::Less => (Operation::Less, true),
|
||||
Token::LessEqual => (Operation::LessEqual, true),
|
||||
Token::Greater => (Operation::LessEqual, false),
|
||||
Token::GreaterEqual => (Operation::Less, false),
|
||||
let right = right_instruction.destination_as_argument().ok_or_else(|| {
|
||||
CompileError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: right_position,
|
||||
}
|
||||
})?;
|
||||
let instruction = match operator {
|
||||
Token::DoubleEqual => Instruction::from(Equal {
|
||||
value: true,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
Token::BangEqual => Instruction::from(Equal {
|
||||
value: false,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
Token::Less => Instruction::from(Less {
|
||||
value: true,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
Token::LessEqual => Instruction::from(LessEqual {
|
||||
value: true,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
Token::Greater => Instruction::from(LessEqual {
|
||||
value: false,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
Token::GreaterEqual => Instruction::from(Less {
|
||||
value: false,
|
||||
left,
|
||||
right,
|
||||
}),
|
||||
_ => {
|
||||
return Err(CompileError::ExpectedTokenMultiple {
|
||||
expected: &[
|
||||
@ -858,37 +878,22 @@ impl<'src> Compiler<'src> {
|
||||
})
|
||||
}
|
||||
};
|
||||
let instruction = Instruction::builder(operation)
|
||||
.a_to_boolean(instruction_boolean)
|
||||
.b(left)
|
||||
.c(right)
|
||||
.b_is_constant(left_is_constant)
|
||||
.c_is_constant(right_is_constant)
|
||||
.build();
|
||||
|
||||
if push_back_left {
|
||||
self.emit_instruction(left_instruction, left_position);
|
||||
}
|
||||
|
||||
if push_back_right {
|
||||
self.emit_instruction(right_instruction, right_position);
|
||||
}
|
||||
|
||||
let register = self.next_register();
|
||||
let jump = Instruction::builder(Operation::Jump)
|
||||
.a(1)
|
||||
.b_to_boolean(true)
|
||||
.build();
|
||||
let load_true = Instruction::builder(Operation::LoadBoolean)
|
||||
.a(register)
|
||||
.b_to_boolean(true)
|
||||
.c_to_boolean(true)
|
||||
.build();
|
||||
let load_false = Instruction::builder(Operation::LoadBoolean)
|
||||
.a(register)
|
||||
.b_to_boolean(false)
|
||||
.c_to_boolean(false)
|
||||
.build();
|
||||
let jump = Instruction::from(Jump {
|
||||
offset: 1,
|
||||
is_positive: true,
|
||||
});
|
||||
let load_true = Instruction::from(LoadBoolean {
|
||||
destination: register,
|
||||
value: true,
|
||||
jump_next: true,
|
||||
});
|
||||
let load_false = Instruction::from(LoadBoolean {
|
||||
destination: register,
|
||||
value: false,
|
||||
jump_next: false,
|
||||
});
|
||||
|
||||
self.emit_instruction(instruction, operator_position);
|
||||
self.emit_instruction(jump, operator_position);
|
||||
@ -910,11 +915,15 @@ impl<'src> Compiler<'src> {
|
||||
});
|
||||
}
|
||||
|
||||
let (_, is_constant, is_local, _, _) = self.handle_binary_argument(&left_instruction)?;
|
||||
let argument = left_instruction.destination_as_argument().ok_or_else(|| {
|
||||
CompileError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: left_position,
|
||||
}
|
||||
})?;
|
||||
let operator = self.current_token;
|
||||
let operator_position = self.current_position;
|
||||
let rule = ParseRule::from(&operator);
|
||||
let test_register = left_instruction.a();
|
||||
let test_boolean = match operator {
|
||||
Token::DoubleAmpersand => true,
|
||||
Token::DoublePipe => false,
|
||||
@ -926,14 +935,14 @@ impl<'src> Compiler<'src> {
|
||||
})
|
||||
}
|
||||
};
|
||||
let test = Instruction::builder(Operation::Test)
|
||||
.b(test_register)
|
||||
.c_to_boolean(test_boolean)
|
||||
.build();
|
||||
let jump = Instruction::builder(Operation::Jump)
|
||||
.a(1)
|
||||
.b_to_boolean(true)
|
||||
.build();
|
||||
let test = Instruction::from(Test {
|
||||
argument,
|
||||
value: test_boolean,
|
||||
});
|
||||
let jump = Instruction::from(Jump {
|
||||
offset: 1,
|
||||
is_positive: true,
|
||||
});
|
||||
|
||||
self.advance()?;
|
||||
self.emit_instruction(left_instruction, left_position);
|
||||
@ -964,10 +973,8 @@ impl<'src> Compiler<'src> {
|
||||
} else if let Some(native_function) = NativeFunction::from_str(identifier) {
|
||||
return self.parse_native_call(native_function);
|
||||
} else if Some(identifier) == self.chunk.name().map(|string| string.as_str()) {
|
||||
let register = self.next_register();
|
||||
let load_self = Instruction::builder(Operation::LoadSelf)
|
||||
.a(register)
|
||||
.build();
|
||||
let destination = self.next_register();
|
||||
let load_self = Instruction::from(LoadSelf { destination });
|
||||
|
||||
self.emit_instruction(load_self, start_position);
|
||||
|
||||
@ -1004,10 +1011,10 @@ impl<'src> Compiler<'src> {
|
||||
self.parse_expression()?;
|
||||
|
||||
let register = self.next_register();
|
||||
let set_local = Instruction::builder(Operation::SetLocal)
|
||||
.a(register)
|
||||
.b(local_index)
|
||||
.build();
|
||||
let set_local = Instruction::from(SetLocal {
|
||||
register,
|
||||
local_index,
|
||||
});
|
||||
|
||||
self.emit_instruction(set_local, start_position);
|
||||
|
||||
@ -1023,17 +1030,16 @@ impl<'src> Compiler<'src> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let register = self.next_register();
|
||||
let get_local = Instruction::builder(Operation::GetLocal)
|
||||
.a(register)
|
||||
.b(local_index)
|
||||
.build();
|
||||
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, self.previous_position);
|
||||
|
||||
let local = self.get_local(local_index)?;
|
||||
|
||||
self.previous_expression_type = local.r#type.clone();
|
||||
self.previous_expression_type = r#type;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -1098,30 +1104,31 @@ impl<'src> Compiler<'src> {
|
||||
|
||||
item_type = self.previous_expression_type.clone();
|
||||
let actual_register = self.next_register() - 1;
|
||||
let close = Instruction::builder(Operation::Close)
|
||||
.a(expected_register)
|
||||
.b(actual_register)
|
||||
.build();
|
||||
|
||||
if expected_register < actual_register {
|
||||
let close = Instruction::from(Close {
|
||||
from: expected_register,
|
||||
to: actual_register,
|
||||
});
|
||||
|
||||
self.emit_instruction(close, self.current_position);
|
||||
}
|
||||
|
||||
self.allow(Token::Comma)?;
|
||||
}
|
||||
|
||||
let to_register = self.next_register();
|
||||
let destination = self.next_register();
|
||||
let end = self.current_position.1;
|
||||
let load_list = Instruction::builder(Operation::LoadList)
|
||||
.a(to_register)
|
||||
.b(start_register)
|
||||
.build();
|
||||
let load_list = Instruction::from(LoadList {
|
||||
destination,
|
||||
start_register,
|
||||
});
|
||||
|
||||
self.emit_instruction(load_list, Span(start, end));
|
||||
|
||||
self.previous_expression_type = Type::List {
|
||||
item_type: Box::new(item_type),
|
||||
length: (to_register - start_register) as usize,
|
||||
length: (destination - start_register) as usize,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
@ -1145,10 +1152,10 @@ impl<'src> Compiler<'src> {
|
||||
self.chunk.instructions_mut().pop();
|
||||
} else if let Some((instruction, _)) = self.chunk.instructions().last() {
|
||||
let test_register = instruction.a();
|
||||
let test = Instruction::builder(Operation::Test)
|
||||
.b(test_register)
|
||||
.c_to_boolean(true)
|
||||
.build();
|
||||
let test = Instruction::from(Test {
|
||||
argument: Argument::Register(test_register),
|
||||
value: true,
|
||||
});
|
||||
|
||||
self.emit_instruction(test, self.current_position)
|
||||
}
|
||||
@ -1212,10 +1219,10 @@ impl<'src> Compiler<'src> {
|
||||
skippable.set_c_to_boolean(true);
|
||||
} else {
|
||||
if_block_distance += 1;
|
||||
let jump = Instruction::builder(Operation::Jump)
|
||||
.b(else_block_distance)
|
||||
.c_to_boolean(true)
|
||||
.build();
|
||||
let jump = Instruction::from(Jump {
|
||||
offset: else_block_distance,
|
||||
is_positive: true,
|
||||
});
|
||||
|
||||
self.chunk
|
||||
.instructions_mut()
|
||||
@ -1224,10 +1231,10 @@ impl<'src> Compiler<'src> {
|
||||
}
|
||||
2.. => {
|
||||
if_block_distance += 1;
|
||||
let jump = Instruction::builder(Operation::Jump)
|
||||
.b(else_block_distance)
|
||||
.c_to_boolean(true)
|
||||
.build();
|
||||
let jump = Instruction::from(Jump {
|
||||
offset: else_block_distance,
|
||||
is_positive: true,
|
||||
});
|
||||
|
||||
self.chunk
|
||||
.instructions_mut()
|
||||
@ -1235,10 +1242,10 @@ impl<'src> Compiler<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
let jump = Instruction::builder(Operation::Jump)
|
||||
.b(if_block_distance)
|
||||
.c_to_boolean(true)
|
||||
.build();
|
||||
let jump = Instruction::from(Jump {
|
||||
offset: if_block_distance,
|
||||
is_positive: true,
|
||||
});
|
||||
|
||||
self.chunk
|
||||
.instructions_mut()
|
||||
@ -1253,10 +1260,10 @@ impl<'src> Compiler<'src> {
|
||||
}
|
||||
|
||||
let else_last_register = self.next_register().saturating_sub(1);
|
||||
let r#move = Instruction::builder(Operation::Move)
|
||||
.a(if_last_register)
|
||||
.b(else_last_register)
|
||||
.build();
|
||||
let r#move = Instruction::from(Move {
|
||||
from: else_last_register,
|
||||
to: if_last_register,
|
||||
});
|
||||
|
||||
if if_last_register < else_last_register {
|
||||
self.emit_instruction(r#move, self.current_position);
|
||||
@ -1292,20 +1299,20 @@ impl<'src> Compiler<'src> {
|
||||
|
||||
let block_end = self.chunk.len() as u16;
|
||||
let jump_distance = block_end - block_start as u16 + 1;
|
||||
let jump = Instruction::builder(Operation::Jump)
|
||||
.b(jump_distance)
|
||||
.c_to_boolean(true)
|
||||
.build();
|
||||
let jump = Instruction::from(Jump {
|
||||
offset: jump_distance,
|
||||
is_positive: true,
|
||||
});
|
||||
|
||||
self.chunk
|
||||
.instructions_mut()
|
||||
.insert(block_start, (jump, self.current_position));
|
||||
|
||||
let jump_back_distance = block_end - expression_start + 1;
|
||||
let jump_back = Instruction::builder(Operation::Jump)
|
||||
.b(jump_back_distance)
|
||||
.c_to_boolean(false)
|
||||
.build();
|
||||
let jump_back = Instruction::from(Jump {
|
||||
offset: jump_back_distance,
|
||||
is_positive: false,
|
||||
});
|
||||
|
||||
self.emit_instruction(jump_back, self.current_position);
|
||||
|
||||
@ -1328,10 +1335,10 @@ impl<'src> Compiler<'src> {
|
||||
let actual_register = self.next_register() - 1;
|
||||
|
||||
if expected_register < actual_register {
|
||||
let close = Instruction::builder(Operation::Close)
|
||||
.b(expected_register)
|
||||
.c(actual_register)
|
||||
.build();
|
||||
let close = Instruction::from(Close {
|
||||
from: expected_register,
|
||||
to: actual_register,
|
||||
});
|
||||
|
||||
self.emit_instruction(close, self.current_position);
|
||||
}
|
||||
@ -1341,13 +1348,13 @@ impl<'src> Compiler<'src> {
|
||||
|
||||
self.previous_expression_type = *function.r#type().return_type;
|
||||
let end = self.previous_position.1;
|
||||
let to_register = self.next_register();
|
||||
let argument_count = to_register - start_register;
|
||||
let call_native = Instruction::builder(Operation::CallNative)
|
||||
.a(start_register)
|
||||
.b(function as u16)
|
||||
.c(argument_count)
|
||||
.build();
|
||||
let destination = self.next_register();
|
||||
let argument_count = destination - start_register;
|
||||
let call_native = Instruction::from(CallNative {
|
||||
destination,
|
||||
function,
|
||||
argument_count,
|
||||
});
|
||||
|
||||
self.emit_instruction(call_native, Span(start, end));
|
||||
|
||||
@ -1390,21 +1397,21 @@ impl<'src> Compiler<'src> {
|
||||
|
||||
self.advance()?;
|
||||
|
||||
let has_return_value = if matches!(self.current_token, Token::Semicolon | Token::RightBrace)
|
||||
{
|
||||
self.update_return_type(Type::None)?;
|
||||
let should_return_value =
|
||||
if matches!(self.current_token, Token::Semicolon | Token::RightBrace) {
|
||||
self.update_return_type(Type::None)?;
|
||||
|
||||
false
|
||||
} else {
|
||||
self.parse_expression()?;
|
||||
self.update_return_type(self.previous_expression_type.clone())?;
|
||||
false
|
||||
} else {
|
||||
self.parse_expression()?;
|
||||
self.update_return_type(self.previous_expression_type.clone())?;
|
||||
|
||||
true
|
||||
};
|
||||
true
|
||||
};
|
||||
let end = self.current_position.1;
|
||||
let r#return = Instruction::builder(Operation::Return)
|
||||
.b_to_boolean(has_return_value)
|
||||
.build();
|
||||
let r#return = Instruction::from(Return {
|
||||
should_return_value,
|
||||
});
|
||||
|
||||
self.emit_instruction(r#return, Span(start, end));
|
||||
|
||||
@ -1415,16 +1422,16 @@ impl<'src> Compiler<'src> {
|
||||
|
||||
fn parse_implicit_return(&mut self) -> Result<(), CompileError> {
|
||||
if self.allow(Token::Semicolon)? {
|
||||
let r#return = Instruction::builder(Operation::Return)
|
||||
.b_to_boolean(false)
|
||||
.build();
|
||||
let r#return = Instruction::from(Return {
|
||||
should_return_value: false,
|
||||
});
|
||||
|
||||
self.emit_instruction(r#return, self.current_position);
|
||||
} else {
|
||||
let should_return_value = self.previous_expression_type != Type::None;
|
||||
let r#return = Instruction::builder(Operation::Return)
|
||||
.b_to_boolean(should_return_value)
|
||||
.build();
|
||||
let r#return = Instruction::from(Return {
|
||||
should_return_value,
|
||||
});
|
||||
|
||||
self.emit_instruction(r#return, self.current_position);
|
||||
}
|
||||
@ -1469,11 +1476,11 @@ impl<'src> Compiler<'src> {
|
||||
};
|
||||
let (local_index, _) =
|
||||
self.declare_local(identifier, r#type, is_mutable, self.current_scope);
|
||||
let define_local = Instruction::builder(Operation::DefineLocal)
|
||||
.a(register)
|
||||
.b(local_index)
|
||||
.c_to_boolean(is_mutable)
|
||||
.build();
|
||||
let define_local = Instruction::from(DefineLocal {
|
||||
local_index,
|
||||
register,
|
||||
is_mutable,
|
||||
});
|
||||
|
||||
self.emit_instruction(define_local, position);
|
||||
|
||||
@ -1574,7 +1581,7 @@ impl<'src> Compiler<'src> {
|
||||
let function = ConcreteValue::Function(function_compiler.finish());
|
||||
let constant_index = self.chunk.push_or_get_constant(function);
|
||||
let function_end = self.current_position.1;
|
||||
let register = self.next_register();
|
||||
let destination = self.next_register();
|
||||
|
||||
self.lexer.skip_to(function_end);
|
||||
|
||||
@ -1585,27 +1592,27 @@ impl<'src> Compiler<'src> {
|
||||
false,
|
||||
self.current_scope,
|
||||
);
|
||||
let load_constant = Instruction::builder(Operation::LoadConstant)
|
||||
.a(register)
|
||||
.b(constant_index)
|
||||
.c_to_boolean(false)
|
||||
.build();
|
||||
let define_local = Instruction::builder(Operation::DefineLocal)
|
||||
.a(register)
|
||||
.b(local_index)
|
||||
.c_to_boolean(false)
|
||||
.build();
|
||||
let load_constant = Instruction::from(LoadConstant {
|
||||
destination,
|
||||
constant_index,
|
||||
jump_next: false,
|
||||
});
|
||||
let define_local = Instruction::from(DefineLocal {
|
||||
local_index,
|
||||
register: destination,
|
||||
is_mutable: false,
|
||||
});
|
||||
|
||||
self.emit_instruction(load_constant, Span(function_start, function_end));
|
||||
self.emit_instruction(define_local, position);
|
||||
|
||||
self.previous_expression_type = Type::None;
|
||||
} else {
|
||||
let load_constant = Instruction::builder(Operation::LoadConstant)
|
||||
.a(register)
|
||||
.b(constant_index)
|
||||
.c_to_boolean(false)
|
||||
.build();
|
||||
let load_constant = Instruction::from(LoadConstant {
|
||||
destination,
|
||||
constant_index,
|
||||
jump_next: false,
|
||||
});
|
||||
|
||||
self.emit_instruction(load_constant, Span(function_start, function_end));
|
||||
|
||||
@ -1632,8 +1639,13 @@ impl<'src> Compiler<'src> {
|
||||
});
|
||||
}
|
||||
|
||||
let function_register = last_instruction.a();
|
||||
let register_type = self.get_register_type(function_register)?;
|
||||
let function = last_instruction.destination_as_argument().ok_or_else(|| {
|
||||
CompileError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: self.previous_position,
|
||||
}
|
||||
})?;
|
||||
let register_type = self.get_register_type(function.index())?;
|
||||
let function_return_type = match register_type {
|
||||
Type::Function(function_type) => *function_type.return_type,
|
||||
Type::SelfChunk => (*self.chunk.r#type().return_type).clone(),
|
||||
@ -1655,12 +1667,13 @@ impl<'src> Compiler<'src> {
|
||||
self.parse_expression()?;
|
||||
|
||||
let actual_register = self.next_register() - 1;
|
||||
let close = Instruction::builder(Operation::Close)
|
||||
.b(expected_register)
|
||||
.c(actual_register)
|
||||
.build();
|
||||
|
||||
if expected_register < actual_register {
|
||||
let close = Instruction::from(Close {
|
||||
from: expected_register,
|
||||
to: actual_register,
|
||||
});
|
||||
|
||||
self.emit_instruction(close, self.current_position);
|
||||
}
|
||||
|
||||
@ -1668,13 +1681,13 @@ impl<'src> Compiler<'src> {
|
||||
}
|
||||
|
||||
let end = self.current_position.1;
|
||||
let to_register = self.next_register();
|
||||
let argument_count = to_register - function_register - 1;
|
||||
let call = Instruction::builder(Operation::Call)
|
||||
.a(to_register)
|
||||
.b(function_register)
|
||||
.c(argument_count)
|
||||
.build();
|
||||
let destination = self.next_register();
|
||||
let argument_count = self.next_register() - function.index() - 1;
|
||||
let call = Instruction::from(Call {
|
||||
destination,
|
||||
function,
|
||||
argument_count,
|
||||
});
|
||||
|
||||
self.emit_instruction(call, Span(start, end));
|
||||
|
||||
|
@ -1,964 +0,0 @@
|
||||
//! An operation and its arguments for the Dust virtual machine.
|
||||
//!
|
||||
//! Each instruction is a 64-bit unsigned integer that is divided into five fields:
|
||||
//! - Bits 0-8: The operation code.
|
||||
//! - Bit 9: Boolean flag indicating whether the B argument is a constant.
|
||||
//! - Bit 10: Boolean flag indicating whether the C argument is a constant.
|
||||
//! - Bit 11: Boolean flag indicating whether the A argument is a local.
|
||||
//! - Bit 12: Boolean flag indicating whether the B argument is a local.
|
||||
//! - Bit 13: Boolean flag indicating whether the C argument is a local.
|
||||
//! - Bits 17-32: The A argument,
|
||||
//! - Bits 33-48: The B argument.
|
||||
//! - Bits 49-63: The C argument.
|
||||
//!
|
||||
//! 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.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{Chunk, NativeFunction, Operation};
|
||||
|
||||
pub struct InstructionBuilder {
|
||||
operation: Operation,
|
||||
a: u16,
|
||||
b: u16,
|
||||
c: u16,
|
||||
b_is_constant: bool,
|
||||
c_is_constant: bool,
|
||||
a_is_local: bool,
|
||||
b_is_local: bool,
|
||||
c_is_local: bool,
|
||||
}
|
||||
|
||||
impl InstructionBuilder {
|
||||
pub fn build(&self) -> Instruction {
|
||||
Instruction(
|
||||
(self.operation as u64)
|
||||
| ((self.b_is_constant as u64) << 9)
|
||||
| ((self.c_is_constant as u64) << 10)
|
||||
| ((self.a_is_local as u64) << 11)
|
||||
| ((self.b_is_local as u64) << 12)
|
||||
| ((self.c_is_local as u64) << 13)
|
||||
| ((self.a as u64) << 16)
|
||||
| ((self.b as u64) << 32)
|
||||
| ((self.c as u64) << 48),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn a(&mut self, a: u16) -> &mut Self {
|
||||
self.a = a;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn a_to_boolean(&mut self, a: bool) -> &mut Self {
|
||||
self.a = a as u16;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn b(&mut self, b: u16) -> &mut Self {
|
||||
self.b = b;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn b_to_boolean(&mut self, b: bool) -> &mut Self {
|
||||
self.b = b as u16;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn c(&mut self, c: u16) -> &mut Self {
|
||||
self.c = c;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn c_to_boolean(&mut self, c: bool) -> &mut Self {
|
||||
self.c = c as u16;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn b_is_constant(&mut self, b_is_constant: bool) -> &mut Self {
|
||||
self.b_is_constant = b_is_constant;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn c_is_constant(&mut self, c_is_constant: bool) -> &mut Self {
|
||||
self.c_is_constant = c_is_constant;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn a_is_local(&mut self, a_is_local: bool) -> &mut Self {
|
||||
self.a_is_local = a_is_local;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn b_is_local(&mut self, b_is_local: bool) -> &mut Self {
|
||||
self.b_is_local = b_is_local;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn c_is_local(&mut self, c_is_local: bool) -> &mut Self {
|
||||
self.c_is_local = c_is_local;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Instruction> for InstructionBuilder {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
InstructionBuilder {
|
||||
operation: instruction.operation(),
|
||||
a: instruction.a(),
|
||||
b: instruction.b(),
|
||||
c: instruction.c(),
|
||||
b_is_constant: instruction.b_is_constant(),
|
||||
c_is_constant: instruction.c_is_constant(),
|
||||
a_is_local: instruction.a_is_local(),
|
||||
b_is_local: instruction.b_is_local(),
|
||||
c_is_local: instruction.c_is_local(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Instruction> for InstructionBuilder {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
InstructionBuilder {
|
||||
operation: instruction.operation(),
|
||||
a: instruction.a(),
|
||||
b: instruction.b(),
|
||||
c: instruction.c(),
|
||||
b_is_constant: instruction.b_is_constant(),
|
||||
c_is_constant: instruction.c_is_constant(),
|
||||
a_is_local: instruction.a_is_local(),
|
||||
b_is_local: instruction.b_is_local(),
|
||||
c_is_local: instruction.c_is_local(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&mut Instruction> for InstructionBuilder {
|
||||
fn from(instruction: &mut Instruction) -> Self {
|
||||
InstructionBuilder {
|
||||
operation: instruction.operation(),
|
||||
a: instruction.a(),
|
||||
b: instruction.b(),
|
||||
c: instruction.c(),
|
||||
b_is_constant: instruction.b_is_constant(),
|
||||
c_is_constant: instruction.c_is_constant(),
|
||||
a_is_local: instruction.a_is_local(),
|
||||
b_is_local: instruction.b_is_local(),
|
||||
c_is_local: instruction.c_is_local(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An operation and its arguments for the Dust virtual machine.
|
||||
///
|
||||
/// See the [module-level documentation](index.html) for more information.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct Instruction(u64);
|
||||
|
||||
impl Instruction {
|
||||
pub fn new(operation: Operation) -> Instruction {
|
||||
Instruction(operation as u64)
|
||||
}
|
||||
|
||||
pub fn builder(operation: Operation) -> InstructionBuilder {
|
||||
InstructionBuilder {
|
||||
operation,
|
||||
a: 0,
|
||||
b: 0,
|
||||
c: 0,
|
||||
b_is_constant: false,
|
||||
c_is_constant: false,
|
||||
a_is_local: false,
|
||||
b_is_local: false,
|
||||
c_is_local: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn operation(&self) -> Operation {
|
||||
Operation::from((self.0 & 0b11111111) as u8)
|
||||
}
|
||||
|
||||
pub fn set_b_is_constant(&mut self) -> &mut Self {
|
||||
self.0 = (self.0 & !(1 << 9)) | ((true as u64) << 9);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_c_is_constant(&mut self) -> &mut Self {
|
||||
self.0 = (self.0 & !(1 << 10)) | ((true as u64) << 10);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_a_is_local(&mut self) -> &mut Self {
|
||||
self.0 = (self.0 & !(1 << 11)) | ((true as u64) << 11);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_b_is_local(&mut self) -> &mut Self {
|
||||
self.0 = (self.0 & !(1 << 12)) | ((true as u64) << 12);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_c_is_local(&mut self) -> &mut Self {
|
||||
self.0 = (self.0 & !(1 << 13)) | ((true as u64) << 13);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn a(&self) -> u16 {
|
||||
((self.0 >> 16) & 0b1111111111111111) as u16
|
||||
}
|
||||
|
||||
pub fn a_as_boolean(&self) -> bool {
|
||||
self.a() != 0
|
||||
}
|
||||
|
||||
pub fn set_a(&mut self, a: u16) -> &mut Self {
|
||||
self.0 = (self.0 & !(0b1111111111111111 << 16)) | ((a as u64) << 16);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_a_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
||||
self.0 = (self.0 & !(0b1111111111111111 << 16)) | ((boolean as u64) << 16);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn b(&self) -> u16 {
|
||||
((self.0 >> 32) & 0b1111111111111111) as u16
|
||||
}
|
||||
|
||||
pub fn b_as_boolean(&self) -> bool {
|
||||
self.b() != 0
|
||||
}
|
||||
|
||||
pub fn set_b(&mut self, b: u16) -> &mut Self {
|
||||
self.0 = (self.0 & !(0b1111111111111111 << 32)) | ((b as u64) << 32);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_b_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
||||
self.0 = (self.0 & !(0b1111111111111111 << 32)) | ((boolean as u64) << 32);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn c(&self) -> u16 {
|
||||
((self.0 >> 48) & 0b1111111111111111) as u16
|
||||
}
|
||||
|
||||
pub fn c_as_boolean(&self) -> bool {
|
||||
self.c() != 0
|
||||
}
|
||||
|
||||
pub fn set_c(&mut self, c: u16) -> &mut Self {
|
||||
self.0 = (self.0 & !(0b1111111111111111 << 48)) | ((c as u64) << 48);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_c_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
||||
self.0 = (self.0 & !(0b1111111111111111 << 48)) | ((boolean as u64) << 48);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn b_is_constant(&self) -> bool {
|
||||
(self.0 >> 9) & 1 == 1
|
||||
}
|
||||
|
||||
pub fn c_is_constant(&self) -> bool {
|
||||
(self.0 >> 10) & 1 == 1
|
||||
}
|
||||
|
||||
pub fn a_is_local(&self) -> bool {
|
||||
(self.0 >> 11) & 1 == 1
|
||||
}
|
||||
|
||||
pub fn b_is_local(&self) -> bool {
|
||||
(self.0 >> 12) & 1 == 1
|
||||
}
|
||||
|
||||
pub fn c_is_local(&self) -> bool {
|
||||
(self.0 >> 13) & 1 == 1
|
||||
}
|
||||
|
||||
pub fn r#move(to_register: u16, from_register: u16) -> Instruction {
|
||||
*Instruction::new(Operation::Move)
|
||||
.set_b(to_register)
|
||||
.set_c(from_register)
|
||||
}
|
||||
|
||||
pub fn close(from_register: u16, to_register: u16) -> Instruction {
|
||||
*Instruction::new(Operation::Close)
|
||||
.set_b(from_register)
|
||||
.set_c(to_register)
|
||||
}
|
||||
|
||||
pub fn load_boolean(to_register: u16, value: bool, skip: bool) -> Instruction {
|
||||
*Instruction::new(Operation::LoadBoolean)
|
||||
.set_a(to_register)
|
||||
.set_b_to_boolean(value)
|
||||
.set_c_to_boolean(skip)
|
||||
}
|
||||
|
||||
pub fn load_constant(to_register: u16, constant_index: u16, skip: bool) -> Instruction {
|
||||
*Instruction::new(Operation::LoadConstant)
|
||||
.set_a(to_register)
|
||||
.set_b(constant_index)
|
||||
.set_c_to_boolean(skip)
|
||||
}
|
||||
|
||||
pub fn load_list(to_register: u16, start_register: u16) -> Instruction {
|
||||
*Instruction::new(Operation::LoadList)
|
||||
.set_a(to_register)
|
||||
.set_b(start_register)
|
||||
}
|
||||
|
||||
pub fn load_self(to_register: u16) -> Instruction {
|
||||
*Instruction::new(Operation::LoadSelf).set_a(to_register)
|
||||
}
|
||||
|
||||
pub fn define_local(to_register: u16, local_index: u16, is_mutable: bool) -> Instruction {
|
||||
*Instruction::new(Operation::DefineLocal)
|
||||
.set_a(to_register)
|
||||
.set_b(local_index)
|
||||
.set_c_to_boolean(is_mutable)
|
||||
}
|
||||
|
||||
pub fn get_local(to_register: u16, local_index: u16) -> Instruction {
|
||||
*Instruction::new(Operation::GetLocal)
|
||||
.set_a(to_register)
|
||||
.set_b(local_index)
|
||||
}
|
||||
|
||||
pub fn set_local(from_register: u16, local_index: u16) -> Instruction {
|
||||
*Instruction::new(Operation::SetLocal)
|
||||
.set_a(from_register)
|
||||
.set_b(local_index)
|
||||
}
|
||||
|
||||
pub fn add(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
||||
*Instruction::new(Operation::Add)
|
||||
.set_a(to_register)
|
||||
.set_b(left_index)
|
||||
.set_c(right_index)
|
||||
}
|
||||
|
||||
pub fn subtract(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
||||
*Instruction::new(Operation::Subtract)
|
||||
.set_a(to_register)
|
||||
.set_b(left_index)
|
||||
.set_c(right_index)
|
||||
}
|
||||
|
||||
pub fn multiply(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
||||
*Instruction::new(Operation::Multiply)
|
||||
.set_a(to_register)
|
||||
.set_b(left_index)
|
||||
.set_c(right_index)
|
||||
}
|
||||
|
||||
pub fn divide(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
||||
*Instruction::new(Operation::Divide)
|
||||
.set_a(to_register)
|
||||
.set_b(left_index)
|
||||
.set_c(right_index)
|
||||
}
|
||||
|
||||
pub fn modulo(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
||||
*Instruction::new(Operation::Modulo)
|
||||
.set_a(to_register)
|
||||
.set_b(left_index)
|
||||
.set_c(right_index)
|
||||
}
|
||||
|
||||
pub fn test(test_register: u16, test_value: bool) -> Instruction {
|
||||
*Instruction::new(Operation::Test)
|
||||
.set_b(test_register)
|
||||
.set_c_to_boolean(test_value)
|
||||
}
|
||||
|
||||
pub fn test_set(to_register: u16, argument_index: u16, test_value: bool) -> Instruction {
|
||||
*Instruction::new(Operation::TestSet)
|
||||
.set_a(to_register)
|
||||
.set_b(argument_index)
|
||||
.set_c_to_boolean(test_value)
|
||||
}
|
||||
|
||||
pub fn equal(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction {
|
||||
*Instruction::new(Operation::Equal)
|
||||
.set_a_to_boolean(comparison_boolean)
|
||||
.set_b(left_index)
|
||||
.set_c(right_index)
|
||||
}
|
||||
|
||||
pub fn less(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction {
|
||||
*Instruction::new(Operation::Less)
|
||||
.set_a_to_boolean(comparison_boolean)
|
||||
.set_b(left_index)
|
||||
.set_c(right_index)
|
||||
}
|
||||
|
||||
pub fn less_equal(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction {
|
||||
*Instruction::new(Operation::LessEqual)
|
||||
.set_a_to_boolean(comparison_boolean)
|
||||
.set_b(left_index)
|
||||
.set_c(right_index)
|
||||
}
|
||||
|
||||
pub fn negate(to_register: u16, from_index: u16) -> Instruction {
|
||||
*Instruction::new(Operation::Negate)
|
||||
.set_a(to_register)
|
||||
.set_b(from_index)
|
||||
}
|
||||
|
||||
pub fn not(to_register: u16, from_index: u16) -> Instruction {
|
||||
*Instruction::new(Operation::Not)
|
||||
.set_a(to_register)
|
||||
.set_b(from_index)
|
||||
}
|
||||
|
||||
pub fn jump(jump_offset: u16, is_positive: bool) -> Instruction {
|
||||
*Instruction::new(Operation::Jump)
|
||||
.set_b(jump_offset)
|
||||
.set_c_to_boolean(is_positive)
|
||||
}
|
||||
|
||||
pub fn call(to_register: u16, function_register: u16, argument_count: u16) -> Instruction {
|
||||
*Instruction::new(Operation::Call)
|
||||
.set_a(to_register)
|
||||
.set_b(function_register)
|
||||
.set_c(argument_count)
|
||||
}
|
||||
|
||||
pub fn call_native(
|
||||
to_register: u16,
|
||||
native_fn: NativeFunction,
|
||||
argument_count: u16,
|
||||
) -> Instruction {
|
||||
*Instruction::new(Operation::CallNative)
|
||||
.set_a(to_register)
|
||||
.set_b(native_fn as u16)
|
||||
.set_c(argument_count)
|
||||
}
|
||||
|
||||
pub fn r#return(should_return_value: bool) -> Instruction {
|
||||
*Instruction::new(Operation::Return).set_b_to_boolean(should_return_value)
|
||||
}
|
||||
pub fn yields_value(&self) -> bool {
|
||||
match self.operation() {
|
||||
Operation::LoadBoolean
|
||||
| Operation::LoadConstant
|
||||
| Operation::LoadList
|
||||
| Operation::LoadSelf
|
||||
| Operation::GetLocal
|
||||
| Operation::Add
|
||||
| Operation::Subtract
|
||||
| Operation::Multiply
|
||||
| Operation::Divide
|
||||
| Operation::Modulo
|
||||
| Operation::Equal
|
||||
| Operation::Less
|
||||
| Operation::LessEqual
|
||||
| Operation::Negate
|
||||
| Operation::Not
|
||||
| Operation::Call => true,
|
||||
|
||||
Operation::CallNative => {
|
||||
let function = NativeFunction::from(self.b());
|
||||
|
||||
function.returns_value()
|
||||
}
|
||||
|
||||
Operation::Move
|
||||
| Operation::Close
|
||||
| Operation::DefineLocal
|
||||
| Operation::SetLocal
|
||||
| Operation::Test
|
||||
| Operation::TestSet
|
||||
| Operation::Jump
|
||||
| Operation::Return => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disassembly_info(&self, chunk: &Chunk) -> String {
|
||||
let InstructionBuilder {
|
||||
operation,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
b_is_constant,
|
||||
c_is_constant,
|
||||
a_is_local,
|
||||
b_is_local,
|
||||
c_is_local,
|
||||
} = InstructionBuilder::from(self);
|
||||
let format_arguments = || {
|
||||
let first_argument = if b_is_constant {
|
||||
format!("C{}", b)
|
||||
} else {
|
||||
format!("R{}", b)
|
||||
};
|
||||
let second_argument = if c_is_constant {
|
||||
format!("C{}", c)
|
||||
} else {
|
||||
format!("R{}", c)
|
||||
};
|
||||
|
||||
(first_argument, second_argument)
|
||||
};
|
||||
|
||||
match operation {
|
||||
Operation::Move => format!("R{a} = R{b}"),
|
||||
Operation::Close => {
|
||||
format!("R{b}..R{c}")
|
||||
}
|
||||
Operation::LoadBoolean => {
|
||||
let boolean = b != 0;
|
||||
let jump = c != 0;
|
||||
|
||||
if jump {
|
||||
format!("R{a} = {boolean} && JUMP +1")
|
||||
} else {
|
||||
format!("R{a} {boolean}")
|
||||
}
|
||||
}
|
||||
Operation::LoadConstant => {
|
||||
let jump = c != 0;
|
||||
|
||||
if jump {
|
||||
format!("R{a} = C{b} JUMP +1")
|
||||
} else {
|
||||
format!("R{a} = C{b}")
|
||||
}
|
||||
}
|
||||
Operation::LoadList => {
|
||||
format!("R{a} = [R{b}..=R{c}]",)
|
||||
}
|
||||
Operation::LoadSelf => {
|
||||
let name = chunk
|
||||
.name()
|
||||
.map(|idenifier| idenifier.as_str())
|
||||
.unwrap_or("self");
|
||||
|
||||
format!("R{a} = {name}")
|
||||
}
|
||||
Operation::DefineLocal => {
|
||||
format!("L{b} = R{a}")
|
||||
}
|
||||
Operation::GetLocal => {
|
||||
format!("R{a} = L{b}")
|
||||
}
|
||||
Operation::SetLocal => {
|
||||
format!("L{b} = R{a}")
|
||||
}
|
||||
Operation::Add => {
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("R{a} = {first_argument} + {second_argument}",)
|
||||
}
|
||||
Operation::Subtract => {
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("R{a} = {first_argument} - {second_argument}",)
|
||||
}
|
||||
Operation::Multiply => {
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("R{a} = {first_argument} * {second_argument}",)
|
||||
}
|
||||
Operation::Divide => {
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("R{a} = {first_argument} / {second_argument}",)
|
||||
}
|
||||
Operation::Modulo => {
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("R{a} = {first_argument} % {second_argument}",)
|
||||
}
|
||||
Operation::Test => {
|
||||
let test_register = if b_is_constant {
|
||||
format!("C{b}")
|
||||
} else {
|
||||
format!("R{b}")
|
||||
};
|
||||
let test_value = c != 0;
|
||||
let bang = if test_value { "" } else { "!" };
|
||||
|
||||
format!("if {bang}{test_register} {{ JUMP +1 }}",)
|
||||
}
|
||||
Operation::TestSet => {
|
||||
let test_register = if b_is_constant {
|
||||
format!("C{b}")
|
||||
} else {
|
||||
format!("R{b}")
|
||||
};
|
||||
let test_value = c != 0;
|
||||
let bang = if test_value { "" } else { "!" };
|
||||
|
||||
format!(
|
||||
"if {bang}R{test_register} {{ JUMP +1 }} else {{ R{a} = R{test_register} }}"
|
||||
)
|
||||
}
|
||||
Operation::Equal => {
|
||||
let comparison_symbol = if a != 0 { "==" } else { "!=" };
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("if {first_argument} {comparison_symbol} {second_argument} {{ JUMP +1 }}")
|
||||
}
|
||||
Operation::Less => {
|
||||
let comparison_symbol = if a != 0 { "<" } else { ">=" };
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("if {first_argument} {comparison_symbol} {second_argument} {{ JUMP +1 }}")
|
||||
}
|
||||
Operation::LessEqual => {
|
||||
let comparison_symbol = if a != 0 { "<=" } else { ">" };
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("if {first_argument} {comparison_symbol} {second_argument} {{ JUMP +1 }}")
|
||||
}
|
||||
Operation::Negate => {
|
||||
let argument = if b_is_constant {
|
||||
format!("C{b}")
|
||||
} else {
|
||||
format!("R{b}")
|
||||
};
|
||||
|
||||
format!("R{a} = -{argument}")
|
||||
}
|
||||
Operation::Not => {
|
||||
let argument = if b_is_constant {
|
||||
format!("C{b}")
|
||||
} else {
|
||||
format!("R{b}")
|
||||
};
|
||||
|
||||
format!("R{a} = !{argument}")
|
||||
}
|
||||
Operation::Jump => {
|
||||
let is_positive = c != 0;
|
||||
|
||||
if is_positive {
|
||||
format!("JUMP +{b}")
|
||||
} else {
|
||||
format!("JUMP -{b}")
|
||||
}
|
||||
}
|
||||
Operation::Call => {
|
||||
let argument_count = c;
|
||||
|
||||
let mut output = format!("R{a} = R{b}(");
|
||||
|
||||
if argument_count != 0 {
|
||||
let first_argument = b + 1;
|
||||
|
||||
for (index, register) in
|
||||
(first_argument..first_argument + argument_count).enumerate()
|
||||
{
|
||||
if index > 0 {
|
||||
output.push_str(", ");
|
||||
}
|
||||
|
||||
output.push_str(&format!("R{}", register));
|
||||
}
|
||||
}
|
||||
|
||||
output.push(')');
|
||||
|
||||
output
|
||||
}
|
||||
Operation::CallNative => {
|
||||
let native_function = NativeFunction::from(b);
|
||||
let argument_count = c;
|
||||
let mut output = String::new();
|
||||
let native_function_name = native_function.as_str();
|
||||
|
||||
output.push_str(&format!("R{a} = {}(", native_function_name));
|
||||
|
||||
if argument_count != 0 {
|
||||
let first_argument = a.saturating_sub(argument_count);
|
||||
|
||||
for register in first_argument..a {
|
||||
if register != first_argument {
|
||||
output.push_str(", ");
|
||||
}
|
||||
|
||||
output.push_str(&format!("R{}", register));
|
||||
}
|
||||
}
|
||||
|
||||
output.push(')');
|
||||
|
||||
output
|
||||
}
|
||||
Operation::Return => {
|
||||
let should_return_value = b != 0;
|
||||
|
||||
if should_return_value {
|
||||
"RETURN".to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Instruction> for u64 {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
instruction.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn builder() {
|
||||
let instruction_from_builder = Instruction::builder(Operation::Add)
|
||||
.a(1)
|
||||
.b(2)
|
||||
.c(3)
|
||||
.b_is_constant(true)
|
||||
.c_is_constant(true)
|
||||
.a_is_local(true)
|
||||
.b_is_local(true)
|
||||
.c_is_local(true)
|
||||
.build();
|
||||
let instruction = *Instruction::add(1, 2, 3)
|
||||
.set_b_is_constant()
|
||||
.set_c_is_constant()
|
||||
.set_a_is_local()
|
||||
.set_b_is_local()
|
||||
.set_c_is_local();
|
||||
|
||||
assert_eq!(instruction_from_builder, instruction);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn r#move() {
|
||||
let instruction = Instruction::r#move(4, 1);
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Move);
|
||||
assert_eq!(instruction.b(), 4);
|
||||
assert_eq!(instruction.c(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn close() {
|
||||
let instruction = Instruction::close(1, 2);
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Close);
|
||||
assert_eq!(instruction.b(), 1);
|
||||
assert_eq!(instruction.c(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_boolean() {
|
||||
let instruction = Instruction::load_boolean(4, true, true);
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::LoadBoolean);
|
||||
assert_eq!(instruction.a(), 4);
|
||||
assert!(instruction.a_as_boolean());
|
||||
assert!(instruction.c_as_boolean());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_constant() {
|
||||
let mut instruction = Instruction::load_constant(4, 1, true);
|
||||
|
||||
instruction.set_b_is_constant();
|
||||
instruction.set_c_is_constant();
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::LoadConstant);
|
||||
assert_eq!(instruction.a(), 4);
|
||||
assert_eq!(instruction.b(), 1);
|
||||
assert!(instruction.b_is_constant());
|
||||
assert!(instruction.b_is_constant());
|
||||
assert!(instruction.c_as_boolean());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_list() {
|
||||
let instruction = Instruction::load_list(4, 1);
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::LoadList);
|
||||
assert_eq!(instruction.a(), 4);
|
||||
assert_eq!(instruction.b(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_self() {
|
||||
let instruction = Instruction::load_self(10);
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::LoadSelf);
|
||||
assert_eq!(instruction.a(), 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declare_local() {
|
||||
let instruction = *Instruction::define_local(4, 1, true).set_b_is_constant();
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::DefineLocal);
|
||||
assert_eq!(instruction.a(), 4);
|
||||
assert_eq!(instruction.b(), 1);
|
||||
assert!(instruction.c_as_boolean());
|
||||
assert!(instruction.b_is_constant());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add() {
|
||||
let instruction = *Instruction::add(1, 1, 4).set_b_is_constant();
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Add);
|
||||
assert_eq!(instruction.a(), 1);
|
||||
assert_eq!(instruction.b(), 1);
|
||||
assert_eq!(instruction.c(), 4);
|
||||
assert!(instruction.b_is_constant());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subtract() {
|
||||
let mut instruction = Instruction::subtract(4, 1, 2);
|
||||
|
||||
instruction.set_b_is_constant();
|
||||
instruction.set_c_is_constant();
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Subtract);
|
||||
assert_eq!(instruction.a(), 4);
|
||||
assert_eq!(instruction.b(), 1);
|
||||
assert_eq!(instruction.c(), 2);
|
||||
assert!(instruction.b_is_constant());
|
||||
assert!(instruction.b_is_constant());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiply() {
|
||||
let mut instruction = Instruction::multiply(4, 1, 2);
|
||||
|
||||
instruction.set_b_is_constant();
|
||||
instruction.set_c_is_constant();
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Multiply);
|
||||
assert_eq!(instruction.a(), 4);
|
||||
assert_eq!(instruction.b(), 1);
|
||||
assert_eq!(instruction.c(), 2);
|
||||
assert!(instruction.b_is_constant());
|
||||
assert!(instruction.b_is_constant());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn divide() {
|
||||
let mut instruction = Instruction::divide(4, 1, 2);
|
||||
|
||||
instruction.set_b_is_constant();
|
||||
instruction.set_c_is_constant();
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Divide);
|
||||
assert_eq!(instruction.a(), 4);
|
||||
assert_eq!(instruction.b(), 1);
|
||||
assert_eq!(instruction.c(), 2);
|
||||
assert!(instruction.b_is_constant());
|
||||
assert!(instruction.b_is_constant());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let instruction = Instruction::test(42, true);
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Test);
|
||||
assert_eq!(instruction.b(), 42);
|
||||
assert!(instruction.c_as_boolean());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set() {
|
||||
let instruction = Instruction::test_set(4, 1, true);
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::TestSet);
|
||||
assert_eq!(instruction.a(), 4);
|
||||
assert_eq!(instruction.b(), 1);
|
||||
assert!(instruction.c_as_boolean());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal() {
|
||||
let mut instruction = Instruction::equal(true, 1, 2);
|
||||
|
||||
instruction.set_b_is_constant();
|
||||
instruction.set_c_is_constant();
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Equal);
|
||||
assert!(instruction.a_as_boolean());
|
||||
assert_eq!(instruction.b(), 1);
|
||||
assert_eq!(instruction.c(), 2);
|
||||
assert!(instruction.b_is_constant());
|
||||
assert!(instruction.b_is_constant());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negate() {
|
||||
let mut instruction = Instruction::negate(4, 1);
|
||||
|
||||
instruction.set_b_is_constant();
|
||||
instruction.set_c_is_constant();
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Negate);
|
||||
assert_eq!(instruction.a(), 4);
|
||||
assert_eq!(instruction.b(), 1);
|
||||
assert!(instruction.b_is_constant());
|
||||
assert!(instruction.b_is_constant());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not() {
|
||||
let mut instruction = Instruction::not(4, 1);
|
||||
|
||||
instruction.set_b_is_constant();
|
||||
instruction.set_c_is_constant();
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Not);
|
||||
assert_eq!(instruction.a(), 4);
|
||||
assert_eq!(instruction.b(), 1);
|
||||
assert!(instruction.b_is_constant());
|
||||
assert!(instruction.b_is_constant());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jump() {
|
||||
let instruction = Instruction::jump(4, true);
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Jump);
|
||||
|
||||
assert_eq!(instruction.b(), 4);
|
||||
assert!(instruction.c_as_boolean());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call() {
|
||||
let instruction = Instruction::call(1, 3, 4);
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Call);
|
||||
assert_eq!(instruction.a(), 1);
|
||||
assert_eq!(instruction.b(), 3);
|
||||
assert_eq!(instruction.c(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn r#return() {
|
||||
let instruction = Instruction::r#return(true);
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::Return);
|
||||
assert!(instruction.b_as_boolean());
|
||||
}
|
||||
}
|
32
dust-lang/src/instruction/add.rs
Normal file
32
dust-lang/src/instruction/add.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Add {
|
||||
pub destination: u16,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Add {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Add {
|
||||
destination: instruction.a(),
|
||||
left,
|
||||
right,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Add> for Instruction {
|
||||
fn from(add: Add) -> Self {
|
||||
*Instruction::new(Operation::Add)
|
||||
.set_a(add.destination)
|
||||
.set_b(add.left.index())
|
||||
.set_b_is_constant(add.left.is_constant())
|
||||
.set_b_is_local(add.left.is_local())
|
||||
.set_c(add.right.index())
|
||||
.set_c_is_constant(add.right.is_constant())
|
||||
.set_c_is_local(add.right.is_local())
|
||||
}
|
||||
}
|
28
dust-lang/src/instruction/call.rs
Normal file
28
dust-lang/src/instruction/call.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Call {
|
||||
pub destination: u16,
|
||||
pub function: Argument,
|
||||
pub argument_count: u16,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Call {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
Call {
|
||||
destination: instruction.a(),
|
||||
function: instruction.b_as_argument(),
|
||||
argument_count: instruction.c(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Call> for Instruction {
|
||||
fn from(call: Call) -> Self {
|
||||
*Instruction::new(Operation::Call)
|
||||
.set_a(call.destination)
|
||||
.set_b(call.function.index())
|
||||
.set_b_is_constant(call.function.is_constant())
|
||||
.set_b_is_local(call.function.is_local())
|
||||
.set_c(call.argument_count)
|
||||
}
|
||||
}
|
26
dust-lang/src/instruction/call_native.rs
Normal file
26
dust-lang/src/instruction/call_native.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use crate::{Instruction, NativeFunction, Operation};
|
||||
|
||||
pub struct CallNative {
|
||||
pub destination: u16,
|
||||
pub function: NativeFunction,
|
||||
pub argument_count: u16,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for CallNative {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
CallNative {
|
||||
destination: instruction.a(),
|
||||
function: NativeFunction::from(instruction.b()),
|
||||
argument_count: instruction.c(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CallNative> for Instruction {
|
||||
fn from(call_native: CallNative) -> Self {
|
||||
*Instruction::new(Operation::CallNative)
|
||||
.set_a(call_native.destination)
|
||||
.set_b(call_native.function as u16)
|
||||
.set_c(call_native.argument_count)
|
||||
}
|
||||
}
|
23
dust-lang/src/instruction/close.rs
Normal file
23
dust-lang/src/instruction/close.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct Close {
|
||||
pub from: u16,
|
||||
pub to: u16,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Close {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
Close {
|
||||
from: instruction.b(),
|
||||
to: instruction.a(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Close> for Instruction {
|
||||
fn from(r#move: Close) -> Self {
|
||||
*Instruction::new(Operation::Move)
|
||||
.set_b(r#move.from)
|
||||
.set_c(r#move.to)
|
||||
}
|
||||
}
|
26
dust-lang/src/instruction/define_local.rs
Normal file
26
dust-lang/src/instruction/define_local.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct DefineLocal {
|
||||
pub register: u16,
|
||||
pub local_index: u16,
|
||||
pub is_mutable: bool,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for DefineLocal {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
DefineLocal {
|
||||
register: instruction.a(),
|
||||
local_index: instruction.b(),
|
||||
is_mutable: instruction.c_as_boolean(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DefineLocal> for Instruction {
|
||||
fn from(define_local: DefineLocal) -> Self {
|
||||
*Instruction::new(Operation::DefineLocal)
|
||||
.set_a(define_local.register)
|
||||
.set_b(define_local.local_index)
|
||||
.set_c_to_boolean(define_local.is_mutable)
|
||||
}
|
||||
}
|
32
dust-lang/src/instruction/divide.rs
Normal file
32
dust-lang/src/instruction/divide.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Divide {
|
||||
pub destination: u16,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Divide {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Divide {
|
||||
destination: instruction.a(),
|
||||
left,
|
||||
right,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Divide> for Instruction {
|
||||
fn from(divide: Divide) -> Self {
|
||||
*Instruction::new(Operation::Divide)
|
||||
.set_a(divide.destination)
|
||||
.set_b(divide.left.index())
|
||||
.set_b_is_constant(divide.left.is_constant())
|
||||
.set_b_is_local(divide.left.is_local())
|
||||
.set_c(divide.right.index())
|
||||
.set_c_is_constant(divide.right.is_constant())
|
||||
.set_c_is_local(divide.right.is_local())
|
||||
}
|
||||
}
|
32
dust-lang/src/instruction/equal.rs
Normal file
32
dust-lang/src/instruction/equal.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Equal {
|
||||
pub value: bool,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Equal {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Equal {
|
||||
value: instruction.a_as_boolean(),
|
||||
left,
|
||||
right,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Equal> for Instruction {
|
||||
fn from(equal: Equal) -> Self {
|
||||
*Instruction::new(Operation::Equal)
|
||||
.set_a_to_boolean(equal.value)
|
||||
.set_b(equal.left.index())
|
||||
.set_b_is_constant(equal.left.is_constant())
|
||||
.set_b_is_local(equal.left.is_local())
|
||||
.set_c(equal.right.index())
|
||||
.set_c_is_constant(equal.right.is_constant())
|
||||
.set_c_is_local(equal.right.is_local())
|
||||
}
|
||||
}
|
23
dust-lang/src/instruction/get_local.rs
Normal file
23
dust-lang/src/instruction/get_local.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct GetLocal {
|
||||
pub destination: u16,
|
||||
pub local_index: u16,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for GetLocal {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
GetLocal {
|
||||
destination: instruction.a(),
|
||||
local_index: instruction.b(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GetLocal> for Instruction {
|
||||
fn from(get_local: GetLocal) -> Self {
|
||||
*Instruction::new(Operation::GetLocal)
|
||||
.set_a(get_local.destination)
|
||||
.set_b(get_local.local_index)
|
||||
}
|
||||
}
|
23
dust-lang/src/instruction/jump.rs
Normal file
23
dust-lang/src/instruction/jump.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct Jump {
|
||||
pub offset: u16,
|
||||
pub is_positive: bool,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Jump {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
Jump {
|
||||
offset: instruction.b(),
|
||||
is_positive: instruction.c_as_boolean(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Jump> for Instruction {
|
||||
fn from(jump: Jump) -> Self {
|
||||
*Instruction::new(Operation::Jump)
|
||||
.set_b(jump.offset)
|
||||
.set_c_to_boolean(jump.is_positive)
|
||||
}
|
||||
}
|
32
dust-lang/src/instruction/less.rs
Normal file
32
dust-lang/src/instruction/less.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Less {
|
||||
pub value: bool,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Less {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Less {
|
||||
value: instruction.a_as_boolean(),
|
||||
left,
|
||||
right,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Less> for Instruction {
|
||||
fn from(less: Less) -> Self {
|
||||
*Instruction::new(Operation::Less)
|
||||
.set_a_to_boolean(less.value)
|
||||
.set_b(less.left.index())
|
||||
.set_b_is_constant(less.left.is_constant())
|
||||
.set_b_is_local(less.left.is_local())
|
||||
.set_c(less.right.index())
|
||||
.set_c_is_constant(less.right.is_constant())
|
||||
.set_c_is_local(less.right.is_local())
|
||||
}
|
||||
}
|
32
dust-lang/src/instruction/less_equal.rs
Normal file
32
dust-lang/src/instruction/less_equal.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct LessEqual {
|
||||
pub value: bool,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for LessEqual {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
LessEqual {
|
||||
value: instruction.a_as_boolean(),
|
||||
left,
|
||||
right,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LessEqual> for Instruction {
|
||||
fn from(less_equal: LessEqual) -> Self {
|
||||
*Instruction::new(Operation::LessEqual)
|
||||
.set_a_to_boolean(less_equal.value)
|
||||
.set_b(less_equal.left.index())
|
||||
.set_b_is_constant(less_equal.left.is_constant())
|
||||
.set_b_is_local(less_equal.left.is_local())
|
||||
.set_c(less_equal.right.index())
|
||||
.set_c_is_constant(less_equal.right.is_constant())
|
||||
.set_c_is_local(less_equal.right.is_local())
|
||||
}
|
||||
}
|
26
dust-lang/src/instruction/load_boolean.rs
Normal file
26
dust-lang/src/instruction/load_boolean.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct LoadBoolean {
|
||||
pub destination: u16,
|
||||
pub value: bool,
|
||||
pub jump_next: bool,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for LoadBoolean {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
LoadBoolean {
|
||||
destination: instruction.a(),
|
||||
value: instruction.b_as_boolean(),
|
||||
jump_next: instruction.c_as_boolean(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoadBoolean> for Instruction {
|
||||
fn from(load_boolean: LoadBoolean) -> Self {
|
||||
*Instruction::new(Operation::LoadBoolean)
|
||||
.set_a(load_boolean.destination)
|
||||
.set_b_to_boolean(load_boolean.value)
|
||||
.set_c_to_boolean(load_boolean.jump_next)
|
||||
}
|
||||
}
|
26
dust-lang/src/instruction/load_constant.rs
Normal file
26
dust-lang/src/instruction/load_constant.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct LoadConstant {
|
||||
pub destination: u16,
|
||||
pub constant_index: u16,
|
||||
pub jump_next: bool,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for LoadConstant {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
LoadConstant {
|
||||
destination: instruction.a(),
|
||||
constant_index: instruction.b(),
|
||||
jump_next: instruction.c_as_boolean(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoadConstant> for Instruction {
|
||||
fn from(load_constant: LoadConstant) -> Self {
|
||||
*Instruction::new(Operation::LoadConstant)
|
||||
.set_a(load_constant.destination)
|
||||
.set_b(load_constant.constant_index)
|
||||
.set_c_to_boolean(load_constant.jump_next)
|
||||
}
|
||||
}
|
23
dust-lang/src/instruction/load_list.rs
Normal file
23
dust-lang/src/instruction/load_list.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct LoadList {
|
||||
pub destination: u16,
|
||||
pub start_register: u16,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for LoadList {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
LoadList {
|
||||
destination: instruction.a(),
|
||||
start_register: instruction.b(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoadList> for Instruction {
|
||||
fn from(load_list: LoadList) -> Self {
|
||||
*Instruction::new(Operation::LoadList)
|
||||
.set_a(load_list.destination)
|
||||
.set_b(load_list.start_register)
|
||||
}
|
||||
}
|
19
dust-lang/src/instruction/load_self.rs
Normal file
19
dust-lang/src/instruction/load_self.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct LoadSelf {
|
||||
pub destination: u16,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for LoadSelf {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
LoadSelf {
|
||||
destination: instruction.a(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoadSelf> for Instruction {
|
||||
fn from(load_self: LoadSelf) -> Self {
|
||||
*Instruction::new(Operation::LoadSelf).set_a(load_self.destination)
|
||||
}
|
||||
}
|
544
dust-lang/src/instruction/mod.rs
Normal file
544
dust-lang/src/instruction/mod.rs
Normal file
@ -0,0 +1,544 @@
|
||||
//! An operation and its arguments for the Dust virtual machine.
|
||||
//!
|
||||
//! Each instruction is a 64-bit unsigned integer that is divided into five fields:
|
||||
//! - Bits 0-8: The operation code.
|
||||
//! - Bit 9: Boolean flag indicating whether the B argument is a constant.
|
||||
//! - Bit 10: Boolean flag indicating whether the C argument is a constant.
|
||||
//! - Bit 11: Boolean flag indicating whether the A argument is a local.
|
||||
//! - Bit 12: Boolean flag indicating whether the B argument is a local.
|
||||
//! - Bit 13: Boolean flag indicating whether the C argument is a local.
|
||||
//! - Bits 17-32: The A argument,
|
||||
//! - Bits 33-48: The B argument.
|
||||
//! - Bits 49-63: The C argument.
|
||||
//!
|
||||
//! 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.
|
||||
mod add;
|
||||
mod call;
|
||||
mod call_native;
|
||||
mod close;
|
||||
mod define_local;
|
||||
mod divide;
|
||||
mod equal;
|
||||
mod get_local;
|
||||
mod jump;
|
||||
mod less;
|
||||
mod less_equal;
|
||||
mod load_boolean;
|
||||
mod load_constant;
|
||||
mod load_list;
|
||||
mod load_self;
|
||||
mod modulo;
|
||||
mod r#move;
|
||||
mod multiply;
|
||||
mod negate;
|
||||
mod not;
|
||||
mod r#return;
|
||||
mod set_local;
|
||||
mod subtract;
|
||||
mod test;
|
||||
mod test_set;
|
||||
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
pub use add::Add;
|
||||
pub use call::Call;
|
||||
pub use call_native::CallNative;
|
||||
pub use close::Close;
|
||||
pub use define_local::DefineLocal;
|
||||
pub use divide::Divide;
|
||||
pub use equal::Equal;
|
||||
pub use get_local::GetLocal;
|
||||
pub use jump::Jump;
|
||||
pub use less::Less;
|
||||
pub use less_equal::LessEqual;
|
||||
pub use load_boolean::LoadBoolean;
|
||||
pub use load_constant::LoadConstant;
|
||||
pub use load_list::LoadList;
|
||||
pub use load_self::LoadSelf;
|
||||
pub use modulo::Modulo;
|
||||
pub use multiply::Multiply;
|
||||
pub use negate::Negate;
|
||||
pub use not::Not;
|
||||
pub use r#move::Move;
|
||||
pub use r#return::Return;
|
||||
pub use set_local::SetLocal;
|
||||
pub use subtract::Subtract;
|
||||
pub use test::Test;
|
||||
pub use test_set::TestSet;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{Chunk, NativeFunction, Operation};
|
||||
|
||||
pub enum Argument {
|
||||
Constant(u16),
|
||||
Local(u16),
|
||||
Register(u16),
|
||||
}
|
||||
|
||||
impl Argument {
|
||||
pub fn index(&self) -> u16 {
|
||||
match self {
|
||||
Argument::Constant(index) => *index,
|
||||
Argument::Local(index) => *index,
|
||||
Argument::Register(index) => *index,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_constant(&self) -> bool {
|
||||
matches!(self, Argument::Constant(_))
|
||||
}
|
||||
|
||||
pub fn is_local(&self) -> bool {
|
||||
matches!(self, Argument::Local(_))
|
||||
}
|
||||
|
||||
pub fn is_register(&self) -> bool {
|
||||
matches!(self, Argument::Register(_))
|
||||
}
|
||||
}
|
||||
|
||||
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}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An operation and its arguments for the Dust virtual machine.
|
||||
///
|
||||
/// See the [module-level documentation](index.html) for more information.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct Instruction(u64);
|
||||
|
||||
impl Instruction {
|
||||
pub fn new(operation: Operation) -> Instruction {
|
||||
Instruction(operation as u64)
|
||||
}
|
||||
|
||||
pub fn destination_as_argument(&self) -> Option<Argument> {
|
||||
if self.yields_value() {
|
||||
Some(Argument::Register(self.a()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn b_as_argument(&self) -> Argument {
|
||||
if self.b_is_constant() {
|
||||
Argument::Constant(self.b())
|
||||
} else if self.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.b_is_constant() {
|
||||
Argument::Constant(self.b())
|
||||
} else if self.b_is_local() {
|
||||
Argument::Local(self.b())
|
||||
} else {
|
||||
Argument::Register(self.b())
|
||||
};
|
||||
let right = if self.c_is_constant() {
|
||||
Argument::Constant(self.c())
|
||||
} else if self.c_is_local() {
|
||||
Argument::Local(self.c())
|
||||
} else {
|
||||
Argument::Register(self.c())
|
||||
};
|
||||
|
||||
(left, right)
|
||||
}
|
||||
|
||||
pub fn operation(&self) -> Operation {
|
||||
Operation::from((self.0 & 0b11111111) as u8)
|
||||
}
|
||||
|
||||
pub fn set_b_is_constant(&mut self, boolean: bool) -> &mut Self {
|
||||
self.0 = (self.0 & !(1 << 9)) | ((boolean as u64) << 9);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_c_is_constant(&mut self, boolean: bool) -> &mut Self {
|
||||
self.0 = (self.0 & !(1 << 10)) | ((boolean as u64) << 10);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_a_is_local(&mut self, boolean: bool) -> &mut Self {
|
||||
self.0 = (self.0 & !(1 << 11)) | ((boolean as u64) << 11);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_b_is_local(&mut self, boolean: bool) -> &mut Self {
|
||||
self.0 = (self.0 & !(1 << 12)) | ((boolean as u64) << 12);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_c_is_local(&mut self, boolean: bool) -> &mut Self {
|
||||
self.0 = (self.0 & !(1 << 13)) | ((boolean as u64) << 13);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn a(&self) -> u16 {
|
||||
((self.0 >> 16) & 0b1111111111111111) as u16
|
||||
}
|
||||
|
||||
pub fn a_as_boolean(&self) -> bool {
|
||||
self.a() != 0
|
||||
}
|
||||
|
||||
pub fn set_a(&mut self, a: u16) -> &mut Self {
|
||||
self.0 = (self.0 & !(0b1111111111111111 << 16)) | ((a as u64) << 16);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_a_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
||||
self.0 = (self.0 & !(0b1111111111111111 << 16)) | ((boolean as u64) << 16);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn b(&self) -> u16 {
|
||||
((self.0 >> 32) & 0b1111111111111111) as u16
|
||||
}
|
||||
|
||||
pub fn b_as_boolean(&self) -> bool {
|
||||
self.b() != 0
|
||||
}
|
||||
|
||||
pub fn set_b(&mut self, b: u16) -> &mut Self {
|
||||
self.0 = (self.0 & !(0b1111111111111111 << 32)) | ((b as u64) << 32);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_b_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
||||
self.0 = (self.0 & !(0b1111111111111111 << 32)) | ((boolean as u64) << 32);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn c(&self) -> u16 {
|
||||
((self.0 >> 48) & 0b1111111111111111) as u16
|
||||
}
|
||||
|
||||
pub fn c_as_boolean(&self) -> bool {
|
||||
self.c() != 0
|
||||
}
|
||||
|
||||
pub fn set_c(&mut self, c: u16) -> &mut Self {
|
||||
self.0 = (self.0 & !(0b1111111111111111 << 48)) | ((c as u64) << 48);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_c_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
||||
self.0 = (self.0 & !(0b1111111111111111 << 48)) | ((boolean as u64) << 48);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn b_is_constant(&self) -> bool {
|
||||
(self.0 >> 9) & 1 == 1
|
||||
}
|
||||
|
||||
pub fn c_is_constant(&self) -> bool {
|
||||
(self.0 >> 10) & 1 == 1
|
||||
}
|
||||
|
||||
pub fn a_is_local(&self) -> bool {
|
||||
(self.0 >> 11) & 1 == 1
|
||||
}
|
||||
|
||||
pub fn b_is_local(&self) -> bool {
|
||||
(self.0 >> 12) & 1 == 1
|
||||
}
|
||||
|
||||
pub fn c_is_local(&self) -> bool {
|
||||
(self.0 >> 13) & 1 == 1
|
||||
}
|
||||
|
||||
pub fn yields_value(&self) -> bool {
|
||||
match self.operation() {
|
||||
Operation::LoadBoolean
|
||||
| Operation::LoadConstant
|
||||
| Operation::LoadList
|
||||
| Operation::LoadSelf
|
||||
| Operation::GetLocal
|
||||
| Operation::Add
|
||||
| Operation::Subtract
|
||||
| Operation::Multiply
|
||||
| Operation::Divide
|
||||
| Operation::Modulo
|
||||
| Operation::Equal
|
||||
| Operation::Less
|
||||
| Operation::LessEqual
|
||||
| Operation::Negate
|
||||
| Operation::Not
|
||||
| Operation::Call => true,
|
||||
|
||||
Operation::CallNative => {
|
||||
let function = NativeFunction::from(self.b());
|
||||
|
||||
function.returns_value()
|
||||
}
|
||||
|
||||
Operation::Move
|
||||
| Operation::Close
|
||||
| Operation::DefineLocal
|
||||
| Operation::SetLocal
|
||||
| Operation::Test
|
||||
| Operation::TestSet
|
||||
| Operation::Jump
|
||||
| Operation::Return => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disassembly_info(&self, chunk: &Chunk) -> String {
|
||||
match self.operation() {
|
||||
Operation::Move => {
|
||||
let Move { from, to } = Move::from(self);
|
||||
|
||||
format!("{to} = {from}")
|
||||
}
|
||||
Operation::Close => {
|
||||
let Close { from, to } = Close::from(self);
|
||||
|
||||
format!("{from}..={to}")
|
||||
}
|
||||
Operation::LoadBoolean => {
|
||||
let LoadBoolean {
|
||||
destination,
|
||||
value,
|
||||
jump_next,
|
||||
} = LoadBoolean::from(self);
|
||||
|
||||
if jump_next {
|
||||
format!("R{destination} = {value} && JUMP +1")
|
||||
} else {
|
||||
format!("R{destination} = {value}")
|
||||
}
|
||||
}
|
||||
Operation::LoadConstant => {
|
||||
let LoadConstant {
|
||||
destination,
|
||||
constant_index: constant,
|
||||
jump_next,
|
||||
} = LoadConstant::from(self);
|
||||
|
||||
if jump_next {
|
||||
format!("R{destination} = C{constant} JUMP +1")
|
||||
} else {
|
||||
format!("R{destination} = C{constant}")
|
||||
}
|
||||
}
|
||||
Operation::LoadList => {
|
||||
let LoadList {
|
||||
destination,
|
||||
start_register,
|
||||
} = LoadList::from(self);
|
||||
let end_register = destination - 1;
|
||||
|
||||
format!("R{destination} = [R{start_register}..=R{end_register}]",)
|
||||
}
|
||||
Operation::LoadSelf => {
|
||||
let LoadSelf { destination } = LoadSelf::from(self);
|
||||
let name = chunk
|
||||
.name()
|
||||
.map(|idenifier| idenifier.as_str())
|
||||
.unwrap_or("self");
|
||||
|
||||
format!("R{destination} = {name}")
|
||||
}
|
||||
Operation::DefineLocal => {
|
||||
let DefineLocal {
|
||||
register,
|
||||
local_index,
|
||||
is_mutable,
|
||||
} = DefineLocal::from(self);
|
||||
|
||||
if is_mutable {
|
||||
format!("mut L{local_index} = R{register}")
|
||||
} else {
|
||||
format!("L{local_index} = R{register}")
|
||||
}
|
||||
}
|
||||
Operation::GetLocal => {
|
||||
let GetLocal {
|
||||
destination,
|
||||
local_index,
|
||||
} = GetLocal::from(self);
|
||||
|
||||
format!("R{destination} = L{local_index}")
|
||||
}
|
||||
Operation::SetLocal => {
|
||||
let SetLocal {
|
||||
register,
|
||||
local_index,
|
||||
} = SetLocal::from(self);
|
||||
|
||||
format!("L{local_index} = R{register}")
|
||||
}
|
||||
Operation::Add => {
|
||||
let Add {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = Add::from(self);
|
||||
|
||||
format!("R{destination} = {left} + {right}")
|
||||
}
|
||||
Operation::Subtract => {
|
||||
let Subtract {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = Subtract::from(self);
|
||||
|
||||
format!("R{destination} = {left} - {right}")
|
||||
}
|
||||
Operation::Multiply => {
|
||||
let Multiply {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = Multiply::from(self);
|
||||
|
||||
format!("R{destination} = {left} * {right}")
|
||||
}
|
||||
Operation::Divide => {
|
||||
let Divide {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = Divide::from(self);
|
||||
|
||||
format!("R{destination} = {left} / {right}")
|
||||
}
|
||||
Operation::Modulo => {
|
||||
let Modulo {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = Modulo::from(self);
|
||||
|
||||
format!("R{destination} = {left} % {right}")
|
||||
}
|
||||
Operation::Test => {
|
||||
let Test { argument, value } = Test::from(self);
|
||||
let bang = if value { "" } else { "!" };
|
||||
|
||||
format!("if {bang}{argument} {{ JUMP +1 }}",)
|
||||
}
|
||||
Operation::TestSet => {
|
||||
let TestSet {
|
||||
destination,
|
||||
argument,
|
||||
value,
|
||||
} = TestSet::from(self);
|
||||
let bang = if value { "" } else { "!" };
|
||||
|
||||
format!("if {bang}{argument} {{ JUMP +1 }} else {{ R{destination} = {argument} }}")
|
||||
}
|
||||
Operation::Equal => {
|
||||
let Equal { value, left, right } = Equal::from(self);
|
||||
let comparison_symbol = if value { "==" } else { "!=" };
|
||||
|
||||
format!("if {left} {comparison_symbol} {right} {{ JUMP +1 }}")
|
||||
}
|
||||
Operation::Less => {
|
||||
let Equal { value, left, right } = Equal::from(self);
|
||||
let comparison_symbol = if value { "<" } else { ">=" };
|
||||
|
||||
format!("if {left} {comparison_symbol} {right} {{ JUMP +1 }}")
|
||||
}
|
||||
Operation::LessEqual => {
|
||||
let LessEqual { value, left, right } = LessEqual::from(self);
|
||||
let comparison_symbol = if value { "<=" } else { ">" };
|
||||
|
||||
format!("if {left} {comparison_symbol} {right} {{ JUMP +1 }}")
|
||||
}
|
||||
Operation::Negate => {
|
||||
let Negate {
|
||||
destination,
|
||||
argument,
|
||||
} = Negate::from(self);
|
||||
|
||||
format!("R{destination} = -{argument}")
|
||||
}
|
||||
Operation::Not => {
|
||||
let Not {
|
||||
destination,
|
||||
argument,
|
||||
} = Not::from(self);
|
||||
|
||||
format!("R{destination} = !{argument}")
|
||||
}
|
||||
Operation::Jump => {
|
||||
let Jump {
|
||||
offset,
|
||||
is_positive,
|
||||
} = Jump::from(self);
|
||||
|
||||
if is_positive {
|
||||
format!("JUMP +{offset}")
|
||||
} else {
|
||||
format!("JUMP -{offset}")
|
||||
}
|
||||
}
|
||||
Operation::Call => {
|
||||
let Call {
|
||||
destination,
|
||||
function,
|
||||
argument_count,
|
||||
} = Call::from(self);
|
||||
let first_argument = destination.saturating_sub(argument_count);
|
||||
let last_argument = destination - 1;
|
||||
|
||||
format!("R{destination} = {function}(R{first_argument}..=R{last_argument})")
|
||||
}
|
||||
Operation::CallNative => {
|
||||
let CallNative {
|
||||
destination,
|
||||
function,
|
||||
argument_count,
|
||||
} = CallNative::from(self);
|
||||
let first_argument = destination.saturating_sub(argument_count);
|
||||
let last_argument = destination - 1;
|
||||
|
||||
format!("R{destination} = {function}(R{first_argument}..=R{last_argument})")
|
||||
}
|
||||
Operation::Return => {
|
||||
let Return {
|
||||
should_return_value,
|
||||
} = Return::from(self);
|
||||
|
||||
if should_return_value {
|
||||
"RETURN".to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Instruction> for u64 {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
instruction.0
|
||||
}
|
||||
}
|
32
dust-lang/src/instruction/modulo.rs
Normal file
32
dust-lang/src/instruction/modulo.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Modulo {
|
||||
pub destination: u16,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Modulo {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Modulo {
|
||||
destination: instruction.a(),
|
||||
left,
|
||||
right,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Modulo> for Instruction {
|
||||
fn from(modulo: Modulo) -> Self {
|
||||
*Instruction::new(Operation::Modulo)
|
||||
.set_a(modulo.destination)
|
||||
.set_b(modulo.left.index())
|
||||
.set_b_is_constant(modulo.left.is_constant())
|
||||
.set_b_is_local(modulo.left.is_local())
|
||||
.set_c(modulo.right.index())
|
||||
.set_c_is_constant(modulo.right.is_constant())
|
||||
.set_c_is_local(modulo.right.is_local())
|
||||
}
|
||||
}
|
23
dust-lang/src/instruction/move.rs
Normal file
23
dust-lang/src/instruction/move.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct Move {
|
||||
pub from: u16,
|
||||
pub to: u16,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Move {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
Move {
|
||||
from: instruction.b(),
|
||||
to: instruction.a(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Move> for Instruction {
|
||||
fn from(r#move: Move) -> Self {
|
||||
*Instruction::new(Operation::Move)
|
||||
.set_b(r#move.from)
|
||||
.set_c(r#move.to)
|
||||
}
|
||||
}
|
32
dust-lang/src/instruction/multiply.rs
Normal file
32
dust-lang/src/instruction/multiply.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Multiply {
|
||||
pub destination: u16,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Multiply {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Multiply {
|
||||
destination: instruction.a(),
|
||||
left,
|
||||
right,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Multiply> for Instruction {
|
||||
fn from(multiply: Multiply) -> Self {
|
||||
*Instruction::new(Operation::Multiply)
|
||||
.set_a(multiply.destination)
|
||||
.set_b(multiply.left.index())
|
||||
.set_b_is_constant(multiply.left.is_constant())
|
||||
.set_b_is_local(multiply.left.is_local())
|
||||
.set_c(multiply.right.index())
|
||||
.set_c_is_constant(multiply.right.is_constant())
|
||||
.set_c_is_local(multiply.right.is_local())
|
||||
}
|
||||
}
|
25
dust-lang/src/instruction/negate.rs
Normal file
25
dust-lang/src/instruction/negate.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Negate {
|
||||
pub destination: u16,
|
||||
pub argument: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Negate {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
Negate {
|
||||
destination: instruction.a(),
|
||||
argument: instruction.b_as_argument(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Negate> for Instruction {
|
||||
fn from(negate: Negate) -> Self {
|
||||
*Instruction::new(Operation::Negate)
|
||||
.set_a(negate.destination)
|
||||
.set_b(negate.argument.index())
|
||||
.set_b_is_constant(negate.argument.is_constant())
|
||||
.set_b_is_local(negate.argument.is_local())
|
||||
}
|
||||
}
|
25
dust-lang/src/instruction/not.rs
Normal file
25
dust-lang/src/instruction/not.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Not {
|
||||
pub destination: u16,
|
||||
pub argument: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Not {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
Not {
|
||||
destination: instruction.a(),
|
||||
argument: instruction.b_as_argument(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Not> for Instruction {
|
||||
fn from(not: Not) -> Self {
|
||||
*Instruction::new(Operation::Not)
|
||||
.set_a(not.destination)
|
||||
.set_b(not.argument.index())
|
||||
.set_b_is_constant(not.argument.is_constant())
|
||||
.set_b_is_local(not.argument.is_local())
|
||||
}
|
||||
}
|
19
dust-lang/src/instruction/return.rs
Normal file
19
dust-lang/src/instruction/return.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct Return {
|
||||
pub should_return_value: bool,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Return {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
Return {
|
||||
should_return_value: instruction.b_as_boolean(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Return> for Instruction {
|
||||
fn from(r#return: Return) -> Self {
|
||||
*Instruction::new(Operation::Return).set_b_to_boolean(r#return.should_return_value)
|
||||
}
|
||||
}
|
23
dust-lang/src/instruction/set_local.rs
Normal file
23
dust-lang/src/instruction/set_local.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use crate::{Instruction, Operation};
|
||||
|
||||
pub struct SetLocal {
|
||||
pub register: u16,
|
||||
pub local_index: u16,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for SetLocal {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
SetLocal {
|
||||
register: instruction.a(),
|
||||
local_index: instruction.b(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SetLocal> for Instruction {
|
||||
fn from(set_local: SetLocal) -> Self {
|
||||
*Instruction::new(Operation::SetLocal)
|
||||
.set_a(set_local.register)
|
||||
.set_b(set_local.local_index)
|
||||
}
|
||||
}
|
32
dust-lang/src/instruction/subtract.rs
Normal file
32
dust-lang/src/instruction/subtract.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use crate::{Argument, Instruction};
|
||||
|
||||
pub struct Subtract {
|
||||
pub destination: u16,
|
||||
pub left: Argument,
|
||||
pub right: Argument,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Subtract {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
let (left, right) = instruction.b_and_c_as_arguments();
|
||||
|
||||
Subtract {
|
||||
destination: instruction.a(),
|
||||
left,
|
||||
right,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Subtract> for Instruction {
|
||||
fn from(subtract: Subtract) -> Self {
|
||||
*Instruction::new(crate::Operation::Subtract)
|
||||
.set_a(subtract.destination)
|
||||
.set_b(subtract.left.index())
|
||||
.set_b_is_constant(subtract.left.is_constant())
|
||||
.set_b_is_local(subtract.left.is_local())
|
||||
.set_c(subtract.right.index())
|
||||
.set_c_is_constant(subtract.right.is_constant())
|
||||
.set_c_is_local(subtract.right.is_local())
|
||||
}
|
||||
}
|
25
dust-lang/src/instruction/test.rs
Normal file
25
dust-lang/src/instruction/test.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct Test {
|
||||
pub argument: Argument,
|
||||
pub value: bool,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for Test {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
Test {
|
||||
argument: instruction.b_as_argument(),
|
||||
value: instruction.c_as_boolean(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Test> for Instruction {
|
||||
fn from(test: Test) -> Self {
|
||||
*Instruction::new(Operation::Test)
|
||||
.set_b(test.argument.index())
|
||||
.set_b_is_constant(test.argument.is_constant())
|
||||
.set_b_is_local(test.argument.is_local())
|
||||
.set_c_to_boolean(test.value)
|
||||
}
|
||||
}
|
28
dust-lang/src/instruction/test_set.rs
Normal file
28
dust-lang/src/instruction/test_set.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use crate::{Argument, Instruction, Operation};
|
||||
|
||||
pub struct TestSet {
|
||||
pub destination: u16,
|
||||
pub argument: Argument,
|
||||
pub value: bool,
|
||||
}
|
||||
|
||||
impl From<&Instruction> for TestSet {
|
||||
fn from(instruction: &Instruction) -> Self {
|
||||
TestSet {
|
||||
destination: instruction.a(),
|
||||
argument: instruction.b_as_argument(),
|
||||
value: instruction.c_as_boolean(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TestSet> for Instruction {
|
||||
fn from(test_set: TestSet) -> Self {
|
||||
*Instruction::new(Operation::TestSet)
|
||||
.set_a(test_set.destination)
|
||||
.set_b(test_set.argument.index())
|
||||
.set_b_is_constant(test_set.argument.is_constant())
|
||||
.set_b_is_local(test_set.argument.is_local())
|
||||
.set_c_to_boolean(test_set.value)
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ pub use crate::compiler::{compile, CompileError, Compiler};
|
||||
pub use crate::disassembler::Disassembler;
|
||||
pub use crate::dust_error::{AnnotatedError, DustError};
|
||||
pub use crate::formatter::{format, Formatter};
|
||||
pub use crate::instruction::{Instruction, InstructionBuilder};
|
||||
pub use crate::instruction::{Argument, Instruction};
|
||||
pub use crate::lexer::{lex, LexError, Lexer};
|
||||
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
||||
pub use crate::operation::Operation;
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Tool used by the compiler to optimize a chunk's bytecode.
|
||||
|
||||
use crate::{instruction::InstructionBuilder, Chunk, Instruction, Operation, Span};
|
||||
use crate::{Chunk, Instruction, Operation, Span};
|
||||
|
||||
/// An instruction optimizer that mutably borrows instructions from a chunk.
|
||||
#[derive(Debug)]
|
||||
@ -58,11 +58,7 @@ impl<'a> Optimizer<'a> {
|
||||
|
||||
let first_loader_register = first_loader.a();
|
||||
let second_loader = &mut instructions.last_mut().unwrap().0;
|
||||
let second_loader_new = Instruction::builder(second_loader.operation())
|
||||
.a(first_loader_register)
|
||||
.b(second_loader.b())
|
||||
.c(second_loader.c())
|
||||
.build();
|
||||
let second_loader_new = *second_loader.clone().set_a(first_loader_register);
|
||||
|
||||
*second_loader = second_loader_new;
|
||||
|
||||
@ -90,9 +86,7 @@ impl<'a> Optimizer<'a> {
|
||||
let set_local = instructions.pop().unwrap().0;
|
||||
let set_local_register = set_local.a();
|
||||
let math_instruction = instructions.last_mut().unwrap().0;
|
||||
let math_instruction_new = InstructionBuilder::from(&math_instruction)
|
||||
.a(set_local_register)
|
||||
.build();
|
||||
let math_instruction_new = *math_instruction.clone().set_a(set_local_register);
|
||||
|
||||
instructions.last_mut().unwrap().0 = math_instruction_new;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user