Add and pass tests
This commit is contained in:
parent
a1dd7e3bb9
commit
bf4f319302
@ -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(¢er(&constant_display));
|
disassembly.push_str(¢er(&constant_display));
|
||||||
|
@ -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() {
|
||||||
|
@ -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),
|
||||||
|
@ -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 }";
|
||||||
|
Loading…
Reference in New Issue
Block a user