From d0d80cf4078da44bf780bf030832e0d9c51025cf Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 25 Sep 2024 09:55:10 -0400 Subject: [PATCH] Use test for single registers in conditional expressions --- dust-lang/src/chunk.rs | 14 ++++ dust-lang/src/instruction.rs | 50 ++++-------- dust-lang/src/operation.rs | 7 +- dust-lang/src/parser/mod.rs | 109 +++++++++++++++---------- dust-lang/src/parser/tests.rs | 143 +++++++++++---------------------- dust-lang/src/vm.rs | 24 +++--- dust-lang/tests/expressions.rs | 8 ++ 7 files changed, 165 insertions(+), 190 deletions(-) diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 89bdd27..b110ff4 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -87,6 +87,20 @@ impl Chunk { Ok((instruction, position)) } + pub fn find_last_instruction(&mut self, operation: Operation) -> Option { + self.instructions + .iter() + .enumerate() + .rev() + .find_map(|(index, (instruction, _))| { + if instruction.operation() == operation { + Some(index) + } else { + None + } + }) + } + pub fn get_last_operation(&self) -> Result { self.get_last_instruction() .map(|(instruction, _)| instruction.operation()) diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index a29d2a9..7c3df9b 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -32,11 +32,12 @@ impl Instruction { instruction } - pub fn load_constant(to_register: u8, constant_index: u8) -> Instruction { + pub fn load_constant(to_register: u8, constant_index: u8, skip: bool) -> Instruction { let mut instruction = Instruction(Operation::LoadConstant as u32); instruction.set_a(to_register); instruction.set_b(constant_index); + instruction.set_c_to_boolean(skip); instruction } @@ -214,14 +215,6 @@ impl Instruction { instruction } - pub fn end(returns_value: bool) -> Instruction { - let mut instruction = Instruction(Operation::End as u32); - - instruction.set_a_to_boolean(returns_value); - - instruction - } - pub fn operation(&self) -> Operation { Operation::from((self.0 & 0b0000_0000_0011_1111) as u8) } @@ -355,18 +348,21 @@ impl Instruction { Some(info) } Operation::LoadConstant => { + let register_index = self.a(); let constant_index = self.b(); - - if let Some(chunk) = chunk { + let jump = if self.c_as_boolean() { "&& JUMP" } else { "" }; + let constant = if let Some(chunk) = chunk { match chunk.get_constant(constant_index, Span(0, 0)) { - Ok(value) => Some(format!("R{} = C{} {}", self.a(), constant_index, value)), - Err(error) => { - Some(format!("R{} = C{} {:?}", self.a(), constant_index, error)) - } + Ok(constant) => constant.to_string(), + Err(error) => format!("{error:?}"), } } else { - Some(format!("R{} = C{}", self.a(), constant_index)) - } + "???".to_string() + }; + + Some(format!( + "R{register_index} = C{constant_index} ({constant}) {jump}", + )) } Operation::LoadList => { let destination = self.a(); @@ -541,15 +537,6 @@ impl Instruction { Some(format!("R{from_register}..=R{to_register}")) } - Operation::End => { - let return_value = self.a_as_boolean(); - - if return_value { - Some("return".to_string()) - } else { - Some("null".to_string()) - } - } }; (info, jump_offset) @@ -601,7 +588,7 @@ mod tests { #[test] fn load_constant() { - let mut instruction = Instruction::load_constant(0, 1); + let mut instruction = Instruction::load_constant(0, 1, true); instruction.set_b_is_constant(); instruction.set_c_is_constant(); @@ -611,6 +598,7 @@ mod tests { assert_eq!(instruction.b(), 1); assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant()); + assert!(instruction.c_as_boolean()); } #[test] @@ -763,12 +751,4 @@ mod tests { assert_eq!(instruction.a(), 4); assert_eq!(instruction.b(), 8); } - - #[test] - fn end() { - let instruction = Instruction::end(true); - - assert_eq!(instruction.operation(), Operation::End); - assert!(instruction.a_as_boolean()); - } } diff --git a/dust-lang/src/operation.rs b/dust-lang/src/operation.rs index 08e80d1..6343f69 100644 --- a/dust-lang/src/operation.rs +++ b/dust-lang/src/operation.rs @@ -29,7 +29,6 @@ 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 { @@ -70,7 +69,6 @@ pub enum Operation { // Control flow Jump = JUMP as isize, Return = RETURN as isize, - End = END as isize, } impl Operation { @@ -111,12 +109,11 @@ 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::End + Operation::Return } } } @@ -148,7 +145,6 @@ impl From for u8 { Operation::Not => NOT, Operation::Jump => JUMP, Operation::Return => RETURN, - Operation::End => END, } } } @@ -178,7 +174,6 @@ 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 ffc7d3c..4714fc2 100644 --- a/dust-lang/src/parser/mod.rs +++ b/dust-lang/src/parser/mod.rs @@ -145,7 +145,7 @@ impl<'src> Parser<'src> { let constant_index = self.chunk.push_constant(value, position)?; self.emit_instruction( - Instruction::load_constant(self.current_register, constant_index), + Instruction::load_constant(self.current_register, constant_index, false), position, ); @@ -352,7 +352,16 @@ impl<'src> Parser<'src> { let mut push_back = false; let mut is_constant = false; let argument = match instruction.operation() { - Operation::GetLocal => instruction.a(), + Operation::GetLocal => { + let local_index = instruction.b(); + let local = self.chunk.get_local(local_index, self.current_position)?; + + if let Some(index) = local.register_index { + index + } else { + instruction.a() + } + } Operation::LoadConstant => { is_constant = true; @@ -492,12 +501,12 @@ impl<'src> Parser<'src> { let operator_position = self.current_position; let rule = ParseRule::from(&operator.kind()); let mut instruction = match self.current_token.kind() { - TokenKind::DoubleEqual => Instruction::equal(true, left, 0), - TokenKind::BangEqual => Instruction::equal(false, left, 0), - TokenKind::Less => Instruction::less(true, left, 0), - TokenKind::LessEqual => Instruction::less_equal(true, left, 0), - TokenKind::Greater => Instruction::less_equal(false, left, 0), - TokenKind::GreaterEqual => Instruction::less(false, left, 0), + TokenKind::DoubleEqual => Instruction::equal(true, left.saturating_sub(1), 0), + TokenKind::BangEqual => Instruction::equal(false, left.saturating_sub(1), 0), + TokenKind::Less => Instruction::less(true, left.saturating_sub(1), 0), + TokenKind::LessEqual => Instruction::less_equal(true, left.saturating_sub(1), 0), + TokenKind::Greater => Instruction::less_equal(false, left.saturating_sub(1), 0), + TokenKind::GreaterEqual => Instruction::less(false, left.saturating_sub(1), 0), _ => { return Err(ParseError::ExpectedTokenMultiple { expected: &[ @@ -532,14 +541,12 @@ impl<'src> Parser<'src> { instruction.set_c_is_constant(); } - if push_back_left || push_back_right { - if push_back_left { - self.emit_instruction(left_instruction, left_position); - } + if push_back_left { + self.emit_instruction(left_instruction, left_position); + } - if push_back_right { - self.emit_instruction(right_instruction, right_position); - } + if push_back_right { + self.emit_instruction(right_instruction, right_position); } self.emit_instruction(instruction, operator_position); @@ -735,18 +742,14 @@ impl<'src> Parser<'src> { self.advance()?; self.parse_expression()?; - let is_explicit_true = - matches!(self.previous_token, Token::Boolean("true")) && length == self.chunk.len() - 1; + let is_explicit_boolean = + matches!(self.previous_token, Token::Boolean(_)) && length == self.chunk.len() - 1; - if is_explicit_true { - let (mut load_boolean, load_boolean_position) = - self.chunk.pop_instruction(self.current_position)?; - - debug_assert_eq!(load_boolean.operation(), Operation::LoadBoolean); - - load_boolean.set_c_to_boolean(true); - self.emit_instruction(load_boolean, load_boolean_position); - self.increment_register()?; + if is_explicit_boolean { + self.emit_instruction( + Instruction::test(self.current_register, false), + self.current_position, + ); } let jump_position = if matches!( @@ -771,9 +774,26 @@ impl<'src> Parser<'src> { self.parse_block(allow_assignment, allow_return)?; } + if self.chunk.get_last_operation()? == Operation::LoadConstant + && self.current_token == Token::Else + { + let (mut load_constant, load_constant_position) = + self.chunk.pop_instruction(self.current_position)?; + + load_constant.set_c_to_boolean(true); + + self.emit_instruction(load_constant, load_constant_position); + } + let jump_end = self.chunk.len(); let jump_distance = jump_end.saturating_sub(jump_start); + self.chunk.insert_instruction( + jump_start, + Instruction::jump(jump_distance as u8, true), + jump_position, + ); + if self.allow(TokenKind::Else)? { if let Token::If = self.current_token { self.parse_if(allow_assignment, allow_return)?; @@ -784,12 +804,6 @@ impl<'src> Parser<'src> { } } - self.chunk.insert_instruction( - jump_start, - Instruction::jump(jump_distance as u8, true), - jump_position, - ); - Ok(()) } @@ -799,9 +813,28 @@ impl<'src> Parser<'src> { allow_return: bool, ) -> Result<(), ParseError> { self.advance()?; + + let jump_start = self.chunk.len(); + self.parse_expression()?; self.parse_block(allow_assignment, allow_return)?; + let jump_end = self.chunk.len() - 1; + let jump_distance = jump_end.saturating_sub(jump_start) as u8; + let jump_back = Instruction::jump(jump_distance, false); + let jump_over_index = self.chunk.find_last_instruction(Operation::Jump); + + if let Some(index) = jump_over_index { + let (mut jump_over, jump_over_position) = self.chunk.remove_instruction(index); + + jump_over.set_b(jump_distance); + self.chunk + .insert_instruction(index, jump_over, jump_over_position); + } + + self.chunk + .insert_instruction(jump_end, jump_back, self.current_position); + Ok(()) } @@ -810,27 +843,19 @@ impl<'src> Parser<'src> { } fn parse_statement(&mut self, allow_return: bool) -> Result<(), ParseError> { - let is_expression = match self.current_token { + 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)? } }; - if self.current_token == Token::RightCurlyBrace || self.is_eof() { - self.emit_instruction(Instruction::end(is_expression), self.current_position); - } + self.allow(TokenKind::Semicolon)?; Ok(()) } diff --git a/dust-lang/src/parser/tests.rs b/dust-lang/src/parser/tests.rs index 2370385..84a3b34 100644 --- a/dust-lang/src/parser/tests.rs +++ b/dust-lang/src/parser/tests.rs @@ -12,7 +12,6 @@ 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![] @@ -27,10 +26,7 @@ fn negate() { assert_eq!( parse(source), Ok(Chunk::with_data( - vec![ - (*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)), - (Instruction::end(true), Span(5, 5)) - ], + vec![(*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)),], vec![Value::integer(42)], vec![] )), @@ -54,7 +50,6 @@ 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![] @@ -79,7 +74,6 @@ 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![] @@ -104,7 +98,6 @@ 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![] @@ -129,7 +122,6 @@ 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![] @@ -154,7 +146,6 @@ 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![] @@ -179,7 +170,6 @@ 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![] @@ -201,16 +191,13 @@ fn equality_assignment_long() { .set_c_is_constant(), Span(13, 15) ), - (Instruction::jump(2, true), Span(13, 15)), + (Instruction::jump(1, true), Span(13, 15)), (Instruction::load_boolean(0, true, false), Span(20, 24)), - (Instruction::end(true), Span(25, 26)), (Instruction::load_boolean(0, false, false), Span(34, 39)), - (Instruction::end(true), Span(40, 41)), - (Instruction::define_local(1, 0, false), Span(4, 5)), - (Instruction::end(false), Span(42, 42)), + (Instruction::define_local(0, 0, false), Span(4, 5)), ], vec![Value::integer(4), Value::integer(4)], - vec![Local::new(Identifier::new("a"), false, 0, Some(1))] + vec![Local::new(Identifier::new("a"), false, 0, Some(0))] )), ); } @@ -233,7 +220,6 @@ 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))] @@ -255,10 +241,8 @@ fn if_expression() { .set_c_is_constant(), Span(5, 7) ), - (Instruction::jump(2, true), Span(5, 7)), - (Instruction::load_constant(0, 2), Span(12, 13)), - (Instruction::end(true), Span(14, 15)), - (Instruction::end(true), Span(15, 15)), + (Instruction::jump(1, true), Span(5, 7)), + (Instruction::load_constant(0, 2, false), Span(12, 13)), ], vec![Value::integer(1), Value::integer(1), Value::integer(2)], vec![] @@ -280,12 +264,9 @@ fn if_else_expression() { .set_c_is_constant(), Span(5, 7) ), - (Instruction::jump(2, true), Span(5, 7)), - (Instruction::load_constant(0, 2), Span(12, 13)), - (Instruction::end(true), Span(14, 15)), - (Instruction::load_constant(0, 3), Span(23, 24)), - (Instruction::end(true), Span(25, 26)), - (Instruction::end(true), Span(26, 26)), + (Instruction::jump(1, true), Span(5, 7)), + (Instruction::load_constant(0, 2, true), Span(12, 13)), + (Instruction::load_constant(0, 3, false), Span(23, 24)), ], vec![ Value::integer(1), @@ -306,7 +287,7 @@ fn list_with_complex_expression() { parse(source), Ok(Chunk::with_data( vec![ - (Instruction::load_constant(0, 0), Span(1, 2)), + (Instruction::load_constant(0, 0, false), Span(1, 2)), ( *Instruction::add(1, 1, 2) .set_b_is_constant() @@ -322,7 +303,6 @@ fn list_with_complex_expression() { (Instruction::subtract(3, 1, 2), Span(10, 11)), (Instruction::close(1, 3), Span(17, 18)), (Instruction::load_list(4, 0, 3), Span(0, 18)), - (Instruction::end(true), Span(18, 18)), ], vec![ Value::integer(1), @@ -344,16 +324,15 @@ fn list_with_simple_expression() { parse(source), Ok(Chunk::with_data( vec![ - (Instruction::load_constant(0, 0), Span(1, 2)), + (Instruction::load_constant(0, 0, false), Span(1, 2)), ( *Instruction::add(1, 1, 2) .set_b_is_constant() .set_c_is_constant(), Span(6, 7) ), - (Instruction::load_constant(2, 3), Span(11, 12)), + (Instruction::load_constant(2, 3, false), Span(11, 12)), (Instruction::load_list(3, 0, 2), Span(0, 13)), - (Instruction::end(true), Span(13, 13)), ], vec![ Value::integer(1), @@ -374,11 +353,10 @@ fn list() { parse(source), Ok(Chunk::with_data( vec![ - (Instruction::load_constant(0, 0), Span(1, 2)), - (Instruction::load_constant(1, 1), Span(4, 5)), - (Instruction::load_constant(2, 2), Span(7, 8)), + (Instruction::load_constant(0, 0, false), Span(1, 2)), + (Instruction::load_constant(1, 1, false), Span(4, 5)), + (Instruction::load_constant(2, 2, false), Span(7, 8)), (Instruction::load_list(3, 0, 2), Span(0, 9)), - (Instruction::end(true), Span(9, 9)), ], vec![Value::integer(1), Value::integer(2), Value::integer(3),], vec![] @@ -404,19 +382,16 @@ fn block_scope() { parse(source), Ok(Chunk::with_data( vec![ - (Instruction::load_constant(0, 0), Span(17, 18)), + (Instruction::load_constant(0, 0, false), Span(17, 18)), (Instruction::define_local(0, 0, false), Span(13, 14)), - (Instruction::load_constant(1, 1), Span(50, 52)), + (Instruction::load_constant(1, 1, false), Span(50, 52)), (Instruction::define_local(1, 1, false), Span(46, 47)), - (Instruction::load_constant(2, 2), Span(92, 93)), + (Instruction::load_constant(2, 2, false), Span(92, 93)), (Instruction::define_local(2, 2, false), Span(88, 89)), - (Instruction::end(false), Span(107, 108)), - (Instruction::load_constant(3, 3), Span(129, 130)), + (Instruction::load_constant(3, 3, false), Span(129, 130)), (Instruction::define_local(3, 3, false), Span(125, 126)), - (Instruction::end(false), Span(140, 141)), - (Instruction::load_constant(4, 4), Span(158, 159)), + (Instruction::load_constant(4, 4, false), Span(158, 159)), (Instruction::define_local(4, 4, false), Span(154, 155)), - (Instruction::end(false), Span(165, 165)), ], vec![ Value::integer(0), @@ -447,11 +422,10 @@ fn set_local() { parse("let mut x = 41; x = 42;"), Ok(Chunk::with_data( vec![ - (Instruction::load_constant(0, 0), Span(12, 14)), + (Instruction::load_constant(0, 0, false), Span(12, 14)), (Instruction::define_local(0, 0, true), Span(8, 9)), - (Instruction::load_constant(1, 1), Span(20, 22)), + (Instruction::load_constant(1, 1, false), 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)),] @@ -475,7 +449,6 @@ fn parentheses_precedence() { *Instruction::multiply(1, 0, 2).set_c_is_constant(), Span(8, 9) ), - (Instruction::end(true), Span(11, 11)), ], vec![Value::integer(1), Value::integer(2), Value::integer(3)], vec![] @@ -506,7 +479,6 @@ fn math_operator_precedence() { Span(14, 15) ), (Instruction::subtract(3, 0, 2), Span(6, 7)), - (Instruction::end(true), Span(17, 17)), ], vec![ Value::integer(1), @@ -526,9 +498,8 @@ fn define_local() { parse("let x = 42;"), Ok(Chunk::with_data( vec![ - (Instruction::load_constant(0, 0), Span(8, 10)), + (Instruction::load_constant(0, 0, false), 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))] @@ -546,7 +517,6 @@ fn or() { (Instruction::test(0, true), Span(5, 7)), (Instruction::jump(1, true), Span(5, 7)), (Instruction::load_boolean(1, false, false), Span(8, 13)), - (Instruction::end(true), Span(13, 13)), ], vec![], vec![] @@ -564,7 +534,6 @@ fn and() { (Instruction::test(0, false), Span(5, 7)), (Instruction::jump(1, true), Span(5, 7)), (Instruction::load_boolean(1, false, false), Span(8, 13)), - (Instruction::end(true), Span(13, 13)), ], vec![], vec![] @@ -586,7 +555,6 @@ fn variable_and() { (Instruction::test(2, false), 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![ @@ -602,15 +570,12 @@ fn divide() { assert_eq!( parse("1 / 2"), Ok(Chunk::with_data( - vec![ - ( - *Instruction::divide(0, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 3) - ), - (Instruction::end(true), Span(5, 5)), - ], + vec![( + *Instruction::divide(0, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 3) + ),], vec![Value::integer(1), Value::integer(2)], vec![] )) @@ -622,15 +587,12 @@ fn multiply() { assert_eq!( parse("1 * 2"), Ok(Chunk::with_data( - vec![ - ( - *Instruction::multiply(0, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 3) - ), - (Instruction::end(true), Span(5, 5)), - ], + vec![( + *Instruction::multiply(0, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 3) + ),], vec![Value::integer(1), Value::integer(2)], vec![] )) @@ -642,15 +604,12 @@ fn add() { assert_eq!( parse("1 + 2"), Ok(Chunk::with_data( - vec![ - ( - *Instruction::add(0, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 3) - ), - (Instruction::end(true), Span(5, 5)), - ], + vec![( + *Instruction::add(0, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 3) + ),], vec![Value::integer(1), Value::integer(2)], vec![] )) @@ -662,15 +621,12 @@ fn subtract() { assert_eq!( parse("1 - 2"), Ok(Chunk::with_data( - vec![ - ( - *Instruction::subtract(0, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 3) - ), - (Instruction::end(true), Span(5, 5)), - ], + vec![( + *Instruction::subtract(0, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 3) + ),], vec![Value::integer(1), Value::integer(2)], vec![] )) @@ -682,10 +638,7 @@ fn constant() { assert_eq!( parse("42"), Ok(Chunk::with_data( - vec![ - (Instruction::load_constant(0, 0), Span(0, 2)), - (Instruction::end(true), Span(2, 2)), - ], + vec![(Instruction::load_constant(0, 0, false), Span(0, 2)),], vec![Value::integer(42)], vec![] )) diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index af3c92a..45fa49e 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -93,9 +93,14 @@ impl Vm { Operation::LoadConstant => { let to_register = instruction.a(); let from_constant = instruction.b(); + let jump = instruction.c_as_boolean(); let value = self.chunk.take_constant(from_constant, position)?; self.insert(value, to_register, position)?; + + if jump { + self.ip += 1; + } } Operation::LoadList => { let to_register = instruction.a(); @@ -367,17 +372,12 @@ impl Vm { return Ok(Some(self.take(start_register, position)?)); } } - Operation::End => { - let returns_value = instruction.a_as_boolean(); - - if returns_value { - return Ok(Some(self.pop(position)?)); - } - } } } - Ok(None) + let final_value = self._pop(Span(0, 0))?; + + Ok(Some(final_value)) } fn insert(&mut self, value: Value, index: u8, position: Span) -> Result<(), VmError> { @@ -446,7 +446,7 @@ impl Vm { } } - fn clone_mutable(&mut self, index: u8, position: Span) -> Result { + fn _clone_mutable(&mut self, index: u8, position: Span) -> Result { let index = index as usize; if let Some(register) = self.register_stack.get_mut(index) { @@ -464,7 +464,7 @@ impl Vm { } } - fn clone_as_variable(&mut self, local: Local, position: Span) -> Result { + fn _clone_as_variable(&mut self, local: Local, position: Span) -> Result { let index = if let Some(index) = local.register_index { index } else { @@ -474,7 +474,7 @@ impl Vm { }); }; let clone_result = if local.mutable { - self.clone_mutable(index, position) + self._clone_mutable(index, position) } else { self.clone(index, position) }; @@ -488,7 +488,7 @@ impl Vm { } } - fn pop(&mut self, position: Span) -> Result { + fn _pop(&mut self, position: Span) -> Result { if let Some(register) = self.register_stack.pop() { let value = register.ok_or(VmError::EmptyRegister { index: self.register_stack.len().saturating_sub(1), diff --git a/dust-lang/tests/expressions.rs b/dust-lang/tests/expressions.rs index 501147d..d8357ab 100644 --- a/dust-lang/tests/expressions.rs +++ b/dust-lang/tests/expressions.rs @@ -1,5 +1,13 @@ use dust_lang::*; +#[test] +fn r#while() { + assert_eq!( + run("let mut x = 0; while x < 5 { x = x + 1 } x"), + Ok(Some(Value::integer(5))) + ); +} + #[test] fn if_else() { assert_eq!(run("if true { 1 } else { 2 }"), Ok(Some(Value::integer(1))));