Add and pass tests

This commit is contained in:
Jeff 2024-09-18 22:00:24 -04:00
parent a1dd7e3bb9
commit bf4f319302
4 changed files with 109 additions and 41 deletions

View File

@ -379,11 +379,12 @@ impl<'a> ChunkDisassembler<'a> {
.as_ref() .as_ref()
.map(|value| value.to_string()) .map(|value| value.to_string())
.unwrap_or("empty".to_string()); .unwrap_or("empty".to_string());
let elipsis = if value_display.len() > 9 { "..." } else { "" }; let trucated_length = 8;
let constant_display = if value_display.len() > 9 { let with_elipsis = trucated_length - 3;
format!("{index:<5} {value_display:^7.7}{elipsis}") let constant_display = if value_display.len() > with_elipsis {
format!("{index:<5} {value_display:.<trucated_length$.with_elipsis$}")
} else { } else {
format!("{index:<5} {value_display:10}") format!("{index:<5} {value_display:<trucated_length$}")
}; };
disassembly.push_str(&center(&constant_display)); disassembly.push_str(&center(&constant_display));

View File

@ -339,7 +339,7 @@ impl Instruction {
} }
Operation::LoadList => { Operation::LoadList => {
let destination = self.destination(); let destination = self.destination();
let first_index = destination - self.first_argument(); let first_index = destination - (self.first_argument() - 1);
let last_index = destination - 1; let last_index = destination - 1;
format!("R{} = [R{}..=R{}]", destination, first_index, last_index) format!("R{} = [R{}..=R{}]", destination, first_index, last_index)
@ -390,43 +390,43 @@ impl Instruction {
let destination = self.destination(); let destination = self.destination();
let (first_argument, second_argument) = format_arguments(); let (first_argument, second_argument) = format_arguments();
format!("R({destination}) = {first_argument} + {second_argument}",) format!("R{destination} = {first_argument} + {second_argument}",)
} }
Operation::Subtract => { Operation::Subtract => {
let destination = self.destination(); let destination = self.destination();
let (first_argument, second_argument) = format_arguments(); let (first_argument, second_argument) = format_arguments();
format!("R({destination}) = {first_argument} - {second_argument}",) format!("R{destination} = {first_argument} - {second_argument}",)
} }
Operation::Multiply => { Operation::Multiply => {
let destination = self.destination(); let destination = self.destination();
let (first_argument, second_argument) = format_arguments(); let (first_argument, second_argument) = format_arguments();
format!("R({destination}) = {first_argument} * {second_argument}",) format!("R{destination} = {first_argument} * {second_argument}",)
} }
Operation::Divide => { Operation::Divide => {
let destination = self.destination(); let destination = self.destination();
let (first_argument, second_argument) = format_arguments(); let (first_argument, second_argument) = format_arguments();
format!("R({destination}) = {first_argument} / {second_argument}",) format!("R{destination} = {first_argument} / {second_argument}",)
} }
Operation::Modulo => { Operation::Modulo => {
let destination = self.destination(); let destination = self.destination();
let (first_argument, second_argument) = format_arguments(); let (first_argument, second_argument) = format_arguments();
format!("R({destination}) = {first_argument} % {second_argument}",) format!("R{destination} = {first_argument} % {second_argument}",)
} }
Operation::And => { Operation::And => {
let destination = self.destination(); let destination = self.destination();
let (first_argument, second_argument) = format_arguments(); let (first_argument, second_argument) = format_arguments();
format!("R({destination}) = {first_argument} && {second_argument}",) format!("R{destination} = {first_argument} && {second_argument}",)
} }
Operation::Or => { Operation::Or => {
let destination = self.destination(); let destination = self.destination();
let (first_argument, second_argument) = format_arguments(); let (first_argument, second_argument) = format_arguments();
format!("R({destination}) = {first_argument} || {second_argument}",) format!("R{destination} = {first_argument} || {second_argument}",)
} }
Operation::Equal => { Operation::Equal => {
let comparison_symbol = if self.destination_as_boolean() { let comparison_symbol = if self.destination_as_boolean() {

View File

@ -149,26 +149,26 @@ impl<'src> Parser<'src> {
let boolean = text.parse::<bool>().unwrap(); let boolean = text.parse::<bool>().unwrap();
if let Ok((last_instruction, _)) = if let Ok((last_instruction, _)) = self
self.chunk.get_last_instruction(self.current_position) .chunk
.get_last_instruction(self.current_position)
.copied()
{ {
let skip = last_instruction.operation() == Operation::Jump; let skip = last_instruction.operation() == Operation::Jump;
let destination = if let Operation::LoadBoolean = last_instruction.operation() {
last_instruction.destination()
} else {
self.current_register
};
self.emit_instruction( self.emit_instruction(
Instruction::load_boolean(destination, boolean, skip), Instruction::load_boolean(self.current_register, boolean, skip),
self.previous_position, self.previous_position,
); );
if let Operation::LoadBoolean = last_instruction.operation() {
self.increment_register()?;
}
} else { } else {
self.emit_instruction( self.emit_instruction(
Instruction::load_boolean(self.current_register, boolean, false), Instruction::load_boolean(self.current_register, boolean, false),
self.previous_position, self.previous_position,
); );
self.increment_register()?;
} }
} }
@ -313,25 +313,20 @@ impl<'src> Parser<'src> {
let mut push_back_left = false; let mut push_back_left = false;
let mut left_is_constant = false; let mut left_is_constant = false;
let left = match left_instruction.operation() { let left = match left_instruction.operation() {
Operation::LoadConstant => { Operation::LoadConstant | Operation::GetLocal => {
log::trace!( log::trace!(
"Condensing {} to binary expression", "Condensing {} to binary expression",
left_instruction.operation() left_instruction.operation()
); );
left_is_constant = true; left_is_constant = matches!(left_instruction.operation(), Operation::LoadConstant);
self.decrement_register()?; self.decrement_register()?;
left_instruction.first_argument() left_instruction.first_argument()
} }
Operation::GetLocal => { Operation::LoadBoolean => {
log::trace!(
"Condensing {} to binary expression",
left_instruction.operation()
);
self.decrement_register()?; self.decrement_register()?;
left_instruction.first_argument() left_instruction.destination()
} }
Operation::Close => { Operation::Close => {
return Err(ParseError::ExpectedExpression { return Err(ParseError::ExpectedExpression {
@ -358,25 +353,21 @@ impl<'src> Parser<'src> {
let mut push_back_right = false; let mut push_back_right = false;
let mut right_is_constant = false; let mut right_is_constant = false;
let right = match right_instruction.operation() { let right = match right_instruction.operation() {
Operation::LoadConstant => { Operation::LoadConstant | Operation::GetLocal => {
log::trace!( log::trace!(
"Condensing {} to binary expression", "Condensing {} to binary expression",
right_instruction.operation() right_instruction.operation()
); );
right_is_constant = true; right_is_constant =
matches!(right_instruction.operation(), Operation::LoadConstant);
self.decrement_register()?; self.decrement_register()?;
right_instruction.first_argument() right_instruction.first_argument()
} }
Operation::GetLocal => { Operation::LoadBoolean => {
log::trace!(
"Condensing {} to binary expression",
right_instruction.operation()
);
self.decrement_register()?; self.decrement_register()?;
right_instruction.first_argument() right_instruction.destination()
} }
Operation::Close => { Operation::Close => {
return Err(ParseError::ExpectedExpression { return Err(ParseError::ExpectedExpression {
@ -665,10 +656,36 @@ impl<'src> Parser<'src> {
self.expect(TokenKind::Equal)?; self.expect(TokenKind::Equal)?;
self.parse_expression()?; self.parse_expression()?;
let register = self.chunk.get_last_instruction(position)?.0.destination(); let (previous_instruction, previous_position) =
*self.chunk.get_last_instruction(position)?;
let register = previous_instruction.destination();
let local_index = let local_index =
self.chunk self.chunk
.declare_local(identifier, is_mutable, register, self.current_position)?; .declare_local(identifier, is_mutable, register, previous_position)?;
// Optimize for assignment to a comparison
if let Operation::Jump = previous_instruction.operation() {
let (jump, jump_position) = self.chunk.pop_instruction(self.current_position)?;
if let Operation::Equal = self
.chunk
.get_last_instruction(self.current_position)?
.0
.operation()
{
self.emit_instruction(jump, jump_position);
self.emit_instruction(
Instruction::load_boolean(self.current_register, true, true),
self.current_position,
);
self.emit_instruction(
Instruction::load_boolean(self.current_register, false, false),
self.current_position,
);
} else {
self.emit_instruction(jump, jump_position);
}
}
self.emit_instruction( self.emit_instruction(
Instruction::define_local(register, local_index, is_mutable), Instruction::define_local(register, local_index, is_mutable),

View File

@ -2,6 +2,56 @@ use crate::Local;
use super::*; use super::*;
#[test]
fn equality_assignment_long() {
let source = "let a = if 4 == 4 { true } else { false };";
assert_eq!(
parse(source),
Ok(Chunk::with_data(
vec![
(
*Instruction::equal(true, 0, 1)
.set_first_argument_to_constant()
.set_second_argument_to_constant(),
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)),
],
vec![Value::integer(4), Value::integer(4),],
vec![Local::new(Identifier::new("a"), false, 0, Some(0)),]
)),
);
}
#[test]
fn equality_assignment_short() {
let source = "let a = 4 == 4;";
assert_eq!(
parse(source),
Ok(Chunk::with_data(
vec![
(
*Instruction::equal(true, 0, 1)
.set_first_argument_to_constant()
.set_second_argument_to_constant(),
Span(10, 12)
),
(Instruction::jump(1, true), Span(10, 12)),
(Instruction::load_boolean(0, true, true), Span(14, 15)),
(Instruction::load_boolean(0, false, false), Span(14, 15)),
(Instruction::define_local(0, 0, false), Span(4, 5)),
],
vec![Value::integer(4), Value::integer(4),],
vec![Local::new(Identifier::new("a"), false, 0, Some(0)),]
)),
);
}
#[test] #[test]
fn if_else_expression() { fn if_else_expression() {
let source = "if 1 == 1 { 2 } else { 3 }"; let source = "if 1 == 1 { 2 } else { 3 }";