diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 044e251..37fa803 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -1092,7 +1092,7 @@ impl<'src> Parser<'src> { fn parse_while(&mut self, allowed: Allowed) -> Result<(), ParseError> { self.advance()?; - let jump_start = self.chunk.len() as u8; + let expression_start = self.chunk.len() as u8; self.parse_expression()?; @@ -1102,21 +1102,27 @@ impl<'src> Parser<'src> { { self.chunk.instructions_mut().pop(); self.chunk.instructions_mut().pop(); + self.chunk.instructions_mut().pop(); } + let block_start = self.chunk.len(); + self.parse_block(Allowed { assignment: true, explicit_return: allowed.explicit_return, implicit_return: false, })?; - if let Some(jump) = self.get_last_jump_mut() { - jump.set_b(jump.b() + 1); - } + let block_end = self.chunk.len() as u8; - let jump_end = self.chunk.len() as u8; - let jump_distance = jump_end - jump_start; - let jump_back = Instruction::jump(jump_distance, false); + self.chunk.insert_instruction( + block_start, + Instruction::jump(block_end - block_start as u8 + 1, true), + self.current_position, + )?; + + let jump_back_distance = block_end - expression_start + 1; + let jump_back = Instruction::jump(jump_back_distance, false); self.emit_instruction(jump_back, self.current_position); self.optimize_statement(); diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index c8f1734..04bb99c 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -353,7 +353,7 @@ impl Vm { let new_ip = if is_positive { self.ip + jump_distance as usize } else { - self.ip - jump_distance as usize + self.ip - jump_distance as usize - 1 }; self.jump_to_ip(new_ip); diff --git a/dust-lang/tests/comparison.rs b/dust-lang/tests/comparison.rs new file mode 100644 index 0000000..9702838 --- /dev/null +++ b/dust-lang/tests/comparison.rs @@ -0,0 +1,169 @@ +use dust_lang::*; + +#[test] +fn equal() { + let source = "1 == 2"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + ( + *Instruction::equal(true, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 4) + ), + (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(true), Span(6, 6)), + ], + vec![Value::integer(1), Value::integer(2)], + vec![] + )), + ); + + assert_eq!(run(source), Ok(Some(Value::boolean(false)))); +} + +#[test] +fn greater() { + let source = "1 > 2"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + ( + *Instruction::less_equal(false, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 3) + ), + (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(true), Span(5, 5)), + ], + vec![Value::integer(1), Value::integer(2)], + vec![] + )), + ); + + assert_eq!(run(source), Ok(Some(Value::boolean(false)))); +} + +#[test] +fn greater_than_or_equal() { + let source = "1 >= 2"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + ( + *Instruction::less(false, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 4) + ), + (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(true), Span(6, 6)), + ], + vec![Value::integer(1), Value::integer(2)], + vec![] + )), + ); + + assert_eq!(run(source), Ok(Some(Value::boolean(false)))); +} + +#[test] +fn less_than() { + let source = "1 < 2"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + ( + *Instruction::less(true, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 3) + ), + (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(true), Span(5, 5)), + ], + vec![Value::integer(1), Value::integer(2)], + vec![] + )), + ); + + assert_eq!(run(source), Ok(Some(Value::boolean(true)))); +} + +#[test] +fn less_than_or_equal() { + let source = "1 <= 2"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + ( + *Instruction::less_equal(true, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 4) + ), + (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(true), Span(6, 6)), + ], + vec![Value::integer(1), Value::integer(2)], + vec![] + )), + ); + + assert_eq!(run(source), Ok(Some(Value::boolean(true)))); +} + +#[test] +fn not_equal() { + let source = "1 != 2"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + ( + *Instruction::equal(false, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 4) + ), + (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(true), Span(6, 6)), + ], + vec![Value::integer(1), Value::integer(2)], + vec![] + )), + ); + + assert_eq!(run(source), Ok(Some(Value::boolean(true)))); +} diff --git a/dust-lang/tests/expressions.rs b/dust-lang/tests/expressions.rs index dc89588..ffef606 100644 --- a/dust-lang/tests/expressions.rs +++ b/dust-lang/tests/expressions.rs @@ -48,29 +48,6 @@ fn add_assign() { assert_eq!(run(source), Ok(Some(Value::integer(3)))); } -#[test] -fn and() { - let source = "true && false"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (Instruction::load_boolean(0, true, false), Span(0, 4)), - (Instruction::test(0, false), Span(5, 7)), - (Instruction::jump(4, true), Span(5, 7)), - (Instruction::load_boolean(1, false, false), Span(8, 13)), - (Instruction::r#return(true), Span(13, 13)), - ], - vec![], - vec![] - )) - ); - - assert_eq!(run(source), Ok(Some(Value::boolean(false)))); -} - #[test] fn block_scope() { let source = " @@ -243,34 +220,6 @@ fn empty_list() { assert_eq!(run(source), Ok(Some(Value::list(0, 0, Type::Any)))); } -#[test] -fn equal() { - let source = "1 == 2"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - ( - *Instruction::equal(true, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 4) - ), - (Instruction::jump(3, 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(true), Span(6, 6)), - ], - vec![Value::integer(1), Value::integer(2)], - vec![] - )), - ); - - assert_eq!(run(source), Ok(Some(Value::boolean(false)))); -} - #[test] fn function() { let source = "fn(a: int, b: int) -> int { a + b }"; @@ -404,118 +353,6 @@ fn function_call() { assert_eq!(run(source), Ok(Some(Value::integer(3)))); } -#[test] -fn greater() { - let source = "1 > 2"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - ( - *Instruction::less_equal(false, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 3) - ), - (Instruction::jump(3, 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(true), Span(5, 5)), - ], - vec![Value::integer(1), Value::integer(2)], - vec![] - )), - ); - - assert_eq!(run(source), Ok(Some(Value::boolean(false)))); -} - -#[test] -fn greater_than_or_equal() { - let source = "1 >= 2"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - ( - *Instruction::less(false, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 4) - ), - (Instruction::jump(3, 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(true), Span(6, 6)), - ], - vec![Value::integer(1), Value::integer(2)], - vec![] - )), - ); - - assert_eq!(run(source), Ok(Some(Value::boolean(false)))); -} - -#[test] -fn less_than() { - let source = "1 < 2"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - ( - *Instruction::less(true, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 3) - ), - (Instruction::jump(3, 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(true), Span(5, 5)), - ], - vec![Value::integer(1), Value::integer(2)], - vec![] - )), - ); - - assert_eq!(run(source), Ok(Some(Value::boolean(true)))); -} - -#[test] -fn less_than_or_equal() { - let source = "1 <= 2"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - ( - *Instruction::less_equal(true, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 4) - ), - (Instruction::jump(3, 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(true), Span(6, 6)), - ], - vec![Value::integer(1), Value::integer(2)], - vec![] - )), - ); - - assert_eq!(run(source), Ok(Some(Value::boolean(true)))); -} - #[test] fn list() { let source = "[1, 2, 3]"; @@ -747,57 +584,6 @@ fn not() { assert_eq!(run(source), Ok(Some(Value::boolean(false)))); } -#[test] -fn not_equal() { - let source = "1 != 2"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - ( - *Instruction::equal(false, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 4) - ), - (Instruction::jump(3, 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(true), Span(6, 6)), - ], - vec![Value::integer(1), Value::integer(2)], - vec![] - )), - ); - - assert_eq!(run(source), Ok(Some(Value::boolean(true)))); -} - -#[test] -fn or() { - let source = "true || false"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (Instruction::load_boolean(0, true, false), Span(0, 4)), - (Instruction::test(0, true), Span(5, 7)), - (Instruction::jump(4, true), Span(5, 7)), - (Instruction::load_boolean(1, false, false), Span(8, 13)), - (Instruction::r#return(true), Span(13, 13)), - ], - vec![], - vec![] - )) - ); - - assert_eq!(run(source), Ok(Some(Value::boolean(true)))); -} - #[test] fn parentheses_precedence() { let source = "(1 + 2) * 3"; @@ -917,7 +703,7 @@ fn variable_and() { (Instruction::define_local(1, 1, false), Span(18, 19)), (Instruction::get_local(2, 0), Span(29, 30)), (Instruction::test(2, false), Span(31, 33)), - (Instruction::jump(8, true), Span(31, 33)), + (Instruction::jump(1, true), Span(31, 33)), (Instruction::get_local(3, 1), Span(34, 35)), (Instruction::r#return(true), Span(35, 35)), ], @@ -947,9 +733,9 @@ fn r#while() { *Instruction::less(true, 0, 1).set_c_is_constant(), Span(23, 24) ), - (Instruction::jump(7, true), Span(23, 24)), - (*Instruction::add(0, 0, 2).set_c_is_constant(), Span(39, 40)), (Instruction::jump(2, true), Span(41, 42)), + (*Instruction::add(0, 0, 2).set_c_is_constant(), Span(39, 40)), + (Instruction::jump(3, false), Span(41, 42)), (Instruction::get_local(1, 0), Span(41, 42)), (Instruction::r#return(true), Span(42, 42)), ], diff --git a/dust-lang/tests/logic.rs b/dust-lang/tests/logic.rs new file mode 100644 index 0000000..532df4f --- /dev/null +++ b/dust-lang/tests/logic.rs @@ -0,0 +1,47 @@ +use dust_lang::*; + +#[test] +fn and() { + let source = "true && false"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (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, false), Span(8, 13)), + (Instruction::r#return(true), Span(13, 13)), + ], + vec![], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(Value::boolean(false)))); +} + +#[test] +fn or() { + let source = "true || false"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (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, false), Span(8, 13)), + (Instruction::r#return(true), Span(13, 13)), + ], + vec![], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(Value::boolean(true)))); +} diff --git a/examples/fizzbuzz.ds b/examples/fizzbuzz.ds index f885afb..87a4bbf 100644 --- a/examples/fizzbuzz.ds +++ b/examples/fizzbuzz.ds @@ -11,7 +11,7 @@ while count <= 15 { } else if divides_by_5 { "buzz" } else { - string(count) + to_string(count) } write_line(output)