Add and pass tests
This commit is contained in:
parent
a1dd7e3bb9
commit
bf4f319302
@ -379,11 +379,12 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
.as_ref()
|
||||
.map(|value| value.to_string())
|
||||
.unwrap_or("empty".to_string());
|
||||
let elipsis = if value_display.len() > 9 { "..." } else { "" };
|
||||
let constant_display = if value_display.len() > 9 {
|
||||
format!("{index:<5} {value_display:^7.7}{elipsis}")
|
||||
let trucated_length = 8;
|
||||
let with_elipsis = trucated_length - 3;
|
||||
let constant_display = if value_display.len() > with_elipsis {
|
||||
format!("{index:<5} {value_display:.<trucated_length$.with_elipsis$}")
|
||||
} else {
|
||||
format!("{index:<5} {value_display:10}")
|
||||
format!("{index:<5} {value_display:<trucated_length$}")
|
||||
};
|
||||
|
||||
disassembly.push_str(¢er(&constant_display));
|
||||
|
@ -339,7 +339,7 @@ impl Instruction {
|
||||
}
|
||||
Operation::LoadList => {
|
||||
let destination = self.destination();
|
||||
let first_index = destination - self.first_argument();
|
||||
let first_index = destination - (self.first_argument() - 1);
|
||||
let last_index = destination - 1;
|
||||
|
||||
format!("R{} = [R{}..=R{}]", destination, first_index, last_index)
|
||||
@ -390,43 +390,43 @@ impl Instruction {
|
||||
let destination = self.destination();
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("R({destination}) = {first_argument} + {second_argument}",)
|
||||
format!("R{destination} = {first_argument} + {second_argument}",)
|
||||
}
|
||||
Operation::Subtract => {
|
||||
let destination = self.destination();
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("R({destination}) = {first_argument} - {second_argument}",)
|
||||
format!("R{destination} = {first_argument} - {second_argument}",)
|
||||
}
|
||||
Operation::Multiply => {
|
||||
let destination = self.destination();
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("R({destination}) = {first_argument} * {second_argument}",)
|
||||
format!("R{destination} = {first_argument} * {second_argument}",)
|
||||
}
|
||||
Operation::Divide => {
|
||||
let destination = self.destination();
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("R({destination}) = {first_argument} / {second_argument}",)
|
||||
format!("R{destination} = {first_argument} / {second_argument}",)
|
||||
}
|
||||
Operation::Modulo => {
|
||||
let destination = self.destination();
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("R({destination}) = {first_argument} % {second_argument}",)
|
||||
format!("R{destination} = {first_argument} % {second_argument}",)
|
||||
}
|
||||
Operation::And => {
|
||||
let destination = self.destination();
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("R({destination}) = {first_argument} && {second_argument}",)
|
||||
format!("R{destination} = {first_argument} && {second_argument}",)
|
||||
}
|
||||
Operation::Or => {
|
||||
let destination = self.destination();
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("R({destination}) = {first_argument} || {second_argument}",)
|
||||
format!("R{destination} = {first_argument} || {second_argument}",)
|
||||
}
|
||||
Operation::Equal => {
|
||||
let comparison_symbol = if self.destination_as_boolean() {
|
||||
|
@ -149,26 +149,26 @@ impl<'src> Parser<'src> {
|
||||
|
||||
let boolean = text.parse::<bool>().unwrap();
|
||||
|
||||
if let Ok((last_instruction, _)) =
|
||||
self.chunk.get_last_instruction(self.current_position)
|
||||
if let Ok((last_instruction, _)) = self
|
||||
.chunk
|
||||
.get_last_instruction(self.current_position)
|
||||
.copied()
|
||||
{
|
||||
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(
|
||||
Instruction::load_boolean(destination, boolean, skip),
|
||||
Instruction::load_boolean(self.current_register, boolean, skip),
|
||||
self.previous_position,
|
||||
);
|
||||
|
||||
if let Operation::LoadBoolean = last_instruction.operation() {
|
||||
self.increment_register()?;
|
||||
}
|
||||
} else {
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, boolean, false),
|
||||
self.previous_position,
|
||||
);
|
||||
self.increment_register()?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,25 +313,20 @@ impl<'src> Parser<'src> {
|
||||
let mut push_back_left = false;
|
||||
let mut left_is_constant = false;
|
||||
let left = match left_instruction.operation() {
|
||||
Operation::LoadConstant => {
|
||||
Operation::LoadConstant | Operation::GetLocal => {
|
||||
log::trace!(
|
||||
"Condensing {} to binary expression",
|
||||
left_instruction.operation()
|
||||
);
|
||||
|
||||
left_is_constant = true;
|
||||
left_is_constant = matches!(left_instruction.operation(), Operation::LoadConstant);
|
||||
|
||||
self.decrement_register()?;
|
||||
left_instruction.first_argument()
|
||||
}
|
||||
Operation::GetLocal => {
|
||||
log::trace!(
|
||||
"Condensing {} to binary expression",
|
||||
left_instruction.operation()
|
||||
);
|
||||
|
||||
Operation::LoadBoolean => {
|
||||
self.decrement_register()?;
|
||||
left_instruction.first_argument()
|
||||
left_instruction.destination()
|
||||
}
|
||||
Operation::Close => {
|
||||
return Err(ParseError::ExpectedExpression {
|
||||
@ -358,25 +353,21 @@ impl<'src> Parser<'src> {
|
||||
let mut push_back_right = false;
|
||||
let mut right_is_constant = false;
|
||||
let right = match right_instruction.operation() {
|
||||
Operation::LoadConstant => {
|
||||
Operation::LoadConstant | Operation::GetLocal => {
|
||||
log::trace!(
|
||||
"Condensing {} to binary expression",
|
||||
right_instruction.operation()
|
||||
);
|
||||
|
||||
right_is_constant = true;
|
||||
right_is_constant =
|
||||
matches!(right_instruction.operation(), Operation::LoadConstant);
|
||||
|
||||
self.decrement_register()?;
|
||||
right_instruction.first_argument()
|
||||
}
|
||||
Operation::GetLocal => {
|
||||
log::trace!(
|
||||
"Condensing {} to binary expression",
|
||||
right_instruction.operation()
|
||||
);
|
||||
|
||||
Operation::LoadBoolean => {
|
||||
self.decrement_register()?;
|
||||
right_instruction.first_argument()
|
||||
right_instruction.destination()
|
||||
}
|
||||
Operation::Close => {
|
||||
return Err(ParseError::ExpectedExpression {
|
||||
@ -665,10 +656,36 @@ impl<'src> Parser<'src> {
|
||||
self.expect(TokenKind::Equal)?;
|
||||
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 =
|
||||
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(
|
||||
Instruction::define_local(register, local_index, is_mutable),
|
||||
|
@ -2,6 +2,56 @@ use crate::Local;
|
||||
|
||||
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]
|
||||
fn if_else_expression() {
|
||||
let source = "if 1 == 1 { 2 } else { 3 }";
|
||||
|
Loading…
Reference in New Issue
Block a user