Use test for single registers in conditional expressions
This commit is contained in:
parent
47d6ea417d
commit
d0d80cf407
@ -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())
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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![]
|
||||
))
|
||||
|
@ -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),
|
||||
|
@ -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))));
|
||||
|
Loading…
Reference in New Issue
Block a user