1
0

Begin refactoring jump instructions

This commit is contained in:
Jeff 2024-10-30 14:48:30 -04:00
parent 1f9adfab2f
commit 604962c535
6 changed files with 182 additions and 70 deletions

View File

@ -211,10 +211,11 @@ impl Instruction {
instruction
}
pub fn jump(jump_to: u8) -> Instruction {
pub fn jump(jump_offset: u8, is_positive: bool) -> Instruction {
let mut instruction = Instruction(Operation::Jump as u32);
instruction.set_b(jump_to);
instruction.set_b(jump_offset);
instruction.set_c_to_boolean(is_positive);
instruction
}
@ -557,9 +558,14 @@ impl Instruction {
format!("R{to_register} = !{argument}")
}
Operation::Jump => {
let jump_to = self.b();
let jump_distance = self.b();
let is_positive = self.c_as_boolean();
format!("JUMP TO {jump_to}")
if is_positive {
format!("JUMP +{jump_distance}")
} else {
format!("JUMP -{jump_distance}")
}
}
Operation::Call => {
let to_register = self.a();
@ -839,10 +845,12 @@ mod tests {
#[test]
fn jump() {
let instruction = Instruction::jump(4);
let instruction = Instruction::jump(4, true);
assert_eq!(instruction.operation(), Operation::Jump);
assert_eq!(instruction.b(), 4);
assert!(instruction.c_as_boolean());
}
#[test]

View File

@ -555,18 +555,37 @@ pub enum NativeFunctionError {
impl AnnotatedError for NativeFunctionError {
fn title() -> &'static str {
todo!()
"Native Function Error"
}
fn description(&self) -> &'static str {
todo!()
match self {
NativeFunctionError::ExpectedArgumentCount { .. } => {
"Expected a different number of arguments"
}
NativeFunctionError::Panic { .. } => "Explicit panic",
NativeFunctionError::Parse { .. } => "Failed to parse value",
NativeFunctionError::Io { .. } => "I/O error",
}
}
fn details(&self) -> Option<String> {
todo!()
match self {
NativeFunctionError::ExpectedArgumentCount {
expected, found, ..
} => Some(format!("Expected {} arguments, found {}", expected, found)),
NativeFunctionError::Panic { message, .. } => message.clone(),
NativeFunctionError::Parse { error, .. } => Some(format!("{}", error)),
NativeFunctionError::Io { error, .. } => Some(format!("{}", error)),
}
}
fn position(&self) -> Span {
todo!()
match self {
NativeFunctionError::ExpectedArgumentCount { position, .. } => *position,
NativeFunctionError::Panic { position, .. } => *position,
NativeFunctionError::Parse { position, .. } => *position,
NativeFunctionError::Io { position, .. } => *position,
}
}
}

View File

@ -228,6 +228,19 @@ impl<'src> Parser<'src> {
})
}
fn get_last_jumpable_mut(&mut self) -> Option<&mut Instruction> {
self.chunk
.instructions_mut()
.iter_mut()
.find_map(|(instruction, _)| {
if let Operation::LoadBoolean | Operation::LoadConstant = instruction.operation() {
Some(instruction)
} else {
None
}
})
}
fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), ParseError> {
let constant_index = self.chunk.push_constant(value, position)?;
let register = self.next_register();
@ -651,7 +664,6 @@ impl<'src> Parser<'src> {
Token::LessEqual => Instruction::less_equal(true, left, right),
Token::Greater => Instruction::less_equal(false, left, right),
Token::GreaterEqual => Instruction::less(false, left, right),
_ => {
return Err(ParseError::ExpectedTokenMultiple {
expected: &[
@ -688,9 +700,7 @@ impl<'src> Parser<'src> {
self.emit_instruction(instruction, operator_position);
let jump_to = (self.chunk.len() + 2) as u8;
self.emit_instruction(Instruction::jump(jump_to), operator_position);
self.emit_instruction(Instruction::jump(1, true), operator_position);
self.emit_instruction(
Instruction::load_boolean(register, true, true),
operator_position,
@ -706,6 +716,7 @@ impl<'src> Parser<'src> {
}
fn parse_logical_binary(&mut self) -> Result<(), ParseError> {
let start_length = self.chunk.len();
let (left_instruction, left_position) =
self.chunk
.instructions_mut()
@ -719,7 +730,7 @@ impl<'src> Parser<'src> {
let operator_position = self.current_position;
let rule = ParseRule::from(&operator);
let instruction = match operator {
let test_instruction = match operator {
Token::DoubleAmpersand => Instruction::test(left_instruction.a(), false),
Token::DoublePipe => Instruction::test(left_instruction.a(), true),
_ => {
@ -733,11 +744,11 @@ impl<'src> Parser<'src> {
self.advance()?;
self.emit_instruction(left_instruction, left_position);
self.emit_instruction(instruction, operator_position);
self.emit_instruction(test_instruction, operator_position);
let jump_to = (self.chunk.len() + 2) as u8;
let jump_distance = (self.chunk.len() - start_length) as u8;
self.emit_instruction(Instruction::jump(jump_to), operator_position);
self.emit_instruction(Instruction::jump(jump_distance, true), operator_position);
self.parse_sub_expression(&rule.precedence)?;
self.current_is_expression = true;
@ -956,7 +967,16 @@ impl<'src> Parser<'src> {
};
if let Token::LeftCurlyBrace = self.current_token {
let block_start = self.chunk.len();
self.parse_block(block_allowed)?;
let block_end = self.chunk.len();
let jump_distance = (block_end - block_start) as u8;
if let Some(jump) = self.get_last_jump_mut() {
jump.set_b(jump_distance);
}
} else {
return Err(ParseError::ExpectedToken {
expected: TokenKind::LeftCurlyBrace,
@ -965,14 +985,57 @@ impl<'src> Parser<'src> {
});
}
let if_block_end = self.chunk.len();
if let Some(if_jump) = self.get_last_jump_mut() {
if_jump.set_b(if_block_end as u8);
}
let if_block_is_expression = self
.chunk
.instructions()
.iter()
.find_map(|(instruction, _)| {
if !matches!(instruction.operation(), Operation::Jump) {
Some(true)
} else {
None
}
})
.unwrap_or(false);
if let Token::Else = self.current_token {
let else_start = self.chunk.len();
self.parse_else(allowed, block_allowed)?;
let else_end = self.chunk.len();
let jump_distance = (else_end - else_start) as u8;
self.current_is_expression = if_block_is_expression
&& self
.chunk
.instructions()
.iter()
.find_map(|(instruction, _)| {
if !matches!(instruction.operation(), Operation::Jump) {
Some(true)
} else {
None
}
})
.unwrap_or(false);
if jump_distance == 1 {
if let Some(skippable) = self.get_last_jumpable_mut() {
skippable.set_c_to_boolean(true);
} else {
self.chunk.insert_instruction(
else_start,
Instruction::jump(jump_distance, true),
self.current_position,
)?;
}
} else {
self.chunk.insert_instruction(
else_start,
Instruction::jump(jump_distance, true),
self.current_position,
)?;
}
} else {
self.current_is_expression = false;
}
@ -987,21 +1050,17 @@ impl<'src> Parser<'src> {
if let Token::If = self.current_token {
self.parse_if(allowed)?;
let if_block_end = self.chunk.len() as u8;
if let Some(if_jump) = self.get_last_jump_mut() {
if_jump.set_b(if_block_end + 1);
}
} else if let Token::LeftCurlyBrace = self.current_token {
self.parse_block(block_allowed)?;
let else_end = self.chunk.len();
if else_end - if_block_end > 1 {
let jump_distance = (else_end - if_block_end) as u8;
self.chunk.insert_instruction(
if_block_end,
Instruction::jump((else_end + 1) as u8),
Instruction::jump(jump_distance, true),
self.current_position,
)?;
}
@ -1051,13 +1110,13 @@ impl<'src> Parser<'src> {
implicit_return: false,
})?;
let jump_end = self.chunk.len() as u8;
if let Some(jump) = self.get_last_jump_mut() {
jump.set_b(jump_end + 1);
jump.set_b(jump.b() + 1);
}
let jump_back = Instruction::jump(jump_start);
let jump_end = self.chunk.len() as u8;
let jump_distance = jump_end - jump_start;
let jump_back = Instruction::jump(jump_distance, false);
self.emit_instruction(jump_back, self.current_position);
self.optimize_statement();

View File

@ -81,13 +81,13 @@ impl Vm {
Operation::LoadBoolean => {
let to_register = instruction.a();
let boolean = instruction.b_as_boolean();
let skip = instruction.c_as_boolean();
let jump = instruction.c_as_boolean();
let value = Value::boolean(boolean);
self.set(to_register, value, position)?;
if skip {
self.ip += 1;
if jump {
self.jump_to_ip(self.ip + 1);
}
}
Operation::LoadConstant => {
@ -98,7 +98,7 @@ impl Vm {
self.set_constant(to_register, from_constant, position)?;
if jump {
self.ip += 1;
self.jump_to_ip(self.ip + 1);
}
}
Operation::LoadList => {
@ -247,7 +247,7 @@ impl Vm {
self.ip - jump_distance as usize
};
self.ip = new_ip;
self.jump_to_ip(new_ip);
}
}
Operation::Less => {
@ -283,7 +283,7 @@ impl Vm {
self.ip - jump_distance as usize
};
self.ip = new_ip;
self.jump_to_ip(new_ip);
}
}
Operation::LessEqual => {
@ -320,7 +320,7 @@ impl Vm {
self.ip - jump_distance as usize
};
self.ip = new_ip;
self.jump_to_ip(new_ip);
}
}
Operation::Negate => {
@ -348,9 +348,15 @@ impl Vm {
self.set(instruction.a(), not, position)?;
}
Operation::Jump => {
let jump_to = instruction.b();
let jump_distance = instruction.b();
let is_positive = instruction.c_as_boolean();
let new_ip = if is_positive {
self.ip + jump_distance as usize
} else {
self.ip - jump_distance as usize
};
self.ip = jump_to as usize;
self.jump_to_ip(new_ip);
}
Operation::Call => {
let to_register = instruction.a();
@ -418,6 +424,24 @@ impl Vm {
Ok(None)
}
fn jump_to_ip(&mut self, new_ip: usize) {
let final_index = self.chunk.len() - 1;
if new_ip > final_index {
let last_operation = self
.chunk
.instructions()
.last()
.map(|(instruction, _)| instruction.operation());
if let Some(Operation::Return) = last_operation {
self.ip = final_index;
}
} else {
self.ip = new_ip;
}
}
fn set(&mut self, to_register: u8, value: Value, position: Span) -> Result<(), VmError> {
let length = self.stack.len();
self.last_assigned_register = Some(to_register);

View File

@ -15,7 +15,7 @@ fn equality_assignment_long() {
.set_c_is_constant(),
Span(13, 15)
),
(Instruction::jump(3), Span(13, 15)),
(Instruction::jump(1, true), Span(13, 15)),
(Instruction::load_boolean(0, true, true), Span(20, 24)),
(Instruction::load_boolean(0, false, false), Span(34, 39)),
(Instruction::define_local(0, 0, false), Span(4, 5)),
@ -45,7 +45,7 @@ fn equality_assignment_short() {
.set_c_is_constant(),
Span(10, 12)
),
(Instruction::jump(3), Span(10, 12)),
(Instruction::jump(1, true), Span(10, 12)),
(Instruction::load_boolean(0, true, true), Span(10, 12)),
(Instruction::load_boolean(0, false, false), Span(10, 12)),
(Instruction::define_local(0, 0, false), Span(4, 5)),
@ -80,12 +80,13 @@ fn if_else_complex() {
.set_c_is_constant(),
Span(14, 16)
),
(Instruction::jump(7), Span(14, 16)),
(Instruction::jump(5, true), Span(14, 16)),
(Instruction::load_constant(0, 2, false), Span(33, 34)),
(Instruction::load_constant(1, 3, false), Span(36, 37)),
(Instruction::load_constant(2, 4, false), Span(39, 40)),
(Instruction::load_constant(3, 5, false), Span(42, 43)),
(Instruction::jump(11), Span(95, 95)),
(Instruction::jump(5, true), Span(95, 95)),
(Instruction::jump(4, true), Span(95, 95)),
(Instruction::load_constant(4, 6, false), Span(74, 75)),
(Instruction::load_constant(5, 7, false), Span(77, 78)),
(Instruction::load_constant(6, 8, false), Span(80, 81)),
@ -137,27 +138,27 @@ fn if_else_complex() {
// .set_c_is_constant(),
// Span(14, 16)
// ),
// (Instruction::jump(7), Span(14, 16)),
// (Instruction::jump(7, true), Span(14, 16)),
// (
// *Instruction::equal(true, 0, 2)
// .set_b_is_constant()
// .set_c_is_constant(),
// Span(38, 41)
// ),
// (Instruction::jump(3), Span(38, 41)),
// (Instruction::jump(3, true), Span(38, 41)),
// (Instruction::load_constant(0, 1, false), Span(61, 62)),
// (Instruction::jump(11), Span(95, 95)),
// (Instruction::jump(1, true1), Span(95, 95)),
// (
// *Instruction::equal(true, 0, 3)
// .set_b_is_constant()
// .set_c_is_constant(),
// Span(77, 79)
// ),
// (Instruction::jump(3), Span(77, 79)),
// (Instruction::jump(3, true), Span(77, 79)),
// (Instruction::load_constant(0, 2, false), Span(94, 95)),
// (Instruction::jump(11), Span(95, 95)),
// (Instruction::jump(1, true1), Span(95, 95)),
// (Instruction::load_constant(0, 3, false), Span(114, 115)),
// (Instruction::jump(11), Span(95, 95)),
// (Instruction::jump(1, true1), Span(95, 95)),
// (Instruction::load_constant(0, 4, false), Span(134, 135)),
// (Instruction::r#return(true), Span(146, 146)),
// ],
@ -194,12 +195,12 @@ fn if_else_false() {
.set_c_is_constant(),
Span(5, 7)
),
(Instruction::jump(3), Span(5, 7)),
(Instruction::jump(1, true), Span(5, 7)),
(
Instruction::call_native(0, NativeFunction::Panic, 0),
Span(12, 19)
),
(Instruction::load_constant(0, 2, false), Span(29, 31)),
(Instruction::load_constant(0, 2, true), Span(29, 31)),
(Instruction::r#return(true), Span(33, 33)),
],
vec![Value::integer(1), Value::integer(2), Value::integer(42)],
@ -225,19 +226,20 @@ fn if_else_true() {
.set_c_is_constant(),
Span(5, 7)
),
(Instruction::jump(3), Span(5, 7)),
(Instruction::load_constant(0, 2, false), Span(12, 14)),
(Instruction::jump(1, true), Span(5, 7)),
(Instruction::load_constant(0, 2, true), Span(12, 14)),
(
Instruction::call_native(1, NativeFunction::Panic, 0),
Span(24, 31)
),
(Instruction::r#return(true), Span(33, 33))
],
vec![Value::integer(1), Value::integer(1), Value::integer(42)],
vec![]
)),
);
assert_eq!(run(source), Ok(None));
assert_eq!(run(source), Ok(Some(Value::integer(42))));
}
#[test]
@ -255,7 +257,7 @@ fn if_expression_false() {
.set_c_is_constant(),
Span(5, 7)
),
(Instruction::jump(3), Span(5, 7)),
(Instruction::jump(1, true), Span(5, 7)),
(Instruction::load_constant(0, 2, false), Span(12, 13)),
],
vec![Value::integer(1), Value::integer(2), Value::integer(2)],
@ -281,7 +283,7 @@ fn if_expression_true() {
.set_c_is_constant(),
Span(5, 7)
),
(Instruction::jump(3), Span(5, 7)),
(Instruction::jump(1, true), Span(5, 7)),
(Instruction::load_constant(0, 2, false), Span(12, 13)),
],
vec![Value::integer(1), Value::integer(1), Value::integer(2)],

View File

@ -59,7 +59,7 @@ fn and() {
vec![
(Instruction::load_boolean(0, true, false), Span(0, 4)),
(Instruction::test(0, false), Span(5, 7)),
(Instruction::jump(4), Span(5, 7)),
(Instruction::jump(4, true), Span(5, 7)),
(Instruction::load_boolean(1, false, false), Span(8, 13)),
(Instruction::r#return(true), Span(13, 13)),
],
@ -258,7 +258,7 @@ fn equal() {
.set_c_is_constant(),
Span(2, 4)
),
(Instruction::jump(3), Span(2, 4)),
(Instruction::jump(3, true), Span(2, 4)),
(Instruction::load_boolean(0, true, true), Span(2, 4)),
(Instruction::load_boolean(0, false, false), Span(2, 4)),
(Instruction::r#return(true), Span(6, 6)),
@ -419,7 +419,7 @@ fn greater() {
.set_c_is_constant(),
Span(2, 3)
),
(Instruction::jump(3), Span(2, 3)),
(Instruction::jump(3, true), Span(2, 3)),
(Instruction::load_boolean(0, true, true), Span(2, 3)),
(Instruction::load_boolean(0, false, false), Span(2, 3)),
(Instruction::r#return(true), Span(5, 5)),
@ -447,7 +447,7 @@ fn greater_than_or_equal() {
.set_c_is_constant(),
Span(2, 4)
),
(Instruction::jump(3), Span(2, 4)),
(Instruction::jump(3, true), Span(2, 4)),
(Instruction::load_boolean(0, true, true), Span(2, 4)),
(Instruction::load_boolean(0, false, false), Span(2, 4)),
(Instruction::r#return(true), Span(6, 6)),
@ -475,7 +475,7 @@ fn less_than() {
.set_c_is_constant(),
Span(2, 3)
),
(Instruction::jump(3), Span(2, 3)),
(Instruction::jump(3, true), Span(2, 3)),
(Instruction::load_boolean(0, true, true), Span(2, 3)),
(Instruction::load_boolean(0, false, false), Span(2, 3)),
(Instruction::r#return(true), Span(5, 5)),
@ -503,7 +503,7 @@ fn less_than_or_equal() {
.set_c_is_constant(),
Span(2, 4)
),
(Instruction::jump(3), Span(2, 4)),
(Instruction::jump(3, true), Span(2, 4)),
(Instruction::load_boolean(0, true, true), Span(2, 4)),
(Instruction::load_boolean(0, false, false), Span(2, 4)),
(Instruction::r#return(true), Span(6, 6)),
@ -762,7 +762,7 @@ fn not_equal() {
.set_c_is_constant(),
Span(2, 4)
),
(Instruction::jump(3), Span(2, 4)),
(Instruction::jump(3, true), Span(2, 4)),
(Instruction::load_boolean(0, true, true), Span(2, 4)),
(Instruction::load_boolean(0, false, false), Span(2, 4)),
(Instruction::r#return(true), Span(6, 6)),
@ -786,7 +786,7 @@ fn or() {
vec![
(Instruction::load_boolean(0, true, false), Span(0, 4)),
(Instruction::test(0, true), Span(5, 7)),
(Instruction::jump(4), Span(5, 7)),
(Instruction::jump(4, true), Span(5, 7)),
(Instruction::load_boolean(1, false, false), Span(8, 13)),
(Instruction::r#return(true), Span(13, 13)),
],
@ -917,7 +917,7 @@ fn variable_and() {
(Instruction::define_local(1, 1, false), Span(18, 19)),
(Instruction::get_local(2, 0), Span(29, 30)),
(Instruction::test(2, false), Span(31, 33)),
(Instruction::jump(8), Span(31, 33)),
(Instruction::jump(8, true), Span(31, 33)),
(Instruction::get_local(3, 1), Span(34, 35)),
(Instruction::r#return(true), Span(35, 35)),
],
@ -947,9 +947,9 @@ fn r#while() {
*Instruction::less(true, 0, 1).set_c_is_constant(),
Span(23, 24)
),
(Instruction::jump(7), Span(23, 24)),
(Instruction::jump(7, true), Span(23, 24)),
(*Instruction::add(0, 0, 2).set_c_is_constant(), Span(39, 40)),
(Instruction::jump(2), Span(41, 42)),
(Instruction::jump(2, true), Span(41, 42)),
(Instruction::get_local(1, 0), Span(41, 42)),
(Instruction::r#return(true), Span(42, 42)),
],