1
0

Use test for single registers in conditional expressions

This commit is contained in:
Jeff 2024-09-25 09:55:10 -04:00
parent 47d6ea417d
commit d0d80cf407
7 changed files with 165 additions and 190 deletions

View File

@ -87,6 +87,20 @@ impl Chunk {
Ok((instruction, position)) Ok((instruction, position))
} }
pub fn find_last_instruction(&mut self, operation: Operation) -> Option<usize> {
self.instructions
.iter()
.enumerate()
.rev()
.find_map(|(index, (instruction, _))| {
if instruction.operation() == operation {
Some(index)
} else {
None
}
})
}
pub fn get_last_operation(&self) -> Result<Operation, ChunkError> { pub fn get_last_operation(&self) -> Result<Operation, ChunkError> {
self.get_last_instruction() self.get_last_instruction()
.map(|(instruction, _)| instruction.operation()) .map(|(instruction, _)| instruction.operation())

View File

@ -32,11 +32,12 @@ impl Instruction {
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); let mut instruction = Instruction(Operation::LoadConstant as u32);
instruction.set_a(to_register); instruction.set_a(to_register);
instruction.set_b(constant_index); instruction.set_b(constant_index);
instruction.set_c_to_boolean(skip);
instruction instruction
} }
@ -214,14 +215,6 @@ impl Instruction {
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 { pub fn operation(&self) -> Operation {
Operation::from((self.0 & 0b0000_0000_0011_1111) as u8) Operation::from((self.0 & 0b0000_0000_0011_1111) as u8)
} }
@ -355,18 +348,21 @@ impl Instruction {
Some(info) Some(info)
} }
Operation::LoadConstant => { Operation::LoadConstant => {
let register_index = self.a();
let constant_index = self.b(); let constant_index = self.b();
let jump = if self.c_as_boolean() { "&& JUMP" } else { "" };
if let Some(chunk) = chunk { let constant = if let Some(chunk) = chunk {
match chunk.get_constant(constant_index, Span(0, 0)) { match chunk.get_constant(constant_index, Span(0, 0)) {
Ok(value) => Some(format!("R{} = C{} {}", self.a(), constant_index, value)), Ok(constant) => constant.to_string(),
Err(error) => { Err(error) => format!("{error:?}"),
Some(format!("R{} = C{} {:?}", self.a(), constant_index, error))
}
} }
} else { } else {
Some(format!("R{} = C{}", self.a(), constant_index)) "???".to_string()
} };
Some(format!(
"R{register_index} = C{constant_index} ({constant}) {jump}",
))
} }
Operation::LoadList => { Operation::LoadList => {
let destination = self.a(); let destination = self.a();
@ -541,15 +537,6 @@ impl Instruction {
Some(format!("R{from_register}..=R{to_register}")) 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) (info, jump_offset)
@ -601,7 +588,7 @@ mod tests {
#[test] #[test]
fn load_constant() { 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_b_is_constant();
instruction.set_c_is_constant(); instruction.set_c_is_constant();
@ -611,6 +598,7 @@ mod tests {
assert_eq!(instruction.b(), 1); assert_eq!(instruction.b(), 1);
assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant());
assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant());
assert!(instruction.c_as_boolean());
} }
#[test] #[test]
@ -763,12 +751,4 @@ mod tests {
assert_eq!(instruction.a(), 4); assert_eq!(instruction.a(), 4);
assert_eq!(instruction.b(), 8); assert_eq!(instruction.b(), 8);
} }
#[test]
fn end() {
let instruction = Instruction::end(true);
assert_eq!(instruction.operation(), Operation::End);
assert!(instruction.a_as_boolean());
}
} }

View File

