1
0

Change return to use last_assigned_value; Add scopes tests

This commit is contained in:
Jeff 2024-10-29 23:11:55 -04:00
parent 004cf73959
commit e304195661
5 changed files with 153 additions and 27 deletions

View File

@ -173,30 +173,19 @@ impl<'src> Parser<'src> {
second_loader_new.set_a(first_loader.a()); second_loader_new.set_a(first_loader.a());
second_loader_new.set_b(second_loader.b()); second_loader_new.set_b(second_loader.b());
second_loader_new.set_c(second_loader.c()); second_loader_new.set_c(second_loader.c());
second_loader_new.set_b_to_boolean(second_loader.b_is_constant());
if second_loader.b_is_constant() { second_loader_new.set_c_to_boolean(second_loader.c_is_constant());
second_loader_new.set_b_is_constant();
}
if second_loader.c_is_constant() {
second_loader_new.set_c_is_constant();
}
*second_loader = second_loader_new; *second_loader = second_loader_new;
let jump = instructions.next().unwrap();
jump.set_b(jump.b() - 1);
} }
self.current_statement_length = 0; self.current_statement_length = 0;
} }
fn get_statement_instructions(&self) -> impl Iterator<Item = &Instruction> {
self.chunk
.instructions()
.iter()
.rev()
.take(self.current_statement_length)
.map(|(instruction, _)| instruction)
}
fn get_last_value_operation(&self) -> Option<Operation> { fn get_last_value_operation(&self) -> Option<Operation> {
self.chunk self.chunk
.instructions() .instructions()

View File

@ -18,6 +18,7 @@ pub struct Vm {
ip: usize, ip: usize,
chunk: Chunk, chunk: Chunk,
stack: Vec<Register>, stack: Vec<Register>,
last_assigned_register: Option<u8>,
} }
impl Vm { impl Vm {
@ -28,6 +29,7 @@ impl Vm {
ip: 0, ip: 0,
chunk, chunk,
stack: Vec::new(), stack: Vec::new(),
last_assigned_register: None,
} }
} }
@ -387,14 +389,17 @@ impl Vm {
Operation::Return => { Operation::Return => {
let should_return_value = instruction.b_as_boolean(); let should_return_value = instruction.b_as_boolean();
return if should_return_value { if !should_return_value {
let top_of_stack = (self.stack.len() - 1) as u8; return Ok(None);
let value = self.empty(top_of_stack, position)?; }
Ok(Some(value)) if let Some(register) = self.last_assigned_register {
let value = self.empty(register, position)?;
return Ok(Some(value));
} else { } else {
Ok(None) return Err(VmError::StackUnderflow { position });
}; }
} }
} }
} }
@ -404,6 +409,7 @@ impl Vm {
fn set(&mut self, to_register: u8, value: Value, position: Span) -> Result<(), VmError> { fn set(&mut self, to_register: u8, value: Value, position: Span) -> Result<(), VmError> {
let length = self.stack.len(); let length = self.stack.len();
self.last_assigned_register = Some(to_register);
let to_register = to_register as usize; let to_register = to_register as usize;
if length == Self::STACK_LIMIT { if length == Self::STACK_LIMIT {
@ -450,6 +456,7 @@ impl Vm {
position: Span, position: Span,
) -> Result<(), VmError> { ) -> Result<(), VmError> {
let length = self.stack.len(); let length = self.stack.len();
self.last_assigned_register = Some(to_register);
let to_register = to_register as usize; let to_register = to_register as usize;
if length == Self::STACK_LIMIT { if length == Self::STACK_LIMIT {
@ -496,6 +503,7 @@ impl Vm {
position: Span, position: Span,
) -> Result<(), VmError> { ) -> Result<(), VmError> {
let length = self.stack.len(); let length = self.stack.len();
self.last_assigned_register = Some(to_register);
let to_register = to_register as usize; let to_register = to_register as usize;
if length == Self::STACK_LIMIT { if length == Self::STACK_LIMIT {

View File

@ -569,6 +569,76 @@ fn if_else_complex() {
) )
} }
#[test]
fn if_else_nested() {
let source = r#"
if 0 == 1 {
if 0 == 2 {
1;
} else {
2;
}
} else {
if 0 == 3 {
3;
} else {
4;
}
}"#;
assert_eq!(
parse(source),
Ok(Chunk::with_data(
None,
vec![
(
*Instruction::equal(true, 0, 1)
.set_b_is_constant()
.set_c_is_constant(),
Span(14, 16)
),
(Instruction::jump(7), Span(14, 16)),
(
*Instruction::equal(true, 0, 2)
.set_b_is_constant()
.set_c_is_constant(),
Span(38, 41)
),
(Instruction::jump(3), Span(38, 41)),
(Instruction::load_constant(0, 1, false), Span(61, 62)),
(Instruction::jump(11), Span(95, 95)),
(
*Instruction::equal(true, 0, 3)
.set_b_is_constant()
.set_c_is_constant(),
Span(77, 79)
),
(Instruction::jump(3), Span(77, 79)),
(Instruction::load_constant(0, 2, false), Span(94, 95)),
(Instruction::jump(11), Span(95, 95)),
(Instruction::load_constant(0, 3, false), Span(114, 115)),
(Instruction::jump(11), Span(95, 95)),
(Instruction::load_constant(0, 4, false), Span(134, 135)),
(Instruction::r#return(true), Span(146, 146)),
],
vec![
Value::integer(0),
Value::integer(1),
Value::integer(0),
Value::integer(2),
Value::integer(1),
Value::integer(0),
Value::integer(3),
Value::integer(3),
Value::integer(4)
],
vec![]
))
);
assert_eq!(run(source), Ok(Some(Value::integer(4))));
}
#[test] #[test]
fn if_else_simple() { fn if_else_simple() {
let source = "if 1 == 1 { 2 } else { 3 }"; let source = "if 1 == 1 { 2 } else { 3 }";

57
dust-lang/tests/scopes.rs Normal file
View File

@ -0,0 +1,57 @@
use dust_lang::*;
#[test]
fn allow_access_to_parent_scope() {
let source = r#"
let x = 1;
{
x
}
"#;
assert_eq!(run(source), Ok(Some(Value::integer(1))));
}
// #[test]
// fn disallow_access_to_child_scope() {
// let source = r#"
// {
// let x = 1;
// }
// x
// "#;
// assert_eq!(
// run(source),
// Err(DustError::Parse {
// error: ParseError::Chunk(ChunkError::LocalOutOfScope {
// identifier: Identifier::new("x"),
// position: Span(52, 53)
// }),
// source
// })
// );
// }
// #[test]
// fn disallow_access_to_sibling_scope() {
// let source = r#"
// {
// let x = 1;
// }
// {
// x
// }
// "#;
// assert_eq!(
// run(source),
// Err(DustError::Parse {
// error: ParseError::Chunk(ChunkError::LocalOutOfScope {
// identifier: Identifier::new("x"),
// position: Span(52, 53)
// }),
// source
// })
// );
// }

View File

@ -1,11 +1,13 @@
fn fib (n: int) -> int { fn fib (n: int) -> int {
if n <= 0 { if n <= 0 {
0 0
} else if n == 1 { } else {
if n == 1 {
1 1
} else { } else {
fib(n - 1) + fib(n - 2) fib(n - 1) + fib(n - 2)
} }
} }
}
fib(2) fib(10)