1
0

Refine equality implementation

This commit is contained in:
Jeff 2024-09-24 08:29:33 -04:00
parent 1d03876b89
commit 3df42f6a47
7 changed files with 302 additions and 199 deletions

View File

@ -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(&center(&instruction_display));
}

View File

@ -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());
}
}

View File

@ -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"),
}
}
}

View File

@ -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(())
}

View File

@ -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![]
))

View File

@ -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() {

View File

@ -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)))
);
}