@ -29,7 +29,6 @@ const NOT: u8 = 0b0001_0011;
const JUMP: u8 = 0b0001_0100; const JUMP: u8 = 0b0001_0100;
const RETURN: u8 = 0b0001_0101; const RETURN: u8 = 0b0001_0101;
const END: u8 = 0b0001_0110;
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum Operation { pub enum Operation {
@ -70,7 +69,6 @@ pub enum Operation {
// Control flow // Control flow
Jump = JUMP as isize, Jump = JUMP as isize,
Return = RETURN as isize, Return = RETURN as isize,
End = END as isize,
} }
impl Operation { impl Operation {
@ -111,12 +109,11 @@ impl From<u8> for Operation {
NOT => Operation::Not, NOT => Operation::Not,
JUMP => Operation::Jump, JUMP => Operation::Jump,
RETURN => Operation::Return, RETURN => Operation::Return,
END => Operation::End,
_ => { _ => {
if cfg!(test) { if cfg!(test) {
panic!("Invalid operation byte: {}", byte) panic!("Invalid operation byte: {}", byte)
} else { } else {
Operation::End Operation::Return
} }
} }
} }
@ -148,7 +145,6 @@ impl From<Operation> for u8 {
Operation::Not => NOT, Operation::Not => NOT,
Operation::Jump => JUMP, Operation::Jump => JUMP,
Operation::Return => RETURN, Operation::Return => RETURN,
Operation::End => END,
} }
} }
} }
@ -178,7 +174,6 @@ impl Display for Operation {
Operation::Not => write!(f, "NOT"), Operation::Not => write!(f, "NOT"),
Operation::Jump => write!(f, "JUMP"), Operation::Jump => write!(f, "JUMP"),
Operation::Return => write!(f, "RETURN"), Operation::Return => write!(f, "RETURN"),
Operation::End => write!(f, "END"),
} }
} }
} }

View File

