Refine equality implementation
This commit is contained in:
parent
1d03876b89
commit
3df42f6a47
@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Display, Formatter};
|
||||
use colored::Colorize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{AnnotatedError, Identifier, Instruction, Operation, Span, Value};
|
||||
use crate::{instruction, AnnotatedError, Identifier, Instruction, Operation, Span, Value};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Chunk {
|
||||
@ -307,8 +307,8 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
"",
|
||||
"Instructions",
|
||||
"------------",
|
||||
"INDEX BYTECODE OPERATION INFO POSITION",
|
||||
"----- -------- --------------- ------------------------------ --------",
|
||||
"INDEX BYTECODE OPERATION INFO JUMP POSITION",
|
||||
"----- -------- --------------- ------------------------- -------------- --------",
|
||||
];
|
||||
|
||||
const CONSTANT_HEADER: [&'static str; 5] = [
|
||||
@ -395,17 +395,24 @@ impl<'a> ChunkDisassembler<'a> {
|
||||
for (index, (instruction, position)) in self.chunk.instructions.iter().enumerate() {
|
||||
let position = position.to_string();
|
||||
let operation = instruction.operation().to_string();
|
||||
let info_option = instruction.disassembly_info(Some(self.chunk));
|
||||
let bytecode = u32::from(instruction);
|
||||
|
||||
let instruction_display = if let Some(info) = info_option {
|
||||
format!("{index:<5} {bytecode:<08X} {operation:15} {info:30} {position:8}")
|
||||
let (info, jump_offset) = instruction.disassembly_info(Some(self.chunk));
|
||||
let info = if let Some(info) = info {
|
||||
info
|
||||
} else {
|
||||
format!(
|
||||
"{index:<5} {bytecode:<08X} {operation:15} {:30} {position:8}",
|
||||
" "
|
||||
)
|
||||
" ".to_string()
|
||||
};
|
||||
let jump_offset = if let Some(jump_offset) = jump_offset {
|
||||
let index = index as isize;
|
||||
let jump_index = index + jump_offset + 1;
|
||||
|
||||
format!("{index} -> {jump_index}")
|
||||
} else {
|
||||
" ".to_string()
|
||||
};
|
||||
let bytecode = u32::from(instruction);
|
||||
let instruction_display = format!(
|
||||
"{index:<5} {bytecode:<08X} {operation:15} {info:25} {jump_offset:14} {position:8}"
|
||||
);
|
||||
|
||||
disassembly.push_str(¢er(&instruction_display));
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::fmt::{self, format, Display, Formatter};
|
||||
|
||||
use crate::{Chunk, Operation, Span};
|
||||
|
||||
@ -216,6 +216,14 @@ impl Instruction {
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn end(returns_value: bool) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::End as u32);
|
||||
|
||||
instruction.set_destination_to_boolean(returns_value);
|
||||
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn operation(&self) -> Operation {
|
||||
Operation::from((self.0 & 0b0000_0000_0011_1111) as u8)
|
||||
}
|
||||
@ -299,17 +307,7 @@ impl Instruction {
|
||||
self.0 |= (argument as u32) << 8;
|
||||
}
|
||||
|
||||
pub fn disassemble(&self, chunk: &Chunk) -> String {
|
||||
let mut disassembled = format!("{:16} ", self.operation().to_string());
|
||||
|
||||
if let Some(info) = self.disassembly_info(Some(chunk)) {
|
||||
disassembled.push_str(&info);
|
||||
}
|
||||
|
||||
disassembled
|
||||
}
|
||||
|
||||
pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> Option<String> {
|
||||
pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> (Option<String>, Option<isize>) {
|
||||
let format_arguments = || {
|
||||
let first_argument = if self.first_argument_is_constant() {
|
||||
format!("C{}", self.first_argument())
|
||||
@ -324,42 +322,54 @@ impl Instruction {
|
||||
|
||||
(first_argument, second_argument)
|
||||
};
|
||||
let mut jump_offset = None;
|
||||
|
||||
let info = match self.operation() {
|
||||
Operation::Move => {
|
||||
format!("R{} = R{}", self.destination(), self.first_argument())
|
||||
}
|
||||
Operation::Move => Some(format!(
|
||||
"R{} = R{}",
|
||||
self.destination(),
|
||||
self.first_argument()
|
||||
)),
|
||||
Operation::Close => {
|
||||
let from_register = self.first_argument();
|
||||
let to_register = self.second_argument().saturating_sub(1);
|
||||
|
||||
format!("R{from_register}..=R{to_register}")
|
||||
Some(format!("R{from_register}..=R{to_register}"))
|
||||
}
|
||||
Operation::LoadBoolean => {
|
||||
let to_register = self.destination();
|
||||
let boolean = self.first_argument_as_boolean();
|
||||
let skip_display = if self.second_argument_as_boolean() {
|
||||
"IP++"
|
||||
let jump = self.second_argument_as_boolean();
|
||||
let info = if jump {
|
||||
jump_offset = Some(1);
|
||||
|
||||
format!("R{to_register} = {boolean} && JUMP")
|
||||
} else {
|
||||
""
|
||||
format!("R{to_register} = {boolean}")
|
||||
};
|
||||
|
||||
format!("R{to_register} = {boolean} {skip_display}",)
|
||||
Some(info)
|
||||
}
|
||||
Operation::LoadConstant => {
|
||||
let constant_index = self.first_argument();
|
||||
|
||||
if let Some(chunk) = chunk {
|
||||
match chunk.get_constant(constant_index, Span(0, 0)) {
|
||||
Ok(value) => {
|
||||
format!("R{} = C{} {}", self.destination(), constant_index, value)
|
||||
}
|
||||
Err(error) => {
|
||||
format!("R{} = C{} {:?}", self.destination(), constant_index, error)
|
||||
}
|
||||
Ok(value) => Some(format!(
|
||||
"R{} = C{} {}",
|
||||
self.destination(),
|
||||
constant_index,
|
||||
value
|
||||
)),
|
||||
Err(error) => Some(format!(
|
||||
"R{} = C{} {:?}",
|
||||
self.destination(),
|
||||
constant_index,
|
||||
error
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
format!("R{} = C{}", self.destination(), constant_index)
|
||||
Some(format!("R{} = C{}", self.destination(), constant_index))
|
||||
}
|
||||
}
|
||||
Operation::LoadList => {
|
||||
@ -367,7 +377,10 @@ impl Instruction {
|
||||
let first_index = self.first_argument();
|
||||
let last_index = destination.saturating_sub(1);
|
||||
|
||||
format!("R{} = [R{}..=R{}]", destination, first_index, last_index)
|
||||
Some(format!(
|
||||
"R{} = [R{}..=R{}]",
|
||||
destination, first_index, last_index
|
||||
))
|
||||
}
|
||||
Operation::DefineLocal => {
|
||||
let destination = self.destination();
|
||||
@ -386,12 +399,14 @@ impl Instruction {
|
||||
""
|
||||
};
|
||||
|
||||
format!("L{local_index} = R{destination} {mutable_display}{identifier_display}")
|
||||
Some(format!(
|
||||
"L{local_index} = R{destination} {mutable_display}{identifier_display}"
|
||||
))
|
||||
}
|
||||
Operation::GetLocal => {
|
||||
let local_index = self.first_argument();
|
||||
|
||||
format!("R{} = L{}", self.destination(), local_index)
|
||||
Some(format!("R{} = L{}", self.destination(), local_index))
|
||||
}
|
||||
Operation::SetLocal => {
|
||||
let local_index = self.first_argument();
|
||||
@ -404,49 +419,61 @@ impl Instruction {
|
||||
"???".to_string()
|
||||
};
|
||||
|
||||
format!(
|
||||
Some(format!(
|
||||
"L{} = R{} {}",
|
||||
local_index,
|
||||
self.destination(),
|
||||
identifier_display
|
||||
)
|
||||
))
|
||||
}
|
||||
Operation::Add => {
|
||||
let destination = self.destination();
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
|
||||
format!("R{destination} = {first_argument} + {second_argument}",)
|
||||
Some(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}",)
|
||||
Some(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}",)
|
||||
Some(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}",)
|
||||
Some(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}",)
|
||||
Some(format!(
|
||||
"R{destination} = {first_argument} % {second_argument}",
|
||||
))
|
||||
}
|
||||
Operation::Test => {
|
||||
let destination = self.destination();
|
||||
let test_value = self.second_argument_as_boolean();
|
||||
let bang = if test_value { "" } else { "!" };
|
||||
|
||||
format!("if {bang}R{destination} {{ IP++ }}",)
|
||||
jump_offset = Some(1);
|
||||
|
||||
Some(format!("if {bang}R{destination}",))
|
||||
}
|
||||
Operation::TestSet => {
|
||||
let destination = self.destination();
|
||||
@ -454,9 +481,11 @@ impl Instruction {
|
||||
let test_value = self.second_argument_as_boolean();
|
||||
let bang = if test_value { "" } else { "!" };
|
||||
|
||||
format!(
|
||||
"if {bang}R{destination} {{ R{destination} = R{argument} }} else {{ IP++ }}",
|
||||
)
|
||||
jump_offset = Some(1);
|
||||
|
||||
Some(format!(
|
||||
"if {bang}R{destination} {{ R{destination} = R{argument} }}",
|
||||
))
|
||||
}
|
||||
Operation::Equal => {
|
||||
let comparison_symbol = if self.destination_as_boolean() {
|
||||
@ -466,8 +495,11 @@ impl Instruction {
|
||||
};
|
||||
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
jump_offset = Some(1);
|
||||
|
||||
format!("if {first_argument} {comparison_symbol} {second_argument} IP++",)
|
||||
Some(format!(
|
||||
"if {first_argument} {comparison_symbol} {second_argument} {{ JUMP }}",
|
||||
))
|
||||
}
|
||||
Operation::Less => {
|
||||
let comparison_symbol = if self.destination_as_boolean() {
|
||||
@ -476,8 +508,11 @@ impl Instruction {
|
||||
">="
|
||||
};
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
jump_offset = Some(1);
|
||||
|
||||
format!("if {first_argument} {comparison_symbol} {second_argument} IP++",)
|
||||
Some(format!(
|
||||
"if {first_argument} {comparison_symbol} {second_argument}",
|
||||
))
|
||||
}
|
||||
Operation::LessEqual => {
|
||||
let comparison_symbol = if self.destination_as_boolean() {
|
||||
@ -486,8 +521,11 @@ impl Instruction {
|
||||
">"
|
||||
};
|
||||
let (first_argument, second_argument) = format_arguments();
|
||||
jump_offset = Some(1);
|
||||
|
||||
format!("if {first_argument} {comparison_symbol} {second_argument} IP++",)
|
||||
Some(format!(
|
||||
"if {first_argument} {comparison_symbol} {second_argument}",
|
||||
))
|
||||
}
|
||||
Operation::Negate => {
|
||||
let destination = self.destination();
|
||||
@ -497,7 +535,7 @@ impl Instruction {
|
||||
format!("R{}", self.first_argument())
|
||||
};
|
||||
|
||||
format!("R{destination} = -{argument}")
|
||||
Some(format!("R{destination} = -{argument}"))
|
||||
}
|
||||
Operation::Not => {
|
||||
let destination = self.destination();
|
||||
@ -507,44 +545,38 @@ impl Instruction {
|
||||
format!("R{}", self.first_argument())
|
||||
};
|
||||
|
||||
format!("R{destination} = !{argument}")
|
||||
Some(format!("R{destination} = !{argument}"))
|
||||
}
|
||||
Operation::Jump => {
|
||||
let offset = self.first_argument();
|
||||
let positive = self.second_argument() != 0;
|
||||
let offset = self.first_argument() as isize;
|
||||
let is_positive = self.second_argument_as_boolean();
|
||||
|
||||
if positive {
|
||||
format!("IP += {}", offset)
|
||||
if is_positive {
|
||||
jump_offset = Some(offset);
|
||||
} else {
|
||||
format!("IP -= {}", offset)
|
||||
jump_offset = Some(-offset);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
Operation::Return => {
|
||||
let from_register = self.destination();
|
||||
let to_register = self.first_argument();
|
||||
|
||||
format!("R{from_register}..=R{to_register}")
|
||||
Some(format!("R{from_register}..=R{to_register}"))
|
||||
}
|
||||
Operation::End => {
|
||||
let return_value = self.destination_as_boolean();
|
||||
|
||||
if return_value {
|
||||
Some("return".to_string())
|
||||
} else {
|
||||
Some("null".to_string())
|
||||
}
|
||||
}
|
||||
};
|
||||
let trucated_length = 30;
|
||||
let with_elipsis = trucated_length - 3;
|
||||
let truncated_info = if info.len() > with_elipsis {
|
||||
format!("{info:.<trucated_length$.with_elipsis$}")
|
||||
} else {
|
||||
info
|
||||
};
|
||||
|
||||
Some(truncated_info)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Instruction {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
if let Some(info) = self.disassembly_info(None) {
|
||||
write!(f, "{} {}", self.operation(), info)
|
||||
} else {
|
||||
write!(f, "{}", self.operation())
|
||||
}
|
||||
(info, jump_offset)
|
||||
}
|
||||
}
|
||||
|
||||
@ -755,4 +787,12 @@ mod tests {
|
||||
assert_eq!(instruction.destination(), 4);
|
||||
assert_eq!(instruction.first_argument(), 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn end() {
|
||||
let instruction = Instruction::end(true);
|
||||
|
||||
assert_eq!(instruction.operation(), Operation::End);
|
||||
assert!(instruction.destination_as_boolean());
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ const NOT: u8 = 0b0001_0011;
|
||||
|
||||
const JUMP: u8 = 0b0001_0100;
|
||||
const RETURN: u8 = 0b0001_0101;
|
||||
const END: u8 = 0b0001_0110;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Operation {
|
||||
@ -69,10 +70,11 @@ pub enum Operation {
|
||||
// Control flow
|
||||
Jump = JUMP as isize,
|
||||
Return = RETURN as isize,
|
||||
End = END as isize,
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
pub fn is_binary(&self) -> bool {
|
||||
pub fn is_math(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Operation::Add
|
||||
@ -109,11 +111,12 @@ impl From<u8> for Operation {
|
||||
NOT => Operation::Not,
|
||||
JUMP => Operation::Jump,
|
||||
RETURN => Operation::Return,
|
||||
END => Operation::End,
|
||||
_ => {
|
||||
if cfg!(test) {
|
||||
panic!("Invalid operation byte: {}", byte)
|
||||
} else {
|
||||
Operation::Return
|
||||
Operation::End
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -145,6 +148,7 @@ impl From<Operation> for u8 {
|
||||
Operation::Not => NOT,
|
||||
Operation::Jump => JUMP,
|
||||
Operation::Return => RETURN,
|
||||
Operation::End => END,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -174,6 +178,7 @@ impl Display for Operation {
|
||||
Operation::Not => write!(f, "NOT"),
|
||||
Operation::Jump => write!(f, "JUMP"),
|
||||
Operation::Return => write!(f, "RETURN"),
|
||||
Operation::End => write!(f, "END"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -375,7 +375,10 @@ impl<'src> Parser<'src> {
|
||||
let operator_position = self.current_position;
|
||||
let rule = ParseRule::from(&operator.kind());
|
||||
|
||||
let mut instruction = match operator.kind() {
|
||||
self.advance()?;
|
||||
self.parse(rule.precedence.increment())?;
|
||||
|
||||
let mut new_instruction = match operator.kind() {
|
||||
TokenKind::Plus => Instruction::add(self.current_register, left, 0),
|
||||
TokenKind::Minus => Instruction::subtract(self.current_register, left, 0),
|
||||
TokenKind::Star => Instruction::multiply(self.current_register, left, 0),
|
||||
@ -397,33 +400,59 @@ impl<'src> Parser<'src> {
|
||||
};
|
||||
|
||||
self.increment_register()?;
|
||||
self.advance()?;
|
||||
self.parse(rule.precedence.increment())?;
|
||||
|
||||
let (right_instruction, right_position) =
|
||||
self.chunk.pop_instruction(self.current_position)?;
|
||||
let (push_back_right, right_is_constant, right) =
|
||||
self.handle_binary_argument(&right_instruction)?;
|
||||
|
||||
instruction.set_second_argument(right);
|
||||
new_instruction.set_second_argument(right);
|
||||
|
||||
if left_is_constant {
|
||||
instruction.set_first_argument_to_constant();
|
||||
new_instruction.set_first_argument_to_constant();
|
||||
}
|
||||
|
||||
if right_is_constant {
|
||||
instruction.set_second_argument_to_constant();
|
||||
new_instruction.set_second_argument_to_constant();
|
||||
}
|
||||
|
||||
if push_back_left {
|
||||
self.emit_instruction(left_instruction, left_position);
|
||||
let mut instructions = if !push_back_left && !push_back_right {
|
||||
self.emit_instruction(new_instruction, operator_position);
|
||||
|
||||
return Ok(());
|
||||
} else if push_back_right && !push_back_left {
|
||||
vec![
|
||||
(right_instruction, right_position),
|
||||
(new_instruction, operator_position),
|
||||
]
|
||||
} else if push_back_left && !push_back_right {
|
||||
vec![
|
||||
(left_instruction, left_position),
|
||||
(new_instruction, operator_position),
|
||||
]
|
||||
} else {
|
||||
vec![
|
||||
(new_instruction, operator_position),
|
||||
(left_instruction, left_position),
|
||||
(right_instruction, right_position),
|
||||
]
|
||||
};
|
||||
|
||||
while let Some(operation) = self.chunk.get_last_operation() {
|
||||
if operation.is_math() {
|
||||
let (instruction, position) = self.chunk.pop_instruction(self.current_position)?;
|
||||
|
||||
instructions.push((instruction, position));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if push_back_right {
|
||||
self.emit_instruction(right_instruction, right_position);
|
||||
}
|
||||
instructions.sort_by_key(|(instruction, _)| instruction.destination());
|
||||
|
||||
self.emit_instruction(instruction, operator_position);
|
||||
for (instruction, position) in instructions {
|
||||
self.emit_instruction(instruction, position);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -501,26 +530,19 @@ impl<'src> Parser<'src> {
|
||||
if push_back_right {
|
||||
self.emit_instruction(right_instruction, right_position);
|
||||
}
|
||||
|
||||
self.emit_instruction(instruction, operator_position);
|
||||
self.emit_instruction(Instruction::jump(1, true), operator_position);
|
||||
self.emit_instruction(
|
||||
Instruction::r#move(self.current_register, instruction.destination()),
|
||||
operator_position,
|
||||
);
|
||||
} else {
|
||||
self.emit_instruction(instruction, operator_position);
|
||||
self.emit_instruction(Instruction::jump(1, true), operator_position);
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, true, true),
|
||||
operator_position,
|
||||
);
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, false, false),
|
||||
operator_position,
|
||||
);
|
||||
}
|
||||
|
||||
self.emit_instruction(instruction, operator_position);
|
||||
self.emit_instruction(Instruction::jump(1, true), operator_position);
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, true, true),
|
||||
operator_position,
|
||||
);
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, false, false),
|
||||
operator_position,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -549,13 +571,9 @@ impl<'src> Parser<'src> {
|
||||
self.advance()?;
|
||||
self.parse(rule.precedence.increment())?;
|
||||
|
||||
let (mut right_instruction, right_position) =
|
||||
let (right_instruction, right_position) =
|
||||
self.chunk.pop_instruction(self.current_position)?;
|
||||
|
||||
if let Operation::LoadBoolean = right_instruction.operation() {
|
||||
right_instruction.set_second_argument_to_boolean(true);
|
||||
}
|
||||
|
||||
self.emit_instruction(left_instruction, left_position);
|
||||
self.emit_instruction(instruction, operator_position);
|
||||
self.emit_instruction(Instruction::jump(1, true), operator_position);
|
||||
@ -591,7 +609,7 @@ impl<'src> Parser<'src> {
|
||||
let (mut previous_instruction, previous_position) =
|
||||
self.chunk.pop_instruction(self.current_position)?;
|
||||
|
||||
if previous_instruction.operation().is_binary() {
|
||||
if previous_instruction.operation().is_math() {
|
||||
let previous_register = self
|
||||
.chunk
|
||||
.get_local(local_index, start_position)?
|
||||
@ -646,13 +664,13 @@ impl<'src> Parser<'src> {
|
||||
fn parse_block(
|
||||
&mut self,
|
||||
_allow_assignment: bool,
|
||||
allow_return: bool,
|
||||
_allow_return: bool,
|
||||
) -> Result<(), ParseError> {
|
||||
self.advance()?;
|
||||
self.chunk.begin_scope();
|
||||
|
||||
while !self.allow(TokenKind::RightCurlyBrace)? && !self.is_eof() {
|
||||
self.parse_statement(allow_return)?;
|
||||
self.parse_statement(_allow_return)?;
|
||||
}
|
||||
|
||||
self.chunk.end_scope();
|
||||
@ -712,43 +730,20 @@ impl<'src> Parser<'src> {
|
||||
self.advance()?;
|
||||
self.parse_expression()?;
|
||||
|
||||
if self.allow(TokenKind::LeftCurlyBrace)? {
|
||||
if let Token::LeftCurlyBrace = self.current_token {
|
||||
self.parse_block(allow_assignment, allow_return)?;
|
||||
}
|
||||
|
||||
let jump_position = self.current_position;
|
||||
let jump_start = self.current_register;
|
||||
let jump_index = self.chunk.len();
|
||||
|
||||
if self.allow(TokenKind::Else)? {
|
||||
if self.allow(TokenKind::If)? {
|
||||
if let Token::If = self.current_token {
|
||||
self.parse_if(allow_assignment, allow_return)?;
|
||||
}
|
||||
|
||||
if self.allow(TokenKind::LeftCurlyBrace)? {
|
||||
if let Token::LeftCurlyBrace = self.current_token {
|
||||
self.parse_block(allow_assignment, allow_return)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let [Some(Operation::LoadBoolean), Some(Operation::LoadBoolean)] =
|
||||
self.chunk.get_last_n_operations()
|
||||
{
|
||||
// Do not emit a jump if the last two instructions were LoadBoolean operations.
|
||||
} else if let [Some(Operation::LoadConstant), Some(Operation::LoadConstant)] =
|
||||
self.chunk.get_last_n_operations()
|
||||
{
|
||||
// Do not emit a jump if the last two instructions were LoadConstant operations.
|
||||
} else {
|
||||
let jump_end = self.current_register;
|
||||
let jump_distance = (jump_end - jump_start).max(1);
|
||||
let jump = Instruction::jump(jump_distance, true);
|
||||
|
||||
if jump_distance > 1 {
|
||||
self.chunk
|
||||
.insert_instruction(jump_index, jump, jump_position);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -769,19 +764,27 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
fn parse_statement(&mut self, allow_return: bool) -> Result<(), ParseError> {
|
||||
match self.current_token {
|
||||
let is_expression = match self.current_token {
|
||||
Token::Let => {
|
||||
self.parse_let_statement(true, allow_return)?;
|
||||
|
||||
self.allow(TokenKind::Semicolon)?
|
||||
}
|
||||
Token::LeftCurlyBrace => {
|
||||
self.parse_block(true, true)?;
|
||||
|
||||
!self.allow(TokenKind::Semicolon)?
|
||||
}
|
||||
_ => {
|
||||
self.parse_expression()?;
|
||||
|
||||
!self.allow(TokenKind::Semicolon)?
|
||||
}
|
||||
};
|
||||
|
||||
self.allow(TokenKind::Semicolon)?;
|
||||
if self.current_token == Token::RightCurlyBrace || self.is_eof() {
|
||||
self.emit_instruction(Instruction::end(is_expression), self.current_position);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ fn not() {
|
||||
vec![
|
||||
(Instruction::load_boolean(0, true, false), Span(1, 5)),
|
||||
(Instruction::not(1, 0), Span(0, 1)),
|
||||
(Instruction::end(true), Span(5, 5)),
|
||||
],
|
||||
vec![],
|
||||
vec![]
|
||||
@ -26,10 +27,13 @@ fn negate() {
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(Chunk::with_data(
|
||||
vec![(
|
||||
*Instruction::negate(0, 0).set_first_argument_to_constant(),
|
||||
Span(0, 1)
|
||||
),],
|
||||
vec![
|
||||
(
|
||||
*Instruction::negate(0, 0).set_first_argument_to_constant(),
|
||||
Span(0, 1)
|
||||
),
|
||||
(Instruction::end(true), Span(5, 5))
|
||||
],
|
||||
vec![Value::integer(42)],
|
||||
vec![]
|
||||
)),
|
||||
@ -53,6 +57,7 @@ fn greater_than_or_equal() {
|
||||
(Instruction::jump(1, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, true, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
||||
(Instruction::end(false), Span(7, 7)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
@ -77,6 +82,7 @@ fn greater() {
|
||||
(Instruction::jump(1, true), Span(2, 3)),
|
||||
(Instruction::load_boolean(0, true, true), Span(2, 3)),
|
||||
(Instruction::load_boolean(0, false, false), Span(2, 3)),
|
||||
(Instruction::end(false), Span(6, 6)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
@ -101,6 +107,7 @@ fn less_than_or_equal() {
|
||||
(Instruction::jump(1, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, true, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
||||
(Instruction::end(false), Span(7, 7)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
@ -125,6 +132,7 @@ fn less_than() {
|
||||
(Instruction::jump(1, true), Span(2, 3)),
|
||||
(Instruction::load_boolean(0, true, true), Span(2, 3)),
|
||||
(Instruction::load_boolean(0, false, false), Span(2, 3)),
|
||||
(Instruction::end(false), Span(6, 6)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
@ -149,6 +157,7 @@ fn not_equal() {
|
||||
(Instruction::jump(1, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, true, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
||||
(Instruction::end(false), Span(7, 7)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
@ -173,6 +182,7 @@ fn equal() {
|
||||
(Instruction::jump(1, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, true, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
||||
(Instruction::end(true), Span(6, 6)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
@ -198,6 +208,7 @@ fn equality_assignment_long() {
|
||||
(Instruction::load_boolean(0, true, true), Span(13, 15)),
|
||||
(Instruction::load_boolean(0, false, false), Span(13, 15)),
|
||||
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
||||
(Instruction::end(false), Span(42, 42)),
|
||||
],
|
||||
vec![Value::integer(4), Value::integer(4)],
|
||||
vec![Local::new(Identifier::new("a"), false, 0, Some(0))]
|
||||
@ -223,6 +234,7 @@ fn equality_assignment_short() {
|
||||
(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)),
|
||||
(Instruction::end(false), Span(15, 15)),
|
||||
],
|
||||
vec![Value::integer(4), Value::integer(4)],
|
||||
vec![Local::new(Identifier::new("a"), false, 0, Some(0))]
|
||||
@ -247,6 +259,7 @@ fn if_expression() {
|
||||
(Instruction::jump(1, true), Span(5, 7)),
|
||||
(Instruction::load_boolean(0, true, true), Span(5, 7)),
|
||||
(Instruction::load_boolean(0, false, false), Span(5, 7)),
|
||||
(Instruction::end(true), Span(15, 15)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(1)],
|
||||
vec![]
|
||||
@ -271,6 +284,7 @@ fn if_else_expression() {
|
||||
(Instruction::jump(1, true), Span(5, 7)),
|
||||
(Instruction::load_boolean(0, true, true), Span(5, 7)),
|
||||
(Instruction::load_boolean(0, false, false), Span(5, 7)),
|
||||
(Instruction::end(true), Span(26, 26)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(1)],
|
||||
vec![]
|
||||
@ -295,6 +309,7 @@ fn list_with_expression() {
|
||||
),
|
||||
(Instruction::load_constant(2, 3), Span(11, 12)),
|
||||
(Instruction::load_list(3, 0, 3), Span(0, 13)),
|
||||
(Instruction::end(true), Span(13, 13)),
|
||||
],
|
||||
vec![
|
||||
Value::integer(1),
|
||||
@ -319,6 +334,7 @@ fn list() {
|
||||
(Instruction::load_constant(1, 1), Span(4, 5)),
|
||||
(Instruction::load_constant(2, 2), Span(7, 8)),
|
||||
(Instruction::load_list(3, 0, 3), Span(0, 9)),
|
||||
(Instruction::end(true), Span(9, 9)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2), Value::integer(3),],
|
||||
vec![]
|
||||
@ -350,10 +366,13 @@ fn block_scope() {
|
||||
(Instruction::define_local(1, 1, false), Span(46, 47)),
|
||||
(Instruction::load_constant(2, 2), Span(92, 93)),
|
||||
(Instruction::define_local(2, 2, false), Span(88, 89)),
|
||||
(Instruction::end(false), Span(121, 124)),
|
||||
(Instruction::load_constant(3, 3), Span(129, 130)),
|
||||
(Instruction::define_local(3, 3, false), Span(125, 126)),
|
||||
(Instruction::end(false), Span(150, 159)),
|
||||
(Instruction::load_constant(4, 4), Span(158, 159)),
|
||||
(Instruction::define_local(4, 4, false), Span(154, 155)),
|
||||
(Instruction::end(false), Span(165, 165)),
|
||||
],
|
||||
vec![
|
||||
Value::integer(0),
|
||||
@ -388,6 +407,7 @@ fn set_local() {
|
||||
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||
(Instruction::load_constant(1, 1), Span(20, 22)),
|
||||
(Instruction::set_local(1, 0), Span(16, 17)),
|
||||
(Instruction::end(false), Span(23, 23)),
|
||||
],
|
||||
vec![Value::integer(41), Value::integer(42)],
|
||||
vec![Local::new(Identifier::new("x"), true, 0, Some(0)),]
|
||||
@ -411,6 +431,7 @@ fn parentheses_precedence() {
|
||||
*Instruction::multiply(1, 0, 2).set_second_argument_to_constant(),
|
||||
Span(8, 9)
|
||||
),
|
||||
(Instruction::end(true), Span(11, 11)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2), Value::integer(3)],
|
||||
vec![]
|
||||
@ -424,12 +445,6 @@ fn math_operator_precedence() {
|
||||
parse("1 + 2 - 3 * 4 / 5"),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(
|
||||
*Instruction::multiply(2, 2, 3)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(10, 11)
|
||||
),
|
||||
(
|
||||
*Instruction::add(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
@ -437,10 +452,17 @@ fn math_operator_precedence() {
|
||||
Span(2, 3)
|
||||
),
|
||||
(
|
||||
*Instruction::divide(3, 2, 4).set_second_argument_to_constant(),
|
||||
*Instruction::multiply(1, 2, 3)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(10, 11)
|
||||
),
|
||||
(
|
||||
*Instruction::divide(2, 1, 4).set_second_argument_to_constant(),
|
||||
Span(14, 15)
|
||||
),
|
||||
(Instruction::subtract(1, 0, 3), Span(6, 7)),
|
||||
(Instruction::subtract(3, 0, 2), Span(6, 7)),
|
||||
(Instruction::end(true), Span(17, 17)),
|
||||
],
|
||||
vec![
|
||||
Value::integer(1),
|
||||
@ -455,13 +477,14 @@ fn math_operator_precedence() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declare_local() {
|
||||
fn define_local() {
|
||||
assert_eq!(
|
||||
parse("let x = 42;"),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::load_constant(0, 0), Span(8, 10)),
|
||||
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
||||
(Instruction::end(false), Span(11, 11)),
|
||||
],
|
||||
vec![Value::integer(42)],
|
||||
vec![Local::new(Identifier::new("x"), false, 0, Some(0))]
|
||||
@ -478,7 +501,8 @@ fn or() {
|
||||
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
||||
(Instruction::test(0, false), Span(5, 7)),
|
||||
(Instruction::jump(1, true), Span(5, 7)),
|
||||
(Instruction::load_boolean(1, false, true), Span(8, 13)),
|
||||
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
||||
(Instruction::end(true), Span(13, 13)),
|
||||
],
|
||||
vec![],
|
||||
vec![]
|
||||
@ -495,7 +519,8 @@ fn and() {
|
||||
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
||||
(Instruction::test(0, true), Span(5, 7)),
|
||||
(Instruction::jump(1, true), Span(5, 7)),
|
||||
(Instruction::load_boolean(1, false, true), Span(8, 13)),
|
||||
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
||||
(Instruction::end(true), Span(13, 13)),
|
||||
],
|
||||
vec![],
|
||||
vec![]
|
||||
@ -517,6 +542,7 @@ fn variable_and() {
|
||||
(Instruction::test(2, true), Span(31, 33)),
|
||||
(Instruction::jump(1, true), Span(31, 33)),
|
||||
(Instruction::get_local(3, 1), Span(34, 35)),
|
||||
(Instruction::end(true), Span(35, 35)),
|
||||
],
|
||||
vec![],
|
||||
vec![
|
||||
@ -532,12 +558,15 @@ fn divide() {
|
||||
assert_eq!(
|
||||
parse("1 / 2"),
|
||||
Ok(Chunk::with_data(
|
||||
vec![(
|
||||
*Instruction::divide(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),],
|
||||
vec![
|
||||
(
|
||||
*Instruction::divide(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),
|
||||
(Instruction::end(true), Span(5, 5)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
))
|
||||
@ -549,12 +578,15 @@ fn multiply() {
|
||||
assert_eq!(
|
||||
parse("1 * 2"),
|
||||
Ok(Chunk::with_data(
|
||||
vec![(
|
||||
*Instruction::multiply(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),],
|
||||
vec![
|
||||
(
|
||||
*Instruction::multiply(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),
|
||||
(Instruction::end(true), Span(5, 5)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
))
|
||||
@ -566,12 +598,15 @@ fn add() {
|
||||
assert_eq!(
|
||||
parse("1 + 2"),
|
||||
Ok(Chunk::with_data(
|
||||
vec![(
|
||||
*Instruction::add(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),],
|
||||
vec![
|
||||
(
|
||||
*Instruction::add(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),
|
||||
(Instruction::end(true), Span(5, 5)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
))
|
||||
@ -583,12 +618,15 @@ fn subtract() {
|
||||
assert_eq!(
|
||||
parse("1 - 2"),
|
||||
Ok(Chunk::with_data(
|
||||
vec![(
|
||||
*Instruction::subtract(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),],
|
||||
vec![
|
||||
(
|
||||
*Instruction::subtract(0, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),
|
||||
(Instruction::end(true), Span(5, 5)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
))
|
||||
@ -600,7 +638,10 @@ fn constant() {
|
||||
assert_eq!(
|
||||
parse("42"),
|
||||
Ok(Chunk::with_data(
|
||||
vec![(Instruction::load_constant(0, 0), Span(0, 2)),],
|
||||
vec![
|
||||
(Instruction::load_constant(0, 0), Span(0, 2)),
|
||||
(Instruction::end(true), Span(2, 2)),
|
||||
],
|
||||
vec![Value::integer(42)],
|
||||
vec![]
|
||||
))
|
||||
|
@ -16,7 +16,6 @@ pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||
pub struct Vm {
|
||||
chunk: Chunk,
|
||||
ip: usize,
|
||||
last_modified: u8,
|
||||
register_stack: Vec<Option<Value>>,
|
||||
}
|
||||
|
||||
@ -27,7 +26,6 @@ impl Vm {
|
||||
Self {
|
||||
chunk,
|
||||
ip: 0,
|
||||
last_modified: 0,
|
||||
register_stack: Vec::new(),
|
||||
}
|
||||
}
|
||||
@ -60,7 +58,11 @@ impl Vm {
|
||||
}
|
||||
|
||||
while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() {
|
||||
log::trace!("Running IP {} {instruction} at {position}", self.ip);
|
||||
log::trace!(
|
||||
"Running IP {} {} at {position}",
|
||||
self.ip,
|
||||
instruction.operation()
|
||||
);
|
||||
|
||||
match instruction.operation() {
|
||||
Operation::Move => {
|
||||
@ -302,20 +304,25 @@ impl Vm {
|
||||
return Ok(Some(self.take(start_register, position)?));
|
||||
}
|
||||
}
|
||||
Operation::End => {
|
||||
let returns_value = instruction.destination_as_boolean();
|
||||
|
||||
return if returns_value {
|
||||
Ok(Some(self.pop(position)?))
|
||||
} else {
|
||||
Ok(None)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let final_value = self.take(self.last_modified, Span(0, 0))?;
|
||||
|
||||
Ok(Some(final_value))
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn insert(&mut self, value: Value, index: u8, position: Span) -> Result<(), VmError> {
|
||||
if self.register_stack.len() == Self::STACK_LIMIT {
|
||||
Err(VmError::StackOverflow { position })
|
||||
} else {
|
||||
self.last_modified = index;
|
||||
|
||||
let index = index as usize;
|
||||
|
||||
while index >= self.register_stack.len() {
|
||||
|
@ -5,7 +5,7 @@ fn if_expression() {
|
||||
assert_eq!(run("if true { 1 }"), Ok(Some(Value::integer(1))));
|
||||
assert_eq!(
|
||||
run("if 42 == 42 { 1 } else { 2 }"),
|
||||
Ok(Some(Value::integer(2)))
|
||||
Ok(Some(Value::integer(1)))
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user