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))
}
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> {
self.get_last_instruction()
.map(|(instruction, _)| instruction.operation())

View File

@ -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());
}
}

View File

@ -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<u8> 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<Operation> 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"),
}
}
}

View File

@ -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(())
}

View File

@ -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![]
))

View File

@ -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<Value, VmError> {
fn _clone_mutable(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
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<Value, VmError> {
fn _clone_as_variable(&mut self, local: Local, position: Span) -> Result<Value, VmError> {
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<Value, VmError> {
fn _pop(&mut self, position: Span) -> Result<Value, VmError> {
if let Some(register) = self.register_stack.pop() {
let value = register.ok_or(VmError::EmptyRegister {
index: self.register_stack.len().saturating_sub(1),

View File

@ -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))));