diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index abd8b3b..ce90131 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -308,7 +308,7 @@ impl PartialEq for Chunk { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct Local { pub identifier: Identifier, - pub mutable: bool, + pub is_mutable: bool, pub depth: usize, pub register_index: Option, } @@ -322,7 +322,7 @@ impl Local { ) -> Self { Self { identifier, - mutable, + is_mutable: mutable, depth, register_index, } @@ -481,7 +481,7 @@ impl<'a> ChunkDisassembler<'a> { identifier, depth, register_index, - mutable, + is_mutable: mutable, }, ) in self.chunk.locals.iter().enumerate() { diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index 7c3df9b..39abbcc 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -206,13 +206,8 @@ impl Instruction { instruction } - pub fn r#return(from_register: u8, to_register: u8) -> Instruction { - let mut instruction = Instruction(Operation::Return as u32); - - instruction.set_a(from_register); - instruction.set_b(to_register); - - instruction + pub fn r#return() -> Instruction { + Instruction(Operation::Return as u32) } pub fn operation(&self) -> Operation { @@ -350,7 +345,14 @@ impl Instruction { Operation::LoadConstant => { let register_index = self.a(); let constant_index = self.b(); - let jump = if self.c_as_boolean() { "&& JUMP" } else { "" }; + let jump = if self.c_as_boolean() { + jump_offset = Some(1); + + "&& JUMP" + } else { + "" + }; + let constant = if let Some(chunk) = chunk { match chunk.get_constant(constant_index, Span(0, 0)) { Ok(constant) => constant.to_string(), @@ -531,12 +533,7 @@ impl Instruction { None } - Operation::Return => { - let from_register = self.a(); - let to_register = self.b(); - - Some(format!("R{from_register}..=R{to_register}")) - } + Operation::Return => None, }; (info, jump_offset) @@ -745,10 +742,8 @@ mod tests { #[test] fn r#return() { - let instruction = Instruction::r#return(4, 8); + let instruction = Instruction::r#return(); assert_eq!(instruction.operation(), Operation::Return); - assert_eq!(instruction.a(), 4); - assert_eq!(instruction.b(), 8); } } diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 34317aa..90f4f9c 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -585,7 +585,10 @@ impl<'src> Parser<'src> { let local_index = self.parse_identifier_from(token, start_position)?; if allow_assignment && self.allow(TokenKind::Equal)? { - let is_mutable = self.chunk.get_local(local_index, start_position)?.mutable; + let is_mutable = self + .chunk + .get_local(local_index, start_position)? + .is_mutable; if !is_mutable { return Err(ParseError::CannotMutateImmutableVariable { @@ -729,8 +732,10 @@ 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 last_operation = self.chunk.get_last_operation()?; + + if let (Operation::LoadConstant | Operation::LoadBoolean, Token::Else) = + (last_operation, self.current_token) { let (mut load_constant, load_constant_position) = self.chunk.pop_instruction(self.current_position)?; @@ -803,7 +808,6 @@ impl<'src> Parser<'src> { Instruction::load_boolean(self.current_register, false, false), comparison_position, ); - self.increment_register()?; } } @@ -824,12 +828,7 @@ impl<'src> Parser<'src> { self.parse_expression()?; if !self.allow(TokenKind::Semicolon)? && self.is_eof() { - let register = self.current_register.saturating_sub(1); - - self.emit_instruction( - Instruction::r#return(register, register), - self.current_position, - ); + self.emit_instruction(Instruction::r#return(), self.current_position); } } }; diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index bd2c912..fcd3c05 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -146,14 +146,13 @@ impl Vm { let register_index = instruction.a(); let local_index = instruction.b(); let local = self.chunk.get_local(local_index, position)?; - let value = if let Some(index) = local.register_index { - self.take(index, position)? - } else { - return Err(VmError::UndefinedVariable { + + if !local.is_mutable { + return Err(VmError::CannotMutateImmutableLocal { identifier: local.identifier.clone(), position, }); - }; + } let new_value = if instruction.b_is_constant() { self.chunk.take_constant(register_index, position)? @@ -161,10 +160,9 @@ impl Vm { self.take(register_index, position)? }; - value - .mutate(new_value) - .map_err(|error| VmError::Value { error, position })?; - self.insert(value, register_index, position)?; + self.insert(new_value, register_index, position)?; + self.chunk + .define_local(local_index, register_index, position)?; } Operation::Add => { let (left, right) = get_arguments(self, instruction, position)?; @@ -469,7 +467,7 @@ impl Vm { position, }); }; - let clone_result = if local.mutable { + let clone_result = if local.is_mutable { self._clone_mutable(index, position) } else { self.clone(index, position) @@ -508,6 +506,10 @@ impl Vm { #[derive(Debug, Clone, PartialEq)] pub enum VmError { + CannotMutateImmutableLocal { + identifier: Identifier, + position: Span, + }, EmptyRegister { index: usize, position: Span, @@ -555,6 +557,7 @@ impl AnnotatedError for VmError { fn description(&self) -> &'static str { match self { + Self::CannotMutateImmutableLocal { .. } => "Cannot mutate immutable variable", Self::EmptyRegister { .. } => "Empty register", Self::ExpectedBoolean { .. } => "Expected boolean", Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds", @@ -581,6 +584,7 @@ impl AnnotatedError for VmError { fn position(&self) -> Span { match self { + Self::CannotMutateImmutableLocal { position, .. } => *position, Self::EmptyRegister { position, .. } => *position, Self::ExpectedBoolean { position, .. } => *position, Self::RegisterIndexOutOfBounds { position } => *position, diff --git a/dust-lang/tests/expressions.rs b/dust-lang/tests/expressions.rs index 8160241..0a3ba3e 100644 --- a/dust-lang/tests/expressions.rs +++ b/dust-lang/tests/expressions.rs @@ -13,7 +13,7 @@ fn add() { .set_c_is_constant(), Span(2, 3) ), - (Instruction::r#return(0, 0), Span(5, 5)) + (Instruction::r#return(), Span(5, 5)) ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -35,7 +35,7 @@ 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::r#return(0, 0), Span(13, 13)), + (Instruction::r#return(), Span(13, 13)), ], vec![], vec![] @@ -102,7 +102,7 @@ fn constant() { Ok(Chunk::with_data( vec![ (Instruction::load_constant(0, 0, false), Span(0, 2)), - (Instruction::r#return(0, 0), Span(2, 2)) + (Instruction::r#return(), Span(2, 2)) ], vec![Value::integer(42)], vec![] @@ -143,7 +143,7 @@ fn divide() { .set_c_is_constant(), Span(2, 3) ), - (Instruction::r#return(0, 0), Span(5, 5)) + (Instruction::r#return(), Span(5, 5)) ], vec![Value::integer(2), Value::integer(2)], vec![] @@ -177,6 +177,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::r#return(), Span(6, 6)), ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -188,7 +189,7 @@ fn equal() { #[test] fn equality_assignment_long() { - let source = "let a = if 4 == 4 { true } else { false };"; + let source = "let a = if 4 == 4 { true } else { false }; a"; assert_eq!( parse(source), @@ -200,10 +201,12 @@ fn equality_assignment_long() { .set_c_is_constant(), Span(13, 15) ), - (Instruction::jump(1, true), Span(27, 31)), - (Instruction::load_boolean(0, true, false), Span(20, 24)), + (Instruction::jump(1, true), Span(13, 15)), + (Instruction::load_boolean(0, true, true), Span(20, 24)), (Instruction::load_boolean(0, false, false), Span(34, 39)), (Instruction::define_local(0, 0, false), Span(4, 5)), + (Instruction::get_local(1, 0), Span(43, 44)), + (Instruction::r#return(), Span(44, 44)), ], vec![Value::integer(4), Value::integer(4)], vec![Local::new(Identifier::new("a"), false, 0, Some(0))] @@ -215,7 +218,7 @@ fn equality_assignment_long() { #[test] fn equality_assignment_short() { - let source = "let a = 4 == 4;"; + let source = "let a = 4 == 4; a"; assert_eq!( parse(source), @@ -231,18 +234,20 @@ 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::get_local(1, 0), Span(16, 17)), + (Instruction::r#return(), Span(17, 17)), ], vec![Value::integer(4), Value::integer(4)], vec![Local::new(Identifier::new("a"), false, 0, Some(0))] )), ); - assert_eq!(run(source), Ok(None)); + assert_eq!(run(source), Ok(Some(Value::boolean(true)))); } #[test] fn greater() { - let source = "1 > 2;"; + let source = "1 > 2"; assert_eq!( parse(source), @@ -257,6 +262,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::r#return(), Span(5, 5)), ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -268,7 +274,7 @@ fn greater() { #[test] fn greater_than_or_equal() { - let source = "1 >= 2;"; + let source = "1 >= 2"; assert_eq!( parse(source), @@ -283,6 +289,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::r#return(), Span(6, 6)), ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -306,9 +313,10 @@ fn if_else_expression() { .set_c_is_constant(), Span(5, 7) ), - (Instruction::jump(1, true), Span(16, 20)), + (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)), + (Instruction::r#return(), Span(26, 26)), ], vec![ Value::integer(1), @@ -337,8 +345,9 @@ fn if_expression() { .set_c_is_constant(), Span(5, 7) ), - (Instruction::jump(1, true), Span(15, 15)), + (Instruction::jump(1, true), Span(5, 7)), (Instruction::load_constant(0, 2, false), Span(12, 13)), + (Instruction::r#return(), Span(15, 15)), ], vec![Value::integer(1), Value::integer(1), Value::integer(2)], vec![] @@ -350,7 +359,7 @@ fn if_expression() { #[test] fn less_than() { - let source = "1 < 2;"; + let source = "1 < 2"; assert_eq!( parse(source), @@ -365,6 +374,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::r#return(), Span(5, 5)), ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -376,7 +386,7 @@ fn less_than() { #[test] fn less_than_or_equal() { - let source = "1 <= 2;"; + let source = "1 <= 2"; assert_eq!( parse(source), @@ -391,6 +401,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::r#return(), Span(6, 6)), ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -412,6 +423,7 @@ fn list() { (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::r#return(), Span(9, 9)), ], vec![Value::integer(1), Value::integer(2), Value::integer(3),], vec![] @@ -452,6 +464,7 @@ 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::r#return(), Span(18, 18)), ], vec![ Value::integer(1), @@ -468,7 +481,7 @@ fn list_with_complex_expression() { run(source), Ok(Some(Value::list(vec![ Value::integer(1), - Value::integer(1) + Value::integer(-15) ]))) ); } @@ -490,6 +503,7 @@ fn list_with_simple_expression() { ), (Instruction::load_constant(2, 3, false), Span(11, 12)), (Instruction::load_list(3, 0, 2), Span(0, 13)), + (Instruction::r#return(), Span(13, 13)), ], vec![ Value::integer(1), @@ -535,6 +549,7 @@ fn math_operator_precedence() { Span(14, 15) ), (Instruction::subtract(3, 0, 2), Span(6, 7)), + (Instruction::r#return(), Span(17, 17)), ], vec![ Value::integer(1), @@ -556,12 +571,15 @@ fn multiply() { assert_eq!( parse(source), Ok(Chunk::with_data( - vec![( - *Instruction::multiply(0, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 3) - ),], + vec![ + ( + *Instruction::multiply(0, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 3) + ), + (Instruction::r#return(), Span(5, 5)), + ], vec![Value::integer(1), Value::integer(2)], vec![] )) @@ -577,7 +595,10 @@ fn negate() { assert_eq!( parse(source), Ok(Chunk::with_data( - vec![(*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)),], + vec![ + (*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)), + (Instruction::r#return(), Span(5, 5)), + ], vec![Value::integer(42)], vec![] )), @@ -596,6 +617,7 @@ fn not() { vec![ (Instruction::load_boolean(0, true, false), Span(1, 5)), (Instruction::not(1, 0), Span(0, 1)), + (Instruction::r#return(), Span(5, 5)), ], vec![], vec![] @@ -607,7 +629,7 @@ fn not() { #[test] fn not_equal() { - let source = "1 != 2;"; + let source = "1 != 2"; assert_eq!( parse(source), @@ -622,6 +644,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::r#return(), Span(6, 6)), ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -642,6 +665,7 @@ 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::r#return(), Span(13, 13)), ], vec![], vec![] @@ -668,6 +692,7 @@ fn parentheses_precedence() { *Instruction::multiply(1, 0, 2).set_c_is_constant(), Span(8, 9) ), + (Instruction::r#return(), Span(11, 11)), ], vec![Value::integer(1), Value::integer(2), Value::integer(3)], vec![] @@ -679,7 +704,7 @@ fn parentheses_precedence() { #[test] fn set_local() { - let source = "let mut x = 41; x = 42;"; + let source = "let mut x = 41; x = 42; x"; assert_eq!( parse(source), Ok(Chunk::with_data( @@ -688,6 +713,8 @@ fn set_local() { (Instruction::define_local(0, 0, true), Span(8, 9)), (Instruction::load_constant(1, 1, false), Span(20, 22)), (Instruction::set_local(1, 0), Span(16, 17)), + (Instruction::get_local(1, 0), Span(24, 25)), + (Instruction::r#return(), Span(25, 25)), ], vec![Value::integer(41), Value::integer(42)], vec![Local::new(Identifier::new("x"), true, 0, Some(0)),]