From 3df42f6a4761bfe68485f04031fe46f81ae093dd Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 24 Sep 2024 08:29:33 -0400 Subject: [PATCH] Refine equality implementation --- dust-lang/src/chunk.rs | 31 +++--- dust-lang/src/instruction.rs | 180 +++++++++++++++++++++------------- dust-lang/src/operation.rs | 9 +- dust-lang/src/parser/mod.rs | 135 ++++++++++++------------- dust-lang/src/parser/tests.rs | 121 +++++++++++++++-------- dust-lang/src/vm.rs | 23 +++-- dust-lang/tests/operations.rs | 2 +- 7 files changed, 302 insertions(+), 199 deletions(-) diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 8fa79a7..151472a 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -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)); } diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index 97ac855..17ea06a 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -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 { + pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> (Option, Option) { 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:. 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()); + } } diff --git a/dust-lang/src/operation.rs b/dust-lang/src/operation.rs index 7418d8d..08e80d1 100644 --- a/dust-lang/src/operation.rs +++ b/dust-lang/src/operation.rs @@ -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 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 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"), } } } diff --git a/dust-lang/src/parser/mod.rs b/dust-lang/src/parser/mod.rs index a0e96d5..643c2b9 100644 --- a/dust-lang/src/parser/mod.rs +++ b/dust-lang/src/parser/mod.rs @@ -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(()) } diff --git a/dust-lang/src/parser/tests.rs b/dust-lang/src/parser/tests.rs index 3f0d13c..c9ffd58 100644 --- a/dust-lang/src/parser/tests.rs +++ b/dust-lang/src/parser/tests.rs @@ -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![] )) diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 53371cf..7d156ad 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -16,7 +16,6 @@ pub fn run(source: &str) -> Result, DustError> { pub struct Vm { chunk: Chunk, ip: usize, - last_modified: u8, register_stack: Vec>, } @@ -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() { diff --git a/dust-lang/tests/operations.rs b/dust-lang/tests/operations.rs index b2fa42d..5f59a00 100644 --- a/dust-lang/tests/operations.rs +++ b/dust-lang/tests/operations.rs @@ -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))) ); }