@ -145,7 +145,7 @@ impl<'src> Parser<'src> {
let constant_index = self.chunk.push_constant(value, position)?; let constant_index = self.chunk.push_constant(value, position)?;
self.emit_instruction( self.emit_instruction(
Instruction::load_constant(self.current_register, constant_index), Instruction::load_constant(self.current_register, constant_index, false),
position, position,
); );
@ -352,7 +352,16 @@ impl<'src> Parser<'src> {
let mut push_back = false; let mut push_back = false;
let mut is_constant = false; let mut is_constant = false;
let argument = match instruction.operation() { 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 => { Operation::LoadConstant => {
is_constant = true; is_constant = true;
@ -492,12 +501,12 @@ impl<'src> Parser<'src> {
let operator_position = self.current_position; let operator_position = self.current_position;
let rule = ParseRule::from(&operator.kind()); let rule = ParseRule::from(&operator.kind());
let mut instruction = match self.current_token.kind() { let mut instruction = match self.current_token.kind() {
TokenKind::DoubleEqual => Instruction::equal(true, left, 0), TokenKind::DoubleEqual => Instruction::equal(true, left.saturating_sub(1), 0),
TokenKind::BangEqual => Instruction::equal(false, left, 0), TokenKind::BangEqual => Instruction::equal(false, left.saturating_sub(1), 0),
TokenKind::Less => Instruction::less(true, left, 0), TokenKind::Less => Instruction::less(true, left.saturating_sub(1), 0),
TokenKind::LessEqual => Instruction::less_equal(true, left, 0), TokenKind::LessEqual => Instruction::less_equal(true, left.saturating_sub(1), 0),
TokenKind::Greater => Instruction::less_equal(false, left, 0), TokenKind::Greater => Instruction::less_equal(false, left.saturating_sub(1), 0),
TokenKind::GreaterEqual => Instruction::less(false, left, 0), TokenKind::GreaterEqual => Instruction::less(false, left.saturating_sub(1), 0),
_ => { _ => {
return Err(ParseError::ExpectedTokenMultiple { return Err(ParseError::ExpectedTokenMultiple {
expected: &[ expected: &[
@ -532,7 +541,6 @@ impl<'src> Parser<'src> {
instruction.set_c_is_constant(); instruction.set_c_is_constant();
} }
if push_back_left || push_back_right {
if push_back_left { if push_back_left {
self.emit_instruction(left_instruction, left_position); self.emit_instruction(left_instruction, left_position);
} }
@ -540,7 +548,6 @@ impl<'src> Parser<'src> {
if push_back_right { if push_back_right {
self.emit_instruction(right_instruction, right_position); self.emit_instruction(right_instruction, right_position);
} }
}
self.emit_instruction(instruction, operator_position); self.emit_instruction(instruction, operator_position);
self.emit_instruction(Instruction::jump(1, true), operator_position); self.emit_instruction(Instruction::jump(1, true), operator_position);
@ -735,18 +742,14 @@ impl<'src> Parser<'src> {
self.advance()?; self.advance()?;
self.parse_expression()?; self.parse_expression()?;
let is_explicit_true = let is_explicit_boolean =
matches!(self.previous_token, Token::Boolean("true")) && length == self.chunk.len() - 1; matches!(self.previous_token, Token::Boolean(_)) && length == self.chunk.len() - 1;
if is_explicit_true { if is_explicit_boolean {
let (mut load_boolean, load_boolean_position) = self.emit_instruction(
self.chunk.pop_instruction(self.current_position)?; Instruction::test(self.current_register, false),
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()?;
} }
let jump_position = if matches!( let jump_position = if matches!(
@ -771,9 +774,26 @@ impl<'src> Parser<'src> {
self.parse_block(allow_assignment, allow_return)?; 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_end = self.chunk.len();
let jump_distance = jump_end.saturating_sub(jump_start); 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 self.allow(TokenKind::Else)? {
if let Token::If = self.current_token { if let Token::If = self.current_token {
self.parse_if(allow_assignment, allow_return)?; 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(()) Ok(())
} }
@ -799,9 +813,28 @@ impl<'src> Parser<'src> {
allow_return: bool, allow_return: bool,
) -> Result<(), ParseError> { ) -> Result<(), ParseError> {
self.advance()?; self.advance()?;
let jump_start = self.chunk.len();
self.parse_expression()?; self.parse_expression()?;
self.parse_block(allow_assignment, allow_return)?; 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(()) Ok(())
} }
@ -810,27 +843,19 @@ impl<'src> Parser<'src> {
} }
fn parse_statement(&mut self, allow_return: bool) -> Result<(), ParseError> { fn parse_statement(&mut self, allow_return: bool) -> Result<(), ParseError> {
let is_expression = match self.current_token { match self.current_token {
Token::Let => { Token::Let => {
self.parse_let_statement(true, allow_return)?; self.parse_let_statement(true, allow_return)?;
self.allow(TokenKind::Semicolon)?
} }
Token::LeftCurlyBrace => { Token::LeftCurlyBrace => {
self.parse_block(true, true)?; self.parse_block(true, true)?;
!self.allow(TokenKind::Semicolon)?
} }
_ => { _ => {
self.parse_expression()?; self.parse_expression()?;
!self.allow(TokenKind::Semicolon)?
} }
}; };
if self.current_token == Token::RightCurlyBrace || self.is_eof() { self.allow(TokenKind::Semicolon)?;
self.emit_instruction(Instruction::end(is_expression), self.current_position);
}
Ok(()) Ok(())
} }

View File

@ -12,7 +12,6 @@ fn not() {
vec![ vec![
(Instruction::load_boolean(0, true, false), Span(1, 5)), (Instruction::load_boolean(0, true, false), Span(1, 5)),
(Instruction::not(1, 0), Span(0, 1)), (Instruction::not(1, 0), Span(0, 1)),
(Instruction::end(true), Span(5, 5)),
], ],
vec![], vec![],
vec![] vec![]
@ -27,10 +26,7 @@ fn negate() {
assert_eq!( assert_eq!(
parse(source), parse(source),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![(*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)),],
(*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)),
(Instruction::end(true), Span(5, 5))
],
vec![Value::integer(42)], vec![Value::integer(42)],
vec![] vec![]
)), )),
@ -54,7 +50,6 @@ fn greater_than_or_equal() {
(Instruction::jump(1, true), Span(2, 4)), (Instruction::jump(1, true), Span(2, 4)),
(Instruction::load_boolean(0, true, true), Span(2, 4)), (Instruction::load_boolean(0, true, true), Span(2, 4)),
(Instruction::load_boolean(0, false, false), 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![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -79,7 +74,6 @@ fn greater() {
(Instruction::jump(1, true), Span(2, 3)), (Instruction::jump(1, true), Span(2, 3)),
(Instruction::load_boolean(0, true, true), Span(2, 3)), (Instruction::load_boolean(0, true, true), Span(2, 3)),
(Instruction::load_boolean(0, false, false), 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![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -104,7 +98,6 @@ fn less_than_or_equal() {
(Instruction::jump(1, true), Span(2, 4)), (Instruction::jump(1, true), Span(2, 4)),
(Instruction::load_boolean(0, true, true), Span(2, 4)), (Instruction::load_boolean(0, true, true), Span(2, 4)),
(Instruction::load_boolean(0, false, false), 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![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -129,7 +122,6 @@ fn less_than() {
(Instruction::jump(1, true), Span(2, 3)), (Instruction::jump(1, true), Span(2, 3)),
(Instruction::load_boolean(0, true, true), Span(2, 3)), (Instruction::load_boolean(0, true, true), Span(2, 3)),
(Instruction::load_boolean(0, false, false), 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![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -154,7 +146,6 @@ fn not_equal() {
(Instruction::jump(1, true), Span(2, 4)), (Instruction::jump(1, true), Span(2, 4)),
(Instruction::load_boolean(0, true, true), Span(2, 4)), (Instruction::load_boolean(0, true, true), Span(2, 4)),
(Instruction::load_boolean(0, false, false), 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![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -179,7 +170,6 @@ fn equal() {
(Instruction::jump(1, true), Span(2, 4)), (Instruction::jump(1, true), Span(2, 4)),
(Instruction::load_boolean(0, true, true), Span(2, 4)), (Instruction::load_boolean(0, true, true), Span(2, 4)),
(Instruction::load_boolean(0, false, false), 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![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -201,16 +191,13 @@ fn equality_assignment_long() {
.set_c_is_constant(), .set_c_is_constant(),
Span(13, 15) 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::load_boolean(0, true, false), Span(20, 24)),
(Instruction::end(true), Span(25, 26)),
(Instruction::load_boolean(0, false, false), Span(34, 39)), (Instruction::load_boolean(0, false, false), Span(34, 39)),
(Instruction::end(true), Span(40, 41)), (Instruction::define_local(0, 0, false), Span(4, 5)),
(Instruction::define_local(1, 0, false), Span(4, 5)),
(Instruction::end(false), Span(42, 42)),
], ],
vec![Value::integer(4), Value::integer(4)], 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, true, true), Span(10, 12)),
(Instruction::load_boolean(0, false, false), Span(10, 12)), (Instruction::load_boolean(0, false, false), Span(10, 12)),
(Instruction::define_local(0, 0, false), Span(4, 5)), (Instruction::define_local(0, 0, false), Span(4, 5)),
(Instruction::end(false), Span(15, 15)),
], ],
vec![Value::integer(4), Value::integer(4)], vec![Value::integer(4), Value::integer(4)],
vec![Local::new(Identifier::new("a"), false, 0, Some(0))] vec![Local::new(Identifier::new("a"), false, 0, Some(0))]
@ -255,10 +241,8 @@ fn if_expression() {
.set_c_is_constant(), .set_c_is_constant(),
Span(5, 7) Span(5, 7)
), ),
(Instruction::jump(2, true), Span(5, 7)), (Instruction::jump(1, true), Span(5, 7)),
(Instruction::load_constant(0, 2), Span(12, 13)), (Instruction::load_constant(0, 2, false), Span(12, 13)),
(Instruction::end(true), Span(14, 15)),
(Instruction::end(true), Span(15, 15)),
], ],
vec![Value::integer(1), Value::integer(1), Value::integer(2)], vec![Value::integer(1), Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -280,12 +264,9 @@ fn if_else_expression() {
.set_c_is_constant(), .set_c_is_constant(),
Span(5, 7) Span(5, 7)
), ),
(Instruction::jump(2, true), Span(5, 7)), (Instruction::jump(1, true), Span(5, 7)),
(Instruction::load_constant(0, 2), Span(12, 13)), (Instruction::load_constant(0, 2, true), Span(12, 13)),
(Instruction::end(true), Span(14, 15)), (Instruction::load_constant(0, 3, false), Span(23, 24)),
(Instruction::load_constant(0, 3), Span(23, 24)),
(Instruction::end(true), Span(25, 26)),
(Instruction::end(true), Span(26, 26)),
], ],
vec![ vec![
Value::integer(1), Value::integer(1),
@ -306,7 +287,7 @@ fn list_with_complex_expression() {
parse(source), parse(source),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![
(Instruction::load_constant(0, 0), Span(1, 2)), (Instruction::load_constant(0, 0, false), Span(1, 2)),
( (
*Instruction::add(1, 1, 2) *Instruction::add(1, 1, 2)
.set_b_is_constant() .set_b_is_constant()
@ -322,7 +303,6 @@ fn list_with_complex_expression() {
(Instruction::subtract(3, 1, 2), Span(10, 11)), (Instruction::subtract(3, 1, 2), Span(10, 11)),
(Instruction::close(1, 3), Span(17, 18)), (Instruction::close(1, 3), Span(17, 18)),
(Instruction::load_list(4, 0, 3), Span(0, 18)), (Instruction::load_list(4, 0, 3), Span(0, 18)),
(Instruction::end(true), Span(18, 18)),
], ],
vec![ vec![
Value::integer(1), Value::integer(1),
@ -344,16 +324,15 @@ fn list_with_simple_expression() {
parse(source), parse(source),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![
(Instruction::load_constant(0, 0), Span(1, 2)), (Instruction::load_constant(0, 0, false), Span(1, 2)),
( (
*Instruction::add(1, 1, 2) *Instruction::add(1, 1, 2)
.set_b_is_constant() .set_b_is_constant()
.set_c_is_constant(), .set_c_is_constant(),
Span(6, 7) 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::load_list(3, 0, 2), Span(0, 13)),
(Instruction::end(true), Span(13, 13)),
], ],
vec![ vec![
Value::integer(1), Value::integer(1),
@ -374,11 +353,10 @@ fn list() {
parse(source), parse(source),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![
(Instruction::load_constant(0, 0), Span(1, 2)), (Instruction::load_constant(0, 0, false), Span(1, 2)),
(Instruction::load_constant(1, 1), Span(4, 5)), (Instruction::load_constant(1, 1, false), Span(4, 5)),
(Instruction::load_constant(2, 2), Span(7, 8)), (Instruction::load_constant(2, 2, false), Span(7, 8)),
(Instruction::load_list(3, 0, 2), Span(0, 9)), (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![Value::integer(1), Value::integer(2), Value::integer(3),],
vec![] vec![]
@ -404,19 +382,16 @@ fn block_scope() {
parse(source), parse(source),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ 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::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::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::define_local(2, 2, false), Span(88, 89)),
(Instruction::end(false), Span(107, 108)), (Instruction::load_constant(3, 3, false), Span(129, 130)),
(Instruction::load_constant(3, 3), Span(129, 130)),
(Instruction::define_local(3, 3, false), Span(125, 126)), (Instruction::define_local(3, 3, false), Span(125, 126)),
(Instruction::end(false), Span(140, 141)), (Instruction::load_constant(4, 4, false), Span(158, 159)),
(Instruction::load_constant(4, 4), Span(158, 159)),
(Instruction::define_local(4, 4, false), Span(154, 155)), (Instruction::define_local(4, 4, false), Span(154, 155)),
(Instruction::end(false), Span(165, 165)),
], ],
vec![ vec![
Value::integer(0), Value::integer(0),
@ -447,11 +422,10 @@ fn set_local() {
parse("let mut x = 41; x = 42;"), parse("let mut x = 41; x = 42;"),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ 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::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::set_local(1, 0), Span(16, 17)),
(Instruction::end(false), Span(23, 23)),
], ],
vec![Value::integer(41), Value::integer(42)], vec![Value::integer(41), Value::integer(42)],
vec![Local::new(Identifier::new("x"), true, 0, Some(0)),] 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(), *Instruction::multiply(1, 0, 2).set_c_is_constant(),
Span(8, 9) Span(8, 9)
), ),
(Instruction::end(true), Span(11, 11)),
], ],
vec![Value::integer(1), Value::integer(2), Value::integer(3)], vec![Value::integer(1), Value::integer(2), Value::integer(3)],
vec![] vec![]
@ -506,7 +479,6 @@ fn math_operator_precedence() {
Span(14, 15) Span(14, 15)
), ),
(Instruction::subtract(3, 0, 2), Span(6, 7)), (Instruction::subtract(3, 0, 2), Span(6, 7)),
(Instruction::end(true), Span(17, 17)),
], ],
vec![ vec![
Value::integer(1), Value::integer(1),
@ -526,9 +498,8 @@ fn define_local() {
parse("let x = 42;"), parse("let x = 42;"),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ 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::define_local(0, 0, false), Span(4, 5)),
(Instruction::end(false), Span(11, 11)),
], ],
vec![Value::integer(42)], vec![Value::integer(42)],
vec![Local::new(Identifier::new("x"), false, 0, Some(0))] vec![Local::new(Identifier::new("x"), false, 0, Some(0))]
@ -546,7 +517,6 @@ fn or() {
(Instruction::test(0, true), Span(5, 7)), (Instruction::test(0, true), Span(5, 7)),
(Instruction::jump(1, true), Span(5, 7)), (Instruction::jump(1, true), Span(5, 7)),
(Instruction::load_boolean(1, false, false), Span(8, 13)), (Instruction::load_boolean(1, false, false), Span(8, 13)),
(Instruction::end(true), Span(13, 13)),
], ],
vec![], vec![],
vec![] vec![]
@ -564,7 +534,6 @@ fn and() {
(Instruction::test(0, false), Span(5, 7)), (Instruction::test(0, false), Span(5, 7)),
(Instruction::jump(1, true), Span(5, 7)), (Instruction::jump(1, true), Span(5, 7)),
(Instruction::load_boolean(1, false, false), Span(8, 13)), (Instruction::load_boolean(1, false, false), Span(8, 13)),
(Instruction::end(true), Span(13, 13)),
], ],
vec![], vec![],
vec![] vec![]
@ -586,7 +555,6 @@ fn variable_and() {
(Instruction::test(2, false), Span(31, 33)), (Instruction::test(2, false), Span(31, 33)),
(Instruction::jump(1, true), Span(31, 33)), (Instruction::jump(1, true), Span(31, 33)),
(Instruction::get_local(3, 1), Span(34, 35)), (Instruction::get_local(3, 1), Span(34, 35)),
(Instruction::end(true), Span(35, 35)),
], ],
vec![], vec![],
vec![ vec![
@ -602,15 +570,12 @@ fn divide() {
assert_eq!( assert_eq!(
parse("1 / 2"), parse("1 / 2"),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![(
(
*Instruction::divide(0, 0, 1) *Instruction::divide(0, 0, 1)
.set_b_is_constant() .set_b_is_constant()
.set_c_is_constant(), .set_c_is_constant(),
Span(2, 3) Span(2, 3)
), ),],
(Instruction::end(true), Span(5, 5)),
],
vec![Value::integer(1), Value::integer(2)], vec![Value::integer(1), Value::integer(2)],
vec![] vec![]
)) ))
@ -622,15 +587,12 @@ fn multiply() {
assert_eq!( assert_eq!(
parse("1 * 2"), parse("1 * 2"),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![(
(
*Instruction::multiply(0, 0, 1) *Instruction::multiply(0, 0, 1)
.set_b_is_constant() .set_b_is_constant()
.set_c_is_constant(), .set_c_is_constant(),
Span(2, 3) Span(2, 3)
), ),],
(Instruction::end(true), Span(5, 5)),
],
vec![Value::integer(1), Value::integer(2)], vec![Value::integer(1), Value::integer(2)],
vec![] vec![]
)) ))
@ -642,15 +604,12 @@ fn add() {
assert_eq!( assert_eq!(
parse("1 + 2"), parse("1 + 2"),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![(
(
*Instruction::add(0, 0, 1) *Instruction::add(0, 0, 1)
.set_b_is_constant() .set_b_is_constant()
.set_c_is_constant(), .set_c_is_constant(),
Span(2, 3) Span(2, 3)
), ),],
(Instruction::end(true), Span(5, 5)),
],
vec![Value::integer(1), Value::integer(2)], vec![Value::integer(1), Value::integer(2)],
vec![] vec![]
)) ))
@ -662,15 +621,12 @@ fn subtract() {
assert_eq!( assert_eq!(
parse("1 - 2"), parse("1 - 2"),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![(
(
*Instruction::subtract(0, 0, 1) *Instruction::subtract(0, 0, 1)
.set_b_is_constant() .set_b_is_constant()
.set_c_is_constant(), .set_c_is_constant(),
Span(2, 3) Span(2, 3)
), ),],
(Instruction::end(true), Span(5, 5)),
],
vec![Value::integer(1), Value::integer(2)], vec![Value::integer(1), Value::integer(2)],
vec![] vec![]
)) ))
@ -682,10 +638,7 @@ fn constant() {
assert_eq!( assert_eq!(
parse("42"), parse("42"),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![(Instruction::load_constant(0, 0, false), Span(0, 2)),],
(Instruction::load_constant(0, 0), Span(0, 2)),
(Instruction::end(true), Span(2, 2)),
],
vec![Value::integer(42)], vec![Value::integer(42)],
vec![] vec![]
)) ))

View File

@ -93,9 +93,14 @@ impl Vm {
Operation::LoadConstant => { Operation::LoadConstant => {
let to_register = instruction.a(); let to_register = instruction.a();
let from_constant = instruction.b(); let from_constant = instruction.b();
let jump = instruction.c_as_boolean();
let value = self.chunk.take_constant(from_constant, position)?; let value = self.chunk.take_constant(from_constant, position)?;
self.insert(value, to_register, position)?; self.insert(value, to_register, position)?;
if jump {
self.ip += 1;
}
} }
Operation::LoadList => { Operation::LoadList => {
let to_register = instruction.a(); let to_register = instruction.a();
@ -367,17 +372,12 @@ impl Vm {
return Ok(Some(self.take(start_register, position)?)); 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> { 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<Value, VmError> { fn _clone_mutable(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
let index = index as usize; let index = index as usize;
if let Some(register) = self.register_stack.get_mut(index) { 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<Value, VmError> { fn _clone_as_variable(&mut self, local: Local, position: Span) -> Result<Value, VmError> {
let index = if let Some(index) = local.register_index { let index = if let Some(index) = local.register_index {
index index
} else { } else {
@ -474,7 +474,7 @@ impl Vm {
}); });
}; };
let clone_result = if local.mutable { let clone_result = if local.mutable {
self.clone_mutable(index, position) self._clone_mutable(index, position)
} else { } else {
self.clone(index, position) self.clone(index, position)
}; };
@ -488,7 +488,7 @@ impl Vm {
} }
} }
fn pop(&mut self, position: Span) -> Result<Value, VmError> { fn _pop(&mut self, position: Span) -> Result<Value, VmError> {
if let Some(register) = self.register_stack.pop() { if let Some(register) = self.register_stack.pop() {
let value = register.ok_or(VmError::EmptyRegister { let value = register.ok_or(VmError::EmptyRegister {
index: self.register_stack.len().saturating_sub(1), index: self.register_stack.len().saturating_sub(1),

View File

@ -1,5 +1,13 @@
use dust_lang::*; 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] #[test]
fn if_else() { fn if_else() {
assert_eq!(run("if true { 1 } else { 2 }"), Ok(Some(Value::integer(1)))); assert_eq!(run("if true { 1 } else { 2 }"), Ok(Some(Value::integer(1))));