Refactor to use 64-bit instructions
This commit is contained in:
parent
fbaf59abe2
commit
c5e0d8ef1e
@ -87,7 +87,7 @@ impl Chunk {
|
|||||||
&mut self.constants
|
&mut self.constants
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_constant(&self, index: u8) -> Result<&ConcreteValue, ChunkError> {
|
pub fn get_constant(&self, index: u16) -> Result<&ConcreteValue, ChunkError> {
|
||||||
self.constants
|
self.constants
|
||||||
.get(index as usize)
|
.get(index as usize)
|
||||||
.ok_or(ChunkError::ConstantIndexOutOfBounds {
|
.ok_or(ChunkError::ConstantIndexOutOfBounds {
|
||||||
@ -95,15 +95,15 @@ impl Chunk {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_or_get_constant(&mut self, value: ConcreteValue) -> u8 {
|
pub fn push_or_get_constant(&mut self, value: ConcreteValue) -> u16 {
|
||||||
if let Some(index) = self
|
if let Some(index) = self
|
||||||
.constants
|
.constants
|
||||||
.iter()
|
.iter()
|
||||||
.position(|constant| *constant == value)
|
.position(|constant| *constant == value)
|
||||||
{
|
{
|
||||||
index as u8
|
index as u16
|
||||||
} else {
|
} else {
|
||||||
let index = self.constants.len() as u8;
|
let index = self.constants.len() as u16;
|
||||||
|
|
||||||
self.constants.push(value);
|
self.constants.push(value);
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ impl Chunk {
|
|||||||
&mut self.locals
|
&mut self.locals
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local(&self, index: u8) -> Result<&Local, ChunkError> {
|
pub fn get_local(&self, index: u16) -> Result<&Local, ChunkError> {
|
||||||
self.locals
|
self.locals
|
||||||
.get(index as usize)
|
.get(index as usize)
|
||||||
.ok_or(ChunkError::LocalIndexOutOfBounds {
|
.ok_or(ChunkError::LocalIndexOutOfBounds {
|
||||||
@ -141,7 +141,7 @@ impl Chunk {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local_mut(&mut self, index: u8) -> Result<&mut Local, ChunkError> {
|
pub fn get_local_mut(&mut self, index: u16) -> Result<&mut Local, ChunkError> {
|
||||||
self.locals
|
self.locals
|
||||||
.get_mut(index as usize)
|
.get_mut(index as usize)
|
||||||
.ok_or(ChunkError::LocalIndexOutOfBounds {
|
.ok_or(ChunkError::LocalIndexOutOfBounds {
|
||||||
@ -149,7 +149,7 @@ impl Chunk {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_identifier(&self, local_index: u8) -> Option<String> {
|
pub fn get_identifier(&self, local_index: u16) -> Option<String> {
|
||||||
self.locals.get(local_index as usize).and_then(|local| {
|
self.locals.get(local_index as usize).and_then(|local| {
|
||||||
self.constants
|
self.constants
|
||||||
.get(local.identifier_index as usize)
|
.get(local.identifier_index as usize)
|
||||||
@ -157,7 +157,7 @@ impl Chunk {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_constant_type(&self, constant_index: u8) -> Result<Type, ChunkError> {
|
pub fn get_constant_type(&self, constant_index: u16) -> Result<Type, ChunkError> {
|
||||||
self.constants
|
self.constants
|
||||||
.get(constant_index as usize)
|
.get(constant_index as usize)
|
||||||
.map(|value| value.r#type())
|
.map(|value| value.r#type())
|
||||||
@ -166,7 +166,7 @@ impl Chunk {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local_type(&self, local_index: u8) -> Result<&Type, ChunkError> {
|
pub fn get_local_type(&self, local_index: u16) -> Result<&Type, ChunkError> {
|
||||||
self.locals
|
self.locals
|
||||||
.get(local_index as usize)
|
.get(local_index as usize)
|
||||||
.map(|local| &local.r#type)
|
.map(|local| &local.r#type)
|
||||||
@ -214,7 +214,7 @@ impl PartialEq for Chunk {
|
|||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct Local {
|
pub struct Local {
|
||||||
/// The index of the identifier in the constants table.
|
/// The index of the identifier in the constants table.
|
||||||
pub identifier_index: u8,
|
pub identifier_index: u16,
|
||||||
|
|
||||||
/// The expected type of the local's value.
|
/// The expected type of the local's value.
|
||||||
pub r#type: Type,
|
pub r#type: Type,
|
||||||
@ -228,7 +228,7 @@ pub struct Local {
|
|||||||
|
|
||||||
impl Local {
|
impl Local {
|
||||||
/// Creates a new Local instance.
|
/// Creates a new Local instance.
|
||||||
pub fn new(identifier_index: u8, r#type: Type, mutable: bool, scope: Scope) -> Self {
|
pub fn new(identifier_index: u16, r#type: Type, mutable: bool, scope: Scope) -> Self {
|
||||||
Self {
|
Self {
|
||||||
identifier_index,
|
identifier_index,
|
||||||
r#type,
|
r#type,
|
||||||
|
@ -48,7 +48,6 @@ pub fn compile(source: &str) -> Result<Chunk, DustError> {
|
|||||||
pub struct Compiler<'src> {
|
pub struct Compiler<'src> {
|
||||||
chunk: Chunk,
|
chunk: Chunk,
|
||||||
lexer: Lexer<'src>,
|
lexer: Lexer<'src>,
|
||||||
local_declarations: Vec<u8>,
|
|
||||||
|
|
||||||
current_token: Token<'src>,
|
current_token: Token<'src>,
|
||||||
current_position: Span,
|
current_position: Span,
|
||||||
@ -58,7 +57,7 @@ pub struct Compiler<'src> {
|
|||||||
return_type: Option<Type>,
|
return_type: Option<Type>,
|
||||||
optimization_count: usize,
|
optimization_count: usize,
|
||||||
previous_expression_type: Type,
|
previous_expression_type: Type,
|
||||||
minimum_register: u8,
|
minimum_register: u16,
|
||||||
|
|
||||||
block_index: u8,
|
block_index: u8,
|
||||||
current_scope: Scope,
|
current_scope: Scope,
|
||||||
@ -78,7 +77,6 @@ impl<'src> Compiler<'src> {
|
|||||||
Ok(Compiler {
|
Ok(Compiler {
|
||||||
chunk,
|
chunk,
|
||||||
lexer,
|
lexer,
|
||||||
local_declarations: Vec::new(),
|
|
||||||
current_token,
|
current_token,
|
||||||
current_position,
|
current_position,
|
||||||
previous_token: Token::Eof,
|
previous_token: Token::Eof,
|
||||||
@ -112,7 +110,7 @@ impl<'src> Compiler<'src> {
|
|||||||
matches!(self.current_token, Token::Eof)
|
matches!(self.current_token, Token::Eof)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_register(&self) -> u8 {
|
fn next_register(&self) -> u16 {
|
||||||
self.chunk
|
self.chunk
|
||||||
.instructions()
|
.instructions()
|
||||||
.iter()
|
.iter()
|
||||||
@ -146,7 +144,7 @@ impl<'src> Compiler<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_local(&self, index: u8) -> Result<&Local, CompileError> {
|
fn get_local(&self, index: u16) -> Result<&Local, CompileError> {
|
||||||
self.chunk
|
self.chunk
|
||||||
.get_local(index)
|
.get_local(index)
|
||||||
.map_err(|error| CompileError::Chunk {
|
.map_err(|error| CompileError::Chunk {
|
||||||
@ -155,7 +153,7 @@ impl<'src> Compiler<'src> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_local_index(&self, identifier_text: &str) -> Result<u8, CompileError> {
|
fn get_local_index(&self, identifier_text: &str) -> Result<u16, CompileError> {
|
||||||
self.chunk
|
self.chunk
|
||||||
.locals()
|
.locals()
|
||||||
.iter()
|
.iter()
|
||||||
@ -173,7 +171,7 @@ impl<'src> Compiler<'src> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if identifier == identifier_text {
|
if identifier == identifier_text {
|
||||||
Some(index as u8)
|
Some(index as u16)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -190,8 +188,7 @@ impl<'src> Compiler<'src> {
|
|||||||
r#type: Type,
|
r#type: Type,
|
||||||
is_mutable: bool,
|
is_mutable: bool,
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
register_index: u8,
|
) -> (u16, u16) {
|
||||||
) -> (u8, u8) {
|
|
||||||
log::info!("Declaring local {identifier}");
|
log::info!("Declaring local {identifier}");
|
||||||
|
|
||||||
let identifier = ConcreteValue::string(identifier);
|
let identifier = ConcreteValue::string(identifier);
|
||||||
@ -201,34 +198,8 @@ impl<'src> Compiler<'src> {
|
|||||||
self.chunk
|
self.chunk
|
||||||
.locals_mut()
|
.locals_mut()
|
||||||
.push(Local::new(identifier_index, r#type, is_mutable, scope));
|
.push(Local::new(identifier_index, r#type, is_mutable, scope));
|
||||||
self.local_declarations.insert(local_index, register_index);
|
|
||||||
|
|
||||||
(local_index as u8, identifier_index)
|
(local_index as u16, identifier_index)
|
||||||
}
|
|
||||||
|
|
||||||
fn redeclare_local(&mut self, local_index: u8, register_index: u8) -> Result<(), CompileError> {
|
|
||||||
let local_index = local_index as usize;
|
|
||||||
|
|
||||||
if !self.local_declarations.len() <= local_index {
|
|
||||||
let local = self.get_local(local_index as u8)?;
|
|
||||||
let identifier = self
|
|
||||||
.chunk
|
|
||||||
.constants()
|
|
||||||
.get(local.identifier_index as usize)
|
|
||||||
.map(|constant| constant.to_string())
|
|
||||||
.unwrap_or("unknown".to_string());
|
|
||||||
|
|
||||||
return Err(CompileError::VariableOutOfScope {
|
|
||||||
identifier,
|
|
||||||
variable_scope: local.scope,
|
|
||||||
access_scope: self.current_scope,
|
|
||||||
position: self.current_position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.local_declarations[local_index] = register_index;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn allow(&mut self, allowed: Token) -> Result<bool, CompileError> {
|
fn allow(&mut self, allowed: Token) -> Result<bool, CompileError> {
|
||||||
@ -374,7 +345,7 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_register_type(&self, register_index: u8) -> Result<Type, CompileError> {
|
pub fn get_register_type(&self, register_index: u16) -> Result<Type, CompileError> {
|
||||||
for (index, (instruction, _)) in self.chunk.instructions().iter().enumerate() {
|
for (index, (instruction, _)) in self.chunk.instructions().iter().enumerate() {
|
||||||
if instruction.a() == register_index {
|
if instruction.a() == register_index {
|
||||||
if let Operation::LoadList = instruction.operation() {
|
if let Operation::LoadList = instruction.operation() {
|
||||||
@ -459,11 +430,13 @@ impl<'src> Compiler<'src> {
|
|||||||
) -> Result<(), CompileError> {
|
) -> Result<(), CompileError> {
|
||||||
let constant_index = self.chunk.push_or_get_constant(constant);
|
let constant_index = self.chunk.push_or_get_constant(constant);
|
||||||
let register = self.next_register();
|
let register = self.next_register();
|
||||||
|
let instruction = Instruction::builder(Operation::LoadConstant)
|
||||||
|
.a(register)
|
||||||
|
.b(constant_index)
|
||||||
|
.c_to_boolean(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(instruction, position);
|
||||||
Instruction::load_constant(register, constant_index, false),
|
|
||||||
position,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -476,11 +449,13 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
let boolean = text.parse::<bool>().unwrap();
|
let boolean = text.parse::<bool>().unwrap();
|
||||||
let register = self.next_register();
|
let register = self.next_register();
|
||||||
|
let instruction = Instruction::builder(Operation::LoadBoolean)
|
||||||
|
.a(register)
|
||||||
|
.b_to_boolean(boolean)
|
||||||
|
.c_to_boolean(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(instruction, position);
|
||||||
Instruction::load_boolean(register, boolean, false),
|
|
||||||
position,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.previous_expression_type = Type::Boolean;
|
self.previous_expression_type = Type::Boolean;
|
||||||
|
|
||||||
@ -656,9 +631,17 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let register = self.next_register();
|
let register = self.next_register();
|
||||||
let mut instruction = match operator.kind() {
|
let instruction = match operator.kind() {
|
||||||
TokenKind::Bang => Instruction::not(register, argument),
|
TokenKind::Bang => Instruction::builder(Operation::Not)
|
||||||
TokenKind::Minus => Instruction::negate(register, argument),
|
.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(),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(CompileError::ExpectedTokenMultiple {
|
return Err(CompileError::ExpectedTokenMultiple {
|
||||||
expected: &[TokenKind::Bang, TokenKind::Minus],
|
expected: &[TokenKind::Bang, TokenKind::Minus],
|
||||||
@ -668,10 +651,6 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_constant {
|
|
||||||
instruction.set_b_is_constant();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.emit_instruction(instruction, operator_position);
|
self.emit_instruction(instruction, operator_position);
|
||||||
|
|
||||||
if let TokenKind::Bang = operator.kind() {
|
if let TokenKind::Bang = operator.kind() {
|
||||||
@ -684,32 +663,19 @@ impl<'src> Compiler<'src> {
|
|||||||
fn handle_binary_argument(
|
fn handle_binary_argument(
|
||||||
&mut self,
|
&mut self,
|
||||||
instruction: &Instruction,
|
instruction: &Instruction,
|
||||||
) -> Result<(bool, bool, bool, u8), CompileError> {
|
) -> Result<(bool, bool, bool, bool, u16), CompileError> {
|
||||||
let mut push_back = false;
|
let mut push_back = false;
|
||||||
let mut is_constant = false;
|
let mut is_constant = false;
|
||||||
|
let mut is_local = false;
|
||||||
let mut is_mutable_local = false;
|
let mut is_mutable_local = false;
|
||||||
let argument = match instruction.operation() {
|
let argument = match instruction.operation() {
|
||||||
Operation::GetLocal => {
|
Operation::GetLocal => {
|
||||||
let local_index = instruction.b();
|
let local_index = instruction.b();
|
||||||
let local = self.get_local(local_index)?;
|
let local = self.get_local(local_index)?;
|
||||||
|
is_local = true;
|
||||||
is_mutable_local = local.is_mutable;
|
is_mutable_local = local.is_mutable;
|
||||||
|
|
||||||
*self
|
local_index
|
||||||
.local_declarations
|
|
||||||
.get(local_index as usize)
|
|
||||||
.ok_or_else(|| {
|
|
||||||
let identifier = self
|
|
||||||
.chunk
|
|
||||||
.constants()
|
|
||||||
.get(local.identifier_index as usize)
|
|
||||||
.unwrap()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
CompileError::UndeclaredVariable {
|
|
||||||
identifier,
|
|
||||||
position: self.current_position,
|
|
||||||
}
|
|
||||||
})?
|
|
||||||
}
|
}
|
||||||
Operation::LoadConstant => {
|
Operation::LoadConstant => {
|
||||||
is_constant = true;
|
is_constant = true;
|
||||||
@ -733,7 +699,7 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((push_back, is_constant, is_mutable_local, argument))
|
Ok((push_back, is_constant, is_local, is_mutable_local, argument))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_math_binary(&mut self) -> Result<(), CompileError> {
|
fn parse_math_binary(&mut self) -> Result<(), CompileError> {
|
||||||
@ -744,7 +710,7 @@ impl<'src> Compiler<'src> {
|
|||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
let (push_back_left, left_is_constant, left_is_mutable_local, left) =
|
let (push_back_left, left_is_constant, left_is_local, left_is_mutable_local, left) =
|
||||||
self.handle_binary_argument(&left_instruction)?;
|
self.handle_binary_argument(&left_instruction)?;
|
||||||
|
|
||||||
if push_back_left {
|
if push_back_left {
|
||||||
@ -774,7 +740,7 @@ impl<'src> Compiler<'src> {
|
|||||||
self.parse_sub_expression(&rule.precedence)?;
|
self.parse_sub_expression(&rule.precedence)?;
|
||||||
|
|
||||||
let (right_instruction, right_position) = self.pop_last_instruction()?;
|
let (right_instruction, right_position) = self.pop_last_instruction()?;
|
||||||
let (push_back_right, right_is_constant, _, right) =
|
let (push_back_right, right_is_constant, right_is_local, _, right) =
|
||||||
self.handle_binary_argument(&right_instruction)?;
|
self.handle_binary_argument(&right_instruction)?;
|
||||||
|
|
||||||
if push_back_right {
|
if push_back_right {
|
||||||
@ -786,17 +752,17 @@ impl<'src> Compiler<'src> {
|
|||||||
} else {
|
} else {
|
||||||
self.next_register()
|
self.next_register()
|
||||||
};
|
};
|
||||||
let mut new_instruction = match operator {
|
let operation = match operator {
|
||||||
Token::Plus => Instruction::add(register, left, right),
|
Token::Plus => Operation::Add,
|
||||||
Token::PlusEqual => Instruction::add(register, left, right),
|
Token::PlusEqual => Operation::Add,
|
||||||
Token::Minus => Instruction::subtract(register, left, right),
|
Token::Minus => Operation::Subtract,
|
||||||
Token::MinusEqual => Instruction::subtract(register, left, right),
|
Token::MinusEqual => Operation::Subtract,
|
||||||
Token::Star => Instruction::multiply(register, left, right),
|
Token::Star => Operation::Multiply,
|
||||||
Token::StarEqual => Instruction::multiply(register, left, right),
|
Token::StarEqual => Operation::Multiply,
|
||||||
Token::Slash => Instruction::divide(register, left, right),
|
Token::Slash => Operation::Divide,
|
||||||
Token::SlashEqual => Instruction::divide(register, left, right),
|
Token::SlashEqual => Operation::Divide,
|
||||||
Token::Percent => Instruction::modulo(register, left, right),
|
Token::Percent => Operation::Modulo,
|
||||||
Token::PercentEqual => Instruction::modulo(register, left, right),
|
Token::PercentEqual => Operation::Modulo,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(CompileError::ExpectedTokenMultiple {
|
return Err(CompileError::ExpectedTokenMultiple {
|
||||||
expected: &[
|
expected: &[
|
||||||
@ -817,15 +783,15 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if left_is_constant {
|
let instruction = Instruction::builder(operation)
|
||||||
new_instruction.set_b_is_constant();
|
.a(register)
|
||||||
}
|
.b(left)
|
||||||
|
.c(right)
|
||||||
|
.b_is_constant(left_is_constant)
|
||||||
|
.c_is_constant(right_is_constant)
|
||||||
|
.build();
|
||||||
|
|
||||||
if right_is_constant {
|
self.emit_instruction(instruction, operator_position);
|
||||||
new_instruction.set_c_is_constant();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.emit_instruction(new_instruction, operator_position);
|
|
||||||
|
|
||||||
if is_assignment {
|
if is_assignment {
|
||||||
self.previous_expression_type = Type::None;
|
self.previous_expression_type = Type::None;
|
||||||
@ -852,7 +818,7 @@ impl<'src> Compiler<'src> {
|
|||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
let (push_back_left, left_is_constant, _, left) =
|
let (push_back_left, left_is_constant, left_is_local, _, left) =
|
||||||
self.handle_binary_argument(&left_instruction)?;
|
self.handle_binary_argument(&left_instruction)?;
|
||||||
let operator = self.current_token;
|
let operator = self.current_token;
|
||||||
let operator_position = self.current_position;
|
let operator_position = self.current_position;
|
||||||
@ -868,16 +834,15 @@ impl<'src> Compiler<'src> {
|
|||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
let (push_back_right, right_is_constant, _, right) =
|
let (push_back_right, right_is_constant, right_is_local, _, right) =
|
||||||
self.handle_binary_argument(&right_instruction)?;
|
self.handle_binary_argument(&right_instruction)?;
|
||||||
|
let (operation, instruction_boolean) = match operator {
|
||||||
let mut instruction = match operator {
|
Token::DoubleEqual => (Operation::Equal, true),
|
||||||
Token::DoubleEqual => Instruction::equal(true, left, right),
|
Token::BangEqual => (Operation::Equal, false),
|
||||||
Token::BangEqual => Instruction::equal(false, left, right),
|
Token::Less => (Operation::Less, true),
|
||||||
Token::Less => Instruction::less(true, left, right),
|
Token::LessEqual => (Operation::LessEqual, true),
|
||||||
Token::LessEqual => Instruction::less_equal(true, left, right),
|
Token::Greater => (Operation::LessEqual, false),
|
||||||
Token::Greater => Instruction::less_equal(false, left, right),
|
Token::GreaterEqual => (Operation::Less, false),
|
||||||
Token::GreaterEqual => Instruction::less(false, left, right),
|
|
||||||
_ => {
|
_ => {
|
||||||
return Err(CompileError::ExpectedTokenMultiple {
|
return Err(CompileError::ExpectedTokenMultiple {
|
||||||
expected: &[
|
expected: &[
|
||||||
@ -893,14 +858,13 @@ impl<'src> Compiler<'src> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let instruction = Instruction::builder(operation)
|
||||||
if left_is_constant {
|
.a_to_boolean(instruction_boolean)
|
||||||
instruction.set_b_is_constant();
|
.b(left)
|
||||||
}
|
.c(right)
|
||||||
|
.b_is_constant(left_is_constant)
|
||||||
if right_is_constant {
|
.c_is_constant(right_is_constant)
|
||||||
instruction.set_c_is_constant();
|
.build();
|
||||||
}
|
|
||||||
|
|
||||||
if push_back_left {
|
if push_back_left {
|
||||||
self.emit_instruction(left_instruction, left_position);
|
self.emit_instruction(left_instruction, left_position);
|
||||||
@ -911,17 +875,25 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let register = self.next_register();
|
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();
|
||||||
|
|
||||||
self.emit_instruction(instruction, operator_position);
|
self.emit_instruction(instruction, operator_position);
|
||||||
self.emit_instruction(Instruction::jump(1, true), operator_position);
|
self.emit_instruction(jump, operator_position);
|
||||||
self.emit_instruction(
|
self.emit_instruction(load_true, operator_position);
|
||||||
Instruction::load_boolean(register, true, true),
|
self.emit_instruction(load_false, operator_position);
|
||||||
operator_position,
|
|
||||||
);
|
|
||||||
self.emit_instruction(
|
|
||||||
Instruction::load_boolean(register, false, false),
|
|
||||||
operator_position,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.previous_expression_type = Type::Boolean;
|
self.previous_expression_type = Type::Boolean;
|
||||||
|
|
||||||
@ -938,14 +910,14 @@ impl<'src> Compiler<'src> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_, is_constant, _, _) = self.handle_binary_argument(&left_instruction)?;
|
let (_, is_constant, is_local, _, _) = self.handle_binary_argument(&left_instruction)?;
|
||||||
let operator = self.current_token;
|
let operator = self.current_token;
|
||||||
let operator_position = self.current_position;
|
let operator_position = self.current_position;
|
||||||
let rule = ParseRule::from(&operator);
|
let rule = ParseRule::from(&operator);
|
||||||
let test_register = left_instruction.a();
|
let test_register = left_instruction.a();
|
||||||
let mut test_instruction = match operator {
|
let test_boolean = match operator {
|
||||||
Token::DoubleAmpersand => Instruction::test(test_register, true),
|
Token::DoubleAmpersand => true,
|
||||||
Token::DoublePipe => Instruction::test(test_register, false),
|
Token::DoublePipe => false,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(CompileError::ExpectedTokenMultiple {
|
return Err(CompileError::ExpectedTokenMultiple {
|
||||||
expected: &[TokenKind::DoubleAmpersand, TokenKind::DoublePipe],
|
expected: &[TokenKind::DoubleAmpersand, TokenKind::DoublePipe],
|
||||||
@ -954,15 +926,19 @@ impl<'src> Compiler<'src> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let test = Instruction::builder(Operation::Test)
|
||||||
if is_constant {
|
.b(test_register)
|
||||||
test_instruction.set_b_is_constant();
|
.c_to_boolean(test_boolean)
|
||||||
}
|
.build();
|
||||||
|
let jump = Instruction::builder(Operation::Jump)
|
||||||
|
.a(1)
|
||||||
|
.b_to_boolean(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.emit_instruction(left_instruction, left_position);
|
self.emit_instruction(left_instruction, left_position);
|
||||||
self.emit_instruction(test_instruction, operator_position);
|
self.emit_instruction(test, operator_position);
|
||||||
self.emit_instruction(Instruction::jump(1, true), operator_position);
|
self.emit_instruction(jump, operator_position);
|
||||||
self.parse_sub_expression(&rule.precedence)?;
|
self.parse_sub_expression(&rule.precedence)?;
|
||||||
|
|
||||||
self.previous_expression_type = Type::Boolean;
|
self.previous_expression_type = Type::Boolean;
|
||||||
@ -989,8 +965,11 @@ impl<'src> Compiler<'src> {
|
|||||||
return self.parse_native_call(native_function);
|
return self.parse_native_call(native_function);
|
||||||
} else if Some(identifier) == self.chunk.name().map(|string| string.as_str()) {
|
} else if Some(identifier) == self.chunk.name().map(|string| string.as_str()) {
|
||||||
let register = self.next_register();
|
let register = self.next_register();
|
||||||
|
let load_self = Instruction::builder(Operation::LoadSelf)
|
||||||
|
.a(register)
|
||||||
|
.build();
|
||||||
|
|
||||||
self.emit_instruction(Instruction::load_self(register), start_position);
|
self.emit_instruction(load_self, start_position);
|
||||||
|
|
||||||
self.previous_expression_type = Type::SelfChunk;
|
self.previous_expression_type = Type::SelfChunk;
|
||||||
|
|
||||||
@ -1024,13 +1003,13 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
let register = self.local_declarations[local_index as usize];
|
let register = self.next_register();
|
||||||
|
let set_local = Instruction::builder(Operation::SetLocal)
|
||||||
|
.a(register)
|
||||||
|
.b(local_index)
|
||||||
|
.build();
|
||||||
|
|
||||||
self.redeclare_local(local_index, register)?;
|
self.emit_instruction(set_local, start_position);
|
||||||
self.emit_instruction(
|
|
||||||
Instruction::set_local(register, local_index),
|
|
||||||
start_position,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.previous_expression_type = Type::None;
|
self.previous_expression_type = Type::None;
|
||||||
|
|
||||||
@ -1045,11 +1024,12 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let register = self.next_register();
|
let register = self.next_register();
|
||||||
|
let get_local = Instruction::builder(Operation::GetLocal)
|
||||||
|
.a(register)
|
||||||
|
.b(local_index)
|
||||||
|
.build();
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(get_local, self.previous_position);
|
||||||
Instruction::get_local(register, local_index),
|
|
||||||
self.previous_position,
|
|
||||||
);
|
|
||||||
|
|
||||||
let local = self.get_local(local_index)?;
|
let local = self.get_local(local_index)?;
|
||||||
|
|
||||||
@ -1118,12 +1098,13 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
item_type = self.previous_expression_type.clone();
|
item_type = self.previous_expression_type.clone();
|
||||||
let actual_register = self.next_register() - 1;
|
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 {
|
if expected_register < actual_register {
|
||||||
self.emit_instruction(
|
self.emit_instruction(close, self.current_position);
|
||||||
Instruction::close(expected_register, actual_register),
|
|
||||||
self.current_position,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.allow(Token::Comma)?;
|
self.allow(Token::Comma)?;
|
||||||
@ -1131,11 +1112,12 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
let to_register = self.next_register();
|
let to_register = self.next_register();
|
||||||
let end = self.current_position.1;
|
let end = self.current_position.1;
|
||||||
|
let load_list = Instruction::builder(Operation::LoadList)
|
||||||
|
.a(to_register)
|
||||||
|
.b(start_register)
|
||||||
|
.build();
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(load_list, Span(start, end));
|
||||||
Instruction::load_list(to_register, start_register),
|
|
||||||
Span(start, end),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.previous_expression_type = Type::List {
|
self.previous_expression_type = Type::List {
|
||||||
item_type: Box::new(item_type),
|
item_type: Box::new(item_type),
|
||||||
@ -1163,11 +1145,12 @@ impl<'src> Compiler<'src> {
|
|||||||
self.chunk.instructions_mut().pop();
|
self.chunk.instructions_mut().pop();
|
||||||
} else if let Some((instruction, _)) = self.chunk.instructions().last() {
|
} else if let Some((instruction, _)) = self.chunk.instructions().last() {
|
||||||
let test_register = instruction.a();
|
let test_register = instruction.a();
|
||||||
|
let test = Instruction::builder(Operation::Test)
|
||||||
|
.b(test_register)
|
||||||
|
.c_to_boolean(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(test, self.current_position)
|
||||||
Instruction::test(test_register, true),
|
|
||||||
self.current_position,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let if_block_start = self.chunk.len();
|
let if_block_start = self.chunk.len();
|
||||||
@ -1184,7 +1167,7 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let if_block_end = self.chunk.len();
|
let if_block_end = self.chunk.len();
|
||||||
let mut if_block_distance = (if_block_end - if_block_start) as u8;
|
let mut if_block_distance = (if_block_end - if_block_start) as u16;
|
||||||
let if_block_type = self.previous_expression_type.clone();
|
let if_block_type = self.previous_expression_type.clone();
|
||||||
let if_last_register = self.next_register().saturating_sub(1);
|
let if_last_register = self.next_register().saturating_sub(1);
|
||||||
|
|
||||||
@ -1211,7 +1194,7 @@ impl<'src> Compiler<'src> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let else_block_end = self.chunk.len();
|
let else_block_end = self.chunk.len();
|
||||||
let else_block_distance = (else_block_end - if_block_end) as u8;
|
let else_block_distance = (else_block_end - if_block_end) as u16;
|
||||||
|
|
||||||
if let Err(conflict) = if_block_type.check(&self.previous_expression_type) {
|
if let Err(conflict) = if_block_type.check(&self.previous_expression_type) {
|
||||||
return Err(CompileError::IfElseBranchMismatch {
|
return Err(CompileError::IfElseBranchMismatch {
|
||||||
@ -1229,36 +1212,37 @@ impl<'src> Compiler<'src> {
|
|||||||
skippable.set_c_to_boolean(true);
|
skippable.set_c_to_boolean(true);
|
||||||
} else {
|
} else {
|
||||||
if_block_distance += 1;
|
if_block_distance += 1;
|
||||||
|
let jump = Instruction::builder(Operation::Jump)
|
||||||
|
.b(else_block_distance)
|
||||||
|
.c_to_boolean(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
self.chunk.instructions_mut().insert(
|
self.chunk
|
||||||
if_block_end,
|
.instructions_mut()
|
||||||
(
|
.insert(if_block_end, (jump, self.current_position));
|
||||||
Instruction::jump(else_block_distance, true),
|
|
||||||
self.current_position,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2.. => {
|
2.. => {
|
||||||
if_block_distance += 1;
|
if_block_distance += 1;
|
||||||
|
let jump = Instruction::builder(Operation::Jump)
|
||||||
|
.b(else_block_distance)
|
||||||
|
.c_to_boolean(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
self.chunk.instructions_mut().insert(
|
self.chunk
|
||||||
if_block_end,
|
.instructions_mut()
|
||||||
(
|
.insert(if_block_end, (jump, self.current_position));
|
||||||
Instruction::jump(else_block_distance, true),
|
|
||||||
self.current_position,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.chunk.instructions_mut().insert(
|
let jump = Instruction::builder(Operation::Jump)
|
||||||
if_block_start,
|
.b(if_block_distance)
|
||||||
(
|
.c_to_boolean(true)
|
||||||
Instruction::jump(if_block_distance, true),
|
.build();
|
||||||
if_block_start_position,
|
|
||||||
),
|
self.chunk
|
||||||
);
|
.instructions_mut()
|
||||||
|
.insert(if_block_start, (jump, if_block_start_position));
|
||||||
|
|
||||||
if self.chunk.len() >= 4 {
|
if self.chunk.len() >= 4 {
|
||||||
let mut optimizer = Optimizer::new(&mut self.chunk);
|
let mut optimizer = Optimizer::new(&mut self.chunk);
|
||||||
@ -1269,12 +1253,13 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let else_last_register = self.next_register().saturating_sub(1);
|
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();
|
||||||
|
|
||||||
if if_last_register < else_last_register {
|
if if_last_register < else_last_register {
|
||||||
self.emit_instruction(
|
self.emit_instruction(r#move, self.current_position);
|
||||||
Instruction::r#move(else_last_register, if_last_register),
|
|
||||||
self.current_position,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1283,7 +1268,7 @@ impl<'src> Compiler<'src> {
|
|||||||
fn parse_while(&mut self) -> Result<(), CompileError> {
|
fn parse_while(&mut self) -> Result<(), CompileError> {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let expression_start = self.chunk.len() as u8;
|
let expression_start = self.chunk.len() as u16;
|
||||||
|
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
@ -1305,18 +1290,22 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
self.parse_block()?;
|
self.parse_block()?;
|
||||||
|
|
||||||
let block_end = self.chunk.len() as u8;
|
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();
|
||||||
|
|
||||||
self.chunk.instructions_mut().insert(
|
self.chunk
|
||||||
block_start,
|
.instructions_mut()
|
||||||
(
|
.insert(block_start, (jump, self.current_position));
|
||||||
Instruction::jump(block_end - block_start as u8 + 1, true),
|
|
||||||
self.current_position,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
let jump_back_distance = block_end - expression_start + 1;
|
let jump_back_distance = block_end - expression_start + 1;
|
||||||
let jump_back = Instruction::jump(jump_back_distance, false);
|
let jump_back = Instruction::builder(Operation::Jump)
|
||||||
|
.b(jump_back_distance)
|
||||||
|
.c_to_boolean(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
self.emit_instruction(jump_back, self.current_position);
|
self.emit_instruction(jump_back, self.current_position);
|
||||||
|
|
||||||
@ -1339,25 +1328,29 @@ impl<'src> Compiler<'src> {
|
|||||||
let actual_register = self.next_register() - 1;
|
let actual_register = self.next_register() - 1;
|
||||||
|
|
||||||
if expected_register < actual_register {
|
if expected_register < actual_register {
|
||||||
self.emit_instruction(
|
let close = Instruction::builder(Operation::Close)
|
||||||
Instruction::close(expected_register, actual_register),
|
.b(expected_register)
|
||||||
self.current_position,
|
.c(actual_register)
|
||||||
);
|
.build();
|
||||||
|
|
||||||
|
self.emit_instruction(close, self.current_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.allow(Token::Comma)?;
|
self.allow(Token::Comma)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.previous_expression_type = *function.r#type().return_type;
|
||||||
let end = self.previous_position.1;
|
let end = self.previous_position.1;
|
||||||
let to_register = self.next_register();
|
let to_register = self.next_register();
|
||||||
let argument_count = to_register - start_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();
|
||||||
|
|
||||||
self.previous_expression_type = *function.r#type().return_type;
|
self.emit_instruction(call_native, Span(start, end));
|
||||||
|
|
||||||
self.emit_instruction(
|
|
||||||
Instruction::call_native(to_register, function, argument_count),
|
|
||||||
Span(start, end),
|
|
||||||
);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1409,8 +1402,11 @@ impl<'src> Compiler<'src> {
|
|||||||
true
|
true
|
||||||
};
|
};
|
||||||
let end = self.current_position.1;
|
let end = self.current_position.1;
|
||||||
|
let r#return = Instruction::builder(Operation::Return)
|
||||||
|
.b_to_boolean(has_return_value)
|
||||||
|
.build();
|
||||||
|
|
||||||
self.emit_instruction(Instruction::r#return(has_return_value), Span(start, end));
|
self.emit_instruction(r#return, Span(start, end));
|
||||||
|
|
||||||
self.previous_expression_type = Type::None;
|
self.previous_expression_type = Type::None;
|
||||||
|
|
||||||
@ -1419,12 +1415,18 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
fn parse_implicit_return(&mut self) -> Result<(), CompileError> {
|
fn parse_implicit_return(&mut self) -> Result<(), CompileError> {
|
||||||
if self.allow(Token::Semicolon)? {
|
if self.allow(Token::Semicolon)? {
|
||||||
self.emit_instruction(Instruction::r#return(false), self.current_position);
|
let r#return = Instruction::builder(Operation::Return)
|
||||||
|
.b_to_boolean(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
self.emit_instruction(r#return, self.current_position);
|
||||||
} else {
|
} else {
|
||||||
self.emit_instruction(
|
let should_return_value = self.previous_expression_type != Type::None;
|
||||||
Instruction::r#return(self.previous_expression_type != Type::None),
|
let r#return = Instruction::builder(Operation::Return)
|
||||||
self.current_position,
|
.b_to_boolean(should_return_value)
|
||||||
);
|
.build();
|
||||||
|
|
||||||
|
self.emit_instruction(r#return, self.current_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1466,12 +1468,14 @@ impl<'src> Compiler<'src> {
|
|||||||
self.get_register_type(register)?
|
self.get_register_type(register)?
|
||||||
};
|
};
|
||||||
let (local_index, _) =
|
let (local_index, _) =
|
||||||
self.declare_local(identifier, r#type, is_mutable, self.current_scope, register);
|
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();
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(define_local, position);
|
||||||
Instruction::define_local(register, local_index, is_mutable),
|
|
||||||
position,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.previous_expression_type = Type::None;
|
self.previous_expression_type = Type::None;
|
||||||
|
|
||||||
@ -1494,7 +1498,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
function_compiler.expect(Token::LeftParenthesis)?;
|
function_compiler.expect(Token::LeftParenthesis)?;
|
||||||
|
|
||||||
let mut value_parameters: Option<Vec<(u8, Type)>> = None;
|
let mut value_parameters: Option<Vec<(u16, Type)>> = None;
|
||||||
|
|
||||||
while function_compiler.current_token != Token::RightParenthesis {
|
while function_compiler.current_token != Token::RightParenthesis {
|
||||||
let is_mutable = function_compiler.allow(Token::Mut)?;
|
let is_mutable = function_compiler.allow(Token::Mut)?;
|
||||||
@ -1519,13 +1523,11 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
function_compiler.advance()?;
|
function_compiler.advance()?;
|
||||||
|
|
||||||
let register = function_compiler.next_register();
|
|
||||||
let (_, identifier_index) = function_compiler.declare_local(
|
let (_, identifier_index) = function_compiler.declare_local(
|
||||||
parameter,
|
parameter,
|
||||||
r#type.clone(),
|
r#type.clone(),
|
||||||
is_mutable,
|
is_mutable,
|
||||||
function_compiler.current_scope,
|
function_compiler.current_scope,
|
||||||
register,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(value_parameters) = value_parameters.as_mut() {
|
if let Some(value_parameters) = value_parameters.as_mut() {
|
||||||
@ -1582,24 +1584,30 @@ impl<'src> Compiler<'src> {
|
|||||||
Type::Function(function_type),
|
Type::Function(function_type),
|
||||||
false,
|
false,
|
||||||
self.current_scope,
|
self.current_scope,
|
||||||
register,
|
|
||||||
);
|
);
|
||||||
|
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();
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(load_constant, Span(function_start, function_end));
|
||||||
Instruction::load_constant(register, constant_index, false),
|
self.emit_instruction(define_local, position);
|
||||||
Span(function_start, function_end),
|
|
||||||
);
|
|
||||||
self.emit_instruction(
|
|
||||||
Instruction::define_local(register, local_index, false),
|
|
||||||
position,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.previous_expression_type = Type::None;
|
self.previous_expression_type = Type::None;
|
||||||
} else {
|
} else {
|
||||||
self.emit_instruction(
|
let load_constant = Instruction::builder(Operation::LoadConstant)
|
||||||
Instruction::load_constant(register, constant_index, false),
|
.a(register)
|
||||||
Span(function_start, function_end),
|
.b(constant_index)
|
||||||
);
|
.c_to_boolean(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
self.emit_instruction(load_constant, Span(function_start, function_end));
|
||||||
|
|
||||||
self.previous_expression_type = Type::Function(function_type);
|
self.previous_expression_type = Type::Function(function_type);
|
||||||
}
|
}
|
||||||
@ -1647,12 +1655,13 @@ impl<'src> Compiler<'src> {
|
|||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
let actual_register = self.next_register() - 1;
|
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 {
|
if expected_register < actual_register {
|
||||||
self.emit_instruction(
|
self.emit_instruction(close, self.current_position);
|
||||||
Instruction::close(expected_register, actual_register),
|
|
||||||
self.current_position,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.allow(Token::Comma)?;
|
self.allow(Token::Comma)?;
|
||||||
@ -1661,11 +1670,13 @@ impl<'src> Compiler<'src> {
|
|||||||
let end = self.current_position.1;
|
let end = self.current_position.1;
|
||||||
let to_register = self.next_register();
|
let to_register = self.next_register();
|
||||||
let argument_count = to_register - function_register - 1;
|
let argument_count = to_register - function_register - 1;
|
||||||
|
let call = Instruction::builder(Operation::Call)
|
||||||
|
.a(to_register)
|
||||||
|
.b(function_register)
|
||||||
|
.c(argument_count)
|
||||||
|
.build();
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(call, Span(start, end));
|
||||||
Instruction::call(to_register, function_register, argument_count),
|
|
||||||
Span(start, end),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.previous_expression_type = function_return_type;
|
self.previous_expression_type = function_return_type;
|
||||||
|
|
||||||
|
@ -50,8 +50,8 @@ use crate::{value::ConcreteValue, Chunk, Local};
|
|||||||
const INSTRUCTION_HEADER: [&str; 4] = [
|
const INSTRUCTION_HEADER: [&str; 4] = [
|
||||||
"Instructions",
|
"Instructions",
|
||||||
"------------",
|
"------------",
|
||||||
" i POSITION BYTECODE OPERATION INFO ",
|
" i POSITION OPERATION INFO ",
|
||||||
"--- ---------- -------- ------------- ------------------------------------",
|
"--- ---------- ------------- ------------------------------------",
|
||||||
];
|
];
|
||||||
|
|
||||||
const CONSTANT_HEADER: [&str; 4] = [
|
const CONSTANT_HEADER: [&str; 4] = [
|
||||||
@ -257,12 +257,11 @@ impl<'a> Disassembler<'a> {
|
|||||||
|
|
||||||
for (index, (instruction, position)) in self.chunk.instructions().iter().enumerate() {
|
for (index, (instruction, position)) in self.chunk.instructions().iter().enumerate() {
|
||||||
let position = position.to_string();
|
let position = position.to_string();
|
||||||
let bytecode = format!("{:02X}", u32::from(instruction));
|
|
||||||
let operation = instruction.operation().to_string();
|
let operation = instruction.operation().to_string();
|
||||||
let info = instruction.disassembly_info(self.chunk);
|
let info = instruction.disassembly_info(self.chunk);
|
||||||
|
|
||||||
let instruction_display =
|
let instruction_display =
|
||||||
format!("{index:^3} {position:^10} {bytecode:>8} {operation:13} {info:^36}");
|
format!("{index:^3} {position:^10} {operation:13} {info:^36}");
|
||||||
|
|
||||||
self.push_details(&instruction_display);
|
self.push_details(&instruction_display);
|
||||||
}
|
}
|
||||||
|
@ -46,57 +46,110 @@ impl InstructionBuilder {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_a(&mut self, a: u16) -> &mut Self {
|
pub fn a(&mut self, a: u16) -> &mut Self {
|
||||||
self.a = a;
|
self.a = a;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_b(&mut self, b: u16) -> &mut 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.b = b;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_b_to_boolean(&mut self, b: bool) -> &mut Self {
|
pub fn b_to_boolean(&mut self, b: bool) -> &mut Self {
|
||||||
self.b = b as u16;
|
self.b = b as u16;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_c(&mut self, c: u16) -> &mut Self {
|
pub fn c(&mut self, c: u16) -> &mut Self {
|
||||||
self.c = c;
|
self.c = c;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_c_to_boolean(&mut self, c: bool) -> &mut Self {
|
pub fn c_to_boolean(&mut self, c: bool) -> &mut Self {
|
||||||
self.c = c as u16;
|
self.c = c as u16;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_b_is_constant(&mut self, b_is_constant: bool) -> &mut Self {
|
pub fn b_is_constant(&mut self, b_is_constant: bool) -> &mut Self {
|
||||||
self.b_is_constant = b_is_constant;
|
self.b_is_constant = b_is_constant;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_c_is_constant(&mut self, c_is_constant: bool) -> &mut Self {
|
pub fn c_is_constant(&mut self, c_is_constant: bool) -> &mut Self {
|
||||||
self.c_is_constant = c_is_constant;
|
self.c_is_constant = c_is_constant;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_a_is_local(&mut self, a_is_local: bool) -> &mut Self {
|
pub fn a_is_local(&mut self, a_is_local: bool) -> &mut Self {
|
||||||
self.a_is_local = a_is_local;
|
self.a_is_local = a_is_local;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_b_is_local(&mut self, b_is_local: bool) -> &mut Self {
|
pub fn b_is_local(&mut self, b_is_local: bool) -> &mut Self {
|
||||||
self.b_is_local = b_is_local;
|
self.b_is_local = b_is_local;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_c_is_local(&mut self, c_is_local: bool) -> &mut Self {
|
pub fn c_is_local(&mut self, c_is_local: bool) -> &mut Self {
|
||||||
self.c_is_local = c_is_local;
|
self.c_is_local = c_is_local;
|
||||||
self
|
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.
|
/// An operation and its arguments for the Dust virtual machine.
|
||||||
///
|
///
|
||||||
/// See the [module-level documentation](index.html) for more information.
|
/// See the [module-level documentation](index.html) for more information.
|
||||||
@ -104,6 +157,10 @@ impl InstructionBuilder {
|
|||||||
pub struct Instruction(u64);
|
pub struct Instruction(u64);
|
||||||
|
|
||||||
impl Instruction {
|
impl Instruction {
|
||||||
|
pub fn new(operation: Operation) -> Instruction {
|
||||||
|
Instruction(operation as u64)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn builder(operation: Operation) -> InstructionBuilder {
|
pub fn builder(operation: Operation) -> InstructionBuilder {
|
||||||
InstructionBuilder {
|
InstructionBuilder {
|
||||||
operation,
|
operation,
|
||||||
@ -118,195 +175,267 @@ impl Instruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
pub fn r#move(to_register: u16, from_register: u16) -> Instruction {
|
||||||
Instruction::builder(Operation::Move)
|
*Instruction::new(Operation::Move)
|
||||||
.set_a(to_register)
|
.set_b(to_register)
|
||||||
.set_b(from_register)
|
.set_c(from_register)
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(from_register: u16, to_register: u16) -> Instruction {
|
pub fn close(from_register: u16, to_register: u16) -> Instruction {
|
||||||
Instruction::builder(Operation::Close)
|
*Instruction::new(Operation::Close)
|
||||||
.set_b(from_register)
|
.set_b(from_register)
|
||||||
.set_c(to_register)
|
.set_c(to_register)
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_boolean(to_register: u16, value: bool, skip: bool) -> Instruction {
|
pub fn load_boolean(to_register: u16, value: bool, skip: bool) -> Instruction {
|
||||||
Instruction::builder(Operation::LoadBoolean)
|
*Instruction::new(Operation::LoadBoolean)
|
||||||
.set_a(to_register)
|
.set_a(to_register)
|
||||||
.set_b_to_boolean(value)
|
.set_b_to_boolean(value)
|
||||||
.set_c_to_boolean(skip)
|
.set_c_to_boolean(skip)
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_constant(to_register: u16, constant_index: u16, skip: bool) -> Instruction {
|
pub fn load_constant(to_register: u16, constant_index: u16, skip: bool) -> Instruction {
|
||||||
Instruction::builder(Operation::LoadConstant)
|
*Instruction::new(Operation::LoadConstant)
|
||||||
.set_a(to_register)
|
.set_a(to_register)
|
||||||
.set_b(constant_index)
|
.set_b(constant_index)
|
||||||
.set_c_to_boolean(skip)
|
.set_c_to_boolean(skip)
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_list(to_register: u16, start_register: u16) -> Instruction {
|
pub fn load_list(to_register: u16, start_register: u16) -> Instruction {
|
||||||
Instruction::builder(Operation::LoadList)
|
*Instruction::new(Operation::LoadList)
|
||||||
.set_a(to_register)
|
.set_a(to_register)
|
||||||
.set_b(start_register)
|
.set_b(start_register)
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_self(to_register: u16) -> Instruction {
|
pub fn load_self(to_register: u16) -> Instruction {
|
||||||
Instruction::builder(Operation::LoadSelf)
|
*Instruction::new(Operation::LoadSelf).set_a(to_register)
|
||||||
.set_a(to_register)
|
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn define_local(to_register: u16, local_index: u16, is_mutable: bool) -> Instruction {
|
pub fn define_local(to_register: u16, local_index: u16, is_mutable: bool) -> Instruction {
|
||||||
Instruction::builder(Operation::DefineLocal)
|
*Instruction::new(Operation::DefineLocal)
|
||||||
.set_a(to_register as u16)
|
.set_a(to_register)
|
||||||
.set_b(local_index as u16)
|
.set_b(local_index)
|
||||||
.set_c_to_boolean(is_mutable)
|
.set_c_to_boolean(is_mutable)
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local(to_register: u16, local_index: u16) -> Instruction {
|
pub fn get_local(to_register: u16, local_index: u16) -> Instruction {
|
||||||
Instruction::builder(Operation::GetLocal)
|
*Instruction::new(Operation::GetLocal)
|
||||||
.set_a(to_register)
|
.set_a(to_register)
|
||||||
.set_b(local_index)
|
.set_b(local_index)
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_local(from_register: u16, local_index: u16) -> Instruction {
|
pub fn set_local(from_register: u16, local_index: u16) -> Instruction {
|
||||||
Instruction::builder(Operation::SetLocal)
|
*Instruction::new(Operation::SetLocal)
|
||||||
.set_a(from_register)
|
.set_a(from_register)
|
||||||
.set_b(local_index)
|
.set_b(local_index)
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn add(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
pub fn add(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
||||||
// Instruction::builder(Operation::Add)
|
*Instruction::new(Operation::Add)
|
||||||
// .set_a(to_register)
|
.set_a(to_register)
|
||||||
// .set_b(left_index)
|
.set_b(left_index)
|
||||||
// .set_c(right_index)
|
.set_c(right_index)
|
||||||
// .build()
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn subtract(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
pub fn subtract(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
||||||
// let mut instruction = Instruction(Operation::Subtract as u32);
|
*Instruction::new(Operation::Subtract)
|
||||||
|
.set_a(to_register)
|
||||||
|
.set_b(left_index)
|
||||||
|
.set_c(right_index)
|
||||||
|
}
|
||||||
|
|
||||||
// instruction.set_a(to_register);
|
pub fn multiply(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
||||||
// instruction.set_b(left_index);
|
*Instruction::new(Operation::Multiply)
|
||||||
// instruction.set_c(right_index);
|
.set_a(to_register)
|
||||||
|
.set_b(left_index)
|
||||||
|
.set_c(right_index)
|
||||||
|
}
|
||||||
|
|
||||||
// instruction
|
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 multiply(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
pub fn modulo(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
||||||
// let mut instruction = Instruction(Operation::Multiply as u32);
|
*Instruction::new(Operation::Modulo)
|
||||||
|
.set_a(to_register)
|
||||||
|
.set_b(left_index)
|
||||||
|
.set_c(right_index)
|
||||||
|
}
|
||||||
|
|
||||||
// instruction.set_a(to_register);
|
pub fn test(test_register: u16, test_value: bool) -> Instruction {
|
||||||
// instruction.set_b(left_index);
|
*Instruction::new(Operation::Test)
|
||||||
// instruction.set_c(right_index);
|
.set_b(test_register)
|
||||||
|
.set_c_to_boolean(test_value)
|
||||||
|
}
|
||||||
|
|
||||||
// instruction
|
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 divide(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
pub fn equal(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction {
|
||||||
// let mut instruction = Instruction(Operation::Divide as u32);
|
*Instruction::new(Operation::Equal)
|
||||||
|
.set_a_to_boolean(comparison_boolean)
|
||||||
|
.set_b(left_index)
|
||||||
|
.set_c(right_index)
|
||||||
|
}
|
||||||
|
|
||||||
// instruction.set_a(to_register);
|
pub fn less(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction {
|
||||||
// instruction.set_b(left_index);
|
*Instruction::new(Operation::Less)
|
||||||
// instruction.set_c(right_index);
|
.set_a_to_boolean(comparison_boolean)
|
||||||
|
.set_b(left_index)
|
||||||
|
.set_c(right_index)
|
||||||
|
}
|
||||||
|
|
||||||
// instruction
|
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 modulo(to_register: u16, left_index: u16, right_index: u16) -> Instruction {
|
pub fn negate(to_register: u16, from_index: u16) -> Instruction {
|
||||||
// let mut instruction = Instruction(Operation::Modulo as u32);
|
*Instruction::new(Operation::Negate)
|
||||||
|
.set_a(to_register)
|
||||||
|
.set_b(from_index)
|
||||||
|
}
|
||||||
|
|
||||||
// instruction.set_a(to_register);
|
pub fn not(to_register: u16, from_index: u16) -> Instruction {
|
||||||
// instruction.set_b(left_index);
|
*Instruction::new(Operation::Not)
|
||||||
// instruction.set_c(right_index);
|
.set_a(to_register)
|
||||||
|
.set_b(from_index)
|
||||||
// instruction
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn test(test_register: u16, test_value: bool) -> Instruction {
|
|
||||||
// Instruction::builder(Operation::Test)
|
|
||||||
// .set_b(test_register)
|
|
||||||
// .set_c_to_boolean(test_value)
|
|
||||||
// .build()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn test_set(to_register: u16, argument_index: u16, test_value: bool) -> Instruction {
|
|
||||||
// Instruction::builder(Operation::TestSet)
|
|
||||||
// .set_a(to_register)
|
|
||||||
// .set_b(argument_index)
|
|
||||||
// .set_c_to_boolean(test_value)
|
|
||||||
// .build()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn equal(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction {
|
|
||||||
// let mut instruction = Instruction(Operation::Equal as u32);
|
|
||||||
|
|
||||||
// instruction.set_a_to_boolean(comparison_boolean);
|
|
||||||
// instruction.set_b(left_index);
|
|
||||||
// instruction.set_c(right_index);
|
|
||||||
|
|
||||||
// instruction
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn less(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction {
|
|
||||||
// let mut instruction = Instruction(Operation::Less as u32);
|
|
||||||
|
|
||||||
// instruction.set_a_to_boolean(comparison_boolean);
|
|
||||||
// instruction.set_b(left_index);
|
|
||||||
// instruction.set_c(right_index);
|
|
||||||
|
|
||||||
// instruction
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn less_equal(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction {
|
|
||||||
// let mut instruction = Instruction(Operation::LessEqual as u32);
|
|
||||||
|
|
||||||
// instruction.set_a_to_boolean(comparison_boolean);
|
|
||||||
// instruction.set_b(left_index);
|
|
||||||
// instruction.set_c(right_index);
|
|
||||||
|
|
||||||
// instruction
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn negate(to_register: u16, from_index: u16) -> Instruction {
|
|
||||||
// let mut instruction = Instruction(Operation::Negate as u32);
|
|
||||||
|
|
||||||
// instruction.set_a(to_register);
|
|
||||||
// instruction.set_b(from_index);
|
|
||||||
|
|
||||||
// instruction
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn not(to_register: u16, from_index: u16) -> Instruction {
|
|
||||||
// let mut instruction = Instruction(Operation::Not as u32);
|
|
||||||
|
|
||||||
// instruction.set_a(to_register);
|
|
||||||
// instruction.set_b(from_index);
|
|
||||||
|
|
||||||
// instruction
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn jump(jump_offset: u16, is_positive: bool) -> Instruction {
|
pub fn jump(jump_offset: u16, is_positive: bool) -> Instruction {
|
||||||
Instruction::builder(Operation::Jump)
|
*Instruction::new(Operation::Jump)
|
||||||
.set_b(jump_offset)
|
.set_b(jump_offset)
|
||||||
.set_c_to_boolean(is_positive)
|
.set_c_to_boolean(is_positive)
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(to_register: u16, function_register: u16, argument_count: u16) -> Instruction {
|
pub fn call(to_register: u16, function_register: u16, argument_count: u16) -> Instruction {
|
||||||
Instruction::builder(Operation::Call)
|
*Instruction::new(Operation::Call)
|
||||||
.set_a(to_register)
|
.set_a(to_register)
|
||||||
.set_b(function_register)
|
.set_b(function_register)
|
||||||
.set_c(argument_count)
|
.set_c(argument_count)
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call_native(
|
pub fn call_native(
|
||||||
@ -314,17 +443,49 @@ impl Instruction {
|
|||||||
native_fn: NativeFunction,
|
native_fn: NativeFunction,
|
||||||
argument_count: u16,
|
argument_count: u16,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
Instruction::builder(Operation::CallNative)
|
*Instruction::new(Operation::CallNative)
|
||||||
.set_a(to_register)
|
.set_a(to_register)
|
||||||
.set_b(native_fn as u16)
|
.set_b(native_fn as u16)
|
||||||
.set_c(argument_count)
|
.set_c(argument_count)
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#return(should_return_value: bool) -> Instruction {
|
pub fn r#return(should_return_value: bool) -> Instruction {
|
||||||
Instruction::builder(Operation::Return)
|
*Instruction::new(Operation::Return).set_b_to_boolean(should_return_value)
|
||||||
.set_b_to_boolean(should_return_value)
|
}
|
||||||
.build()
|
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 {
|
pub fn disassembly_info(&self, chunk: &Chunk) -> String {
|
||||||
@ -552,22 +713,45 @@ impl Instruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&Instruction> for u64 {
|
||||||
|
fn from(instruction: &Instruction) -> Self {
|
||||||
|
instruction.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn r#move() {
|
fn builder() {
|
||||||
let mut instruction = Instruction::r#move(4, 1);
|
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();
|
||||||
|
|
||||||
instruction.set_b_is_constant();
|
assert_eq!(instruction_from_builder, instruction);
|
||||||
instruction.set_c_is_constant();
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn r#move() {
|
||||||
|
let instruction = Instruction::r#move(4, 1);
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Move);
|
assert_eq!(instruction.operation(), Operation::Move);
|
||||||
assert_eq!(instruction.a(), 4);
|
assert_eq!(instruction.b(), 4);
|
||||||
assert_eq!(instruction.b(), 1);
|
assert_eq!(instruction.c(), 1);
|
||||||
assert!(instruction.b_is_constant());
|
|
||||||
assert!(instruction.b_is_constant());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -623,22 +807,18 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn declare_local() {
|
fn declare_local() {
|
||||||
let mut instruction = Instruction::define_local(4, 1, true);
|
let instruction = *Instruction::define_local(4, 1, true).set_b_is_constant();
|
||||||
|
|
||||||
instruction.set_b_is_constant();
|
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::DefineLocal);
|
assert_eq!(instruction.operation(), Operation::DefineLocal);
|
||||||
assert_eq!(instruction.a(), 4);
|
assert_eq!(instruction.a(), 4);
|
||||||
assert_eq!(instruction.b(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert_eq!(instruction.c(), true as u16);
|
assert!(instruction.c_as_boolean());
|
||||||
assert!(instruction.b_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add() {
|
fn add() {
|
||||||
let mut instruction = Instruction::add(1, 1, 4);
|
let instruction = *Instruction::add(1, 1, 4).set_b_is_constant();
|
||||||
|
|
||||||
instruction.set_b_is_constant();
|
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Add);
|
assert_eq!(instruction.operation(), Operation::Add);
|
||||||
assert_eq!(instruction.a(), 1);
|
assert_eq!(instruction.a(), 1);
|
||||||
|
@ -21,7 +21,7 @@ pub use crate::compiler::{compile, CompileError, Compiler};
|
|||||||
pub use crate::disassembler::Disassembler;
|
pub use crate::disassembler::Disassembler;
|
||||||
pub use crate::dust_error::{AnnotatedError, DustError};
|
pub use crate::dust_error::{AnnotatedError, DustError};
|
||||||
pub use crate::formatter::{format, Formatter};
|
pub use crate::formatter::{format, Formatter};
|
||||||
pub use crate::instruction::Instruction;
|
pub use crate::instruction::{Instruction, InstructionBuilder};
|
||||||
pub use crate::lexer::{lex, LexError, Lexer};
|
pub use crate::lexer::{lex, LexError, Lexer};
|
||||||
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
||||||
pub use crate::operation::Operation;
|
pub use crate::operation::Operation;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Tool used by the compiler to optimize a chunk's bytecode.
|
//! Tool used by the compiler to optimize a chunk's bytecode.
|
||||||
|
|
||||||
use crate::{Chunk, Instruction, Operation, Span};
|
use crate::{instruction::InstructionBuilder, Chunk, Instruction, Operation, Span};
|
||||||
|
|
||||||
/// An instruction optimizer that mutably borrows instructions from a chunk.
|
/// An instruction optimizer that mutably borrows instructions from a chunk.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -58,11 +58,11 @@ impl<'a> Optimizer<'a> {
|
|||||||
|
|
||||||
let first_loader_register = first_loader.a();
|
let first_loader_register = first_loader.a();
|
||||||
let second_loader = &mut instructions.last_mut().unwrap().0;
|
let second_loader = &mut instructions.last_mut().unwrap().0;
|
||||||
let mut second_loader_new = Instruction::with_operation(second_loader.operation());
|
let second_loader_new = Instruction::builder(second_loader.operation())
|
||||||
|
.a(first_loader_register)
|
||||||
second_loader_new.set_a(first_loader_register);
|
.b(second_loader.b())
|
||||||
second_loader_new.set_b(second_loader.b());
|
.c(second_loader.c())
|
||||||
second_loader_new.set_c(second_loader.c());
|
.build();
|
||||||
|
|
||||||
*second_loader = second_loader_new;
|
*second_loader = second_loader_new;
|
||||||
|
|
||||||
@ -89,22 +89,12 @@ impl<'a> Optimizer<'a> {
|
|||||||
let instructions = self.instructions_mut();
|
let instructions = self.instructions_mut();
|
||||||
let set_local = instructions.pop().unwrap().0;
|
let set_local = instructions.pop().unwrap().0;
|
||||||
let set_local_register = set_local.a();
|
let set_local_register = set_local.a();
|
||||||
let math_instruction = &mut instructions.last_mut().unwrap().0;
|
let math_instruction = instructions.last_mut().unwrap().0;
|
||||||
let mut math_instruction_new = Instruction::with_operation(math_instruction.operation());
|
let math_instruction_new = InstructionBuilder::from(&math_instruction)
|
||||||
|
.a(set_local_register)
|
||||||
|
.build();
|
||||||
|
|
||||||
math_instruction_new.set_a(set_local_register);
|
instructions.last_mut().unwrap().0 = math_instruction_new;
|
||||||
math_instruction_new.set_b(math_instruction.b());
|
|
||||||
math_instruction_new.set_c(math_instruction.c());
|
|
||||||
|
|
||||||
if math_instruction.b_is_constant() {
|
|
||||||
math_instruction_new.set_b_is_constant();
|
|
||||||
}
|
|
||||||
|
|
||||||
if math_instruction.c_is_constant() {
|
|
||||||
math_instruction_new.set_c_is_constant();
|
|
||||||
}
|
|
||||||
|
|
||||||
*math_instruction = math_instruction_new;
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -365,8 +365,8 @@ impl Ord for Type {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct FunctionType {
|
pub struct FunctionType {
|
||||||
pub type_parameters: Option<Vec<u8>>,
|
pub type_parameters: Option<Vec<u16>>,
|
||||||
pub value_parameters: Option<Vec<(u8, Type)>>,
|
pub value_parameters: Option<Vec<(u16, Type)>>,
|
||||||
pub return_type: Box<Type>,
|
pub return_type: Box<Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,10 +33,10 @@ pub struct Vm<'a> {
|
|||||||
chunk: &'a Chunk,
|
chunk: &'a Chunk,
|
||||||
stack: Vec<Register>,
|
stack: Vec<Register>,
|
||||||
parent: Option<&'a Vm<'a>>,
|
parent: Option<&'a Vm<'a>>,
|
||||||
local_definitions: Vec<Option<u8>>,
|
local_definitions: Vec<Option<u16>>,
|
||||||
|
|
||||||
ip: usize,
|
ip: usize,
|
||||||
last_assigned_register: Option<u8>,
|
last_assigned_register: Option<u16>,
|
||||||
current_position: Span,
|
current_position: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,7 +387,7 @@ impl<'a> Vm<'a> {
|
|||||||
(first_argument_index..last_argument_index).enumerate()
|
(first_argument_index..last_argument_index).enumerate()
|
||||||
{
|
{
|
||||||
function_vm.set_register(
|
function_vm.set_register(
|
||||||
argument_index as u8,
|
argument_index as u16,
|
||||||
Register::Pointer(Pointer::ParentStack(argument_register_index)),
|
Register::Pointer(Pointer::ParentStack(argument_register_index)),
|
||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
@ -474,7 +474,7 @@ impl<'a> Vm<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_register(&self, register_index: u8) -> Result<ValueRef, VmError> {
|
fn open_register(&self, register_index: u16) -> Result<ValueRef, VmError> {
|
||||||
let register_index = register_index as usize;
|
let register_index = register_index as usize;
|
||||||
let register =
|
let register =
|
||||||
self.stack
|
self.stack
|
||||||
@ -499,7 +499,7 @@ impl<'a> Vm<'a> {
|
|||||||
|
|
||||||
pub(crate) fn open_register_allow_empty(
|
pub(crate) fn open_register_allow_empty(
|
||||||
&self,
|
&self,
|
||||||
register_index: u8,
|
register_index: u16,
|
||||||
) -> Result<Option<ValueRef>, VmError> {
|
) -> Result<Option<ValueRef>, VmError> {
|
||||||
let register_index = register_index as usize;
|
let register_index = register_index as usize;
|
||||||
let register =
|
let register =
|
||||||
@ -533,7 +533,7 @@ impl<'a> Vm<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// DRY helper to get a constant or register values
|
/// DRY helper to get a constant or register values
|
||||||
fn get_argument(&self, index: u8, is_constant: bool) -> Result<&ConcreteValue, VmError> {
|
fn get_argument(&self, index: u16, is_constant: bool) -> Result<&ConcreteValue, VmError> {
|
||||||
let argument = if is_constant {
|
let argument = if is_constant {
|
||||||
self.get_constant(index)?
|
self.get_constant(index)?
|
||||||
} else {
|
} else {
|
||||||
@ -562,7 +562,7 @@ impl<'a> Vm<'a> {
|
|||||||
Ok((left, right))
|
Ok((left, right))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_register(&mut self, to_register: u8, register: Register) -> Result<(), VmError> {
|
fn set_register(&mut self, to_register: u16, register: Register) -> Result<(), VmError> {
|
||||||
self.last_assigned_register = Some(to_register);
|
self.last_assigned_register = Some(to_register);
|
||||||
|
|
||||||
let length = self.stack.len();
|
let length = self.stack.len();
|
||||||
@ -607,7 +607,7 @@ impl<'a> Vm<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_constant(&self, index: u8) -> Result<&ConcreteValue, VmError> {
|
fn get_constant(&self, index: u16) -> Result<&ConcreteValue, VmError> {
|
||||||
self.chunk
|
self.chunk
|
||||||
.get_constant(index)
|
.get_constant(index)
|
||||||
.map_err(|error| VmError::Chunk {
|
.map_err(|error| VmError::Chunk {
|
||||||
@ -654,10 +654,10 @@ impl Display for Register {
|
|||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub enum Pointer {
|
pub enum Pointer {
|
||||||
Stack(u8),
|
Stack(u16),
|
||||||
Constant(u8),
|
Constant(u16),
|
||||||
ParentStack(u8),
|
ParentStack(u16),
|
||||||
ParentConstant(u8),
|
ParentConstant(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Pointer {
|
impl Display for Pointer {
|
||||||
@ -701,7 +701,7 @@ pub enum VmError {
|
|||||||
|
|
||||||
// Local errors
|
// Local errors
|
||||||
UndefinedLocal {
|
UndefinedLocal {
|
||||||
local_index: u8,
|
local_index: u16,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user