Begin reworking jump instructions
This commit is contained in:
parent
8db37bcdfd
commit
1da61f0873
@ -59,6 +59,10 @@ impl Chunk {
|
|||||||
self.instructions.is_empty()
|
self.instructions.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn instructions(&self) -> &[(Instruction, Span)] {
|
||||||
|
&self.instructions
|
||||||
|
}
|
||||||
|
|
||||||
pub fn instructions_mut(&mut self) -> &mut Vec<(Instruction, Span)> {
|
pub fn instructions_mut(&mut self) -> &mut Vec<(Instruction, Span)> {
|
||||||
&mut self.instructions
|
&mut self.instructions
|
||||||
}
|
}
|
||||||
@ -282,8 +286,8 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
const INSTRUCTION_HEADER: [&'static str; 4] = [
|
const INSTRUCTION_HEADER: [&'static str; 4] = [
|
||||||
"Instructions",
|
"Instructions",
|
||||||
"------------",
|
"------------",
|
||||||
"INDEX BYTECODE OPERATION INFO JUMP POSITION ",
|
"INDEX BYTECODE OPERATION INFO POSITION ",
|
||||||
"----- -------- --------------- -------------------- -------- -------------",
|
"----- -------- --------------- ------------------------- -------------",
|
||||||
];
|
];
|
||||||
|
|
||||||
const CONSTANT_HEADER: [&'static str; 4] =
|
const CONSTANT_HEADER: [&'static str; 4] =
|
||||||
@ -495,30 +499,10 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
for (index, (instruction, position)) in self.chunk.instructions.iter().enumerate() {
|
for (index, (instruction, position)) in self.chunk.instructions.iter().enumerate() {
|
||||||
let position = position.to_string();
|
let position = position.to_string();
|
||||||
let operation = instruction.operation().to_string();
|
let operation = instruction.operation().to_string();
|
||||||
let (info, jump_offset) = instruction.disassembly_info(Some(self.chunk));
|
let info = instruction.disassembly_info(Some(self.chunk));
|
||||||
let info = if let Some(info) = info {
|
|
||||||
info
|
|
||||||
} else {
|
|
||||||
" ".to_string()
|
|
||||||
};
|
|
||||||
let jump_offset = if let Some(jump_offset) = jump_offset {
|
|
||||||
let index = index as isize;
|
|
||||||
let jump_index = {
|
|
||||||
if jump_offset > 0 {
|
|
||||||
index + (jump_offset + 1)
|
|
||||||
} else {
|
|
||||||
index + jump_offset
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
format!("{index} -> {jump_index}")
|
|
||||||
} else {
|
|
||||||
" ".to_string()
|
|
||||||
};
|
|
||||||
let bytecode = u32::from(instruction);
|
let bytecode = u32::from(instruction);
|
||||||
let instruction_display = format!(
|
let instruction_display =
|
||||||
"{index:<5} {bytecode:<08X} {operation:15} {info:20} {jump_offset:8} {position:13}"
|
format!("{index:<5} {bytecode:<08X} {operation:15} {info:25} {position:13}");
|
||||||
);
|
|
||||||
|
|
||||||
push_details(&instruction_display, &mut disassembly);
|
push_details(&instruction_display, &mut disassembly);
|
||||||
}
|
}
|
||||||
|
@ -207,11 +207,10 @@ impl Instruction {
|
|||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn jump(offset: u8, is_positive: bool) -> Instruction {
|
pub fn jump(jump_to: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Jump as u32);
|
let mut instruction = Instruction(Operation::Jump as u32);
|
||||||
|
|
||||||
instruction.set_b(offset);
|
instruction.set_b(jump_to);
|
||||||
instruction.set_c(if is_positive { 1 } else { 0 });
|
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -346,7 +345,7 @@ impl Instruction {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> (Option<String>, Option<isize>) {
|
pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> String {
|
||||||
let format_arguments = || {
|
let format_arguments = || {
|
||||||
let first_argument = if self.b_is_constant() {
|
let first_argument = if self.b_is_constant() {
|
||||||
format!("C{}", self.b())
|
format!("C{}", self.b())
|
||||||
@ -361,49 +360,43 @@ impl Instruction {
|
|||||||
|
|
||||||
(first_argument, second_argument)
|
(first_argument, second_argument)
|
||||||
};
|
};
|
||||||
let mut jump_offset = None;
|
|
||||||
|
|
||||||
let info = match self.operation() {
|
match self.operation() {
|
||||||
Operation::Move => Some(format!("R{} = R{}", self.a(), self.b())),
|
Operation::Move => format!("R{} = R{}", self.a(), self.b()),
|
||||||
Operation::Close => {
|
Operation::Close => {
|
||||||
let from_register = self.b();
|
let from_register = self.b();
|
||||||
let to_register = self.c().saturating_sub(1);
|
let to_register = self.c().saturating_sub(1);
|
||||||
|
|
||||||
Some(format!("R{from_register}..=R{to_register}"))
|
format!("R{from_register}..=R{to_register}")
|
||||||
}
|
}
|
||||||
Operation::LoadBoolean => {
|
Operation::LoadBoolean => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
let boolean = self.b_as_boolean();
|
let boolean = self.b_as_boolean();
|
||||||
let jump = self.c_as_boolean();
|
let jump = self.c_as_boolean();
|
||||||
let info = if jump {
|
|
||||||
jump_offset = Some(1);
|
|
||||||
|
|
||||||
format!("R{to_register} = {boolean} && JUMP")
|
if jump {
|
||||||
|
format!("R{to_register} = {boolean} && SKIP")
|
||||||
} else {
|
} else {
|
||||||
format!("R{to_register} = {boolean}")
|
format!("R{to_register} = {boolean}")
|
||||||
};
|
}
|
||||||
|
|
||||||
Some(info)
|
|
||||||
}
|
}
|
||||||
Operation::LoadConstant => {
|
Operation::LoadConstant => {
|
||||||
let register_index = self.a();
|
let register_index = self.a();
|
||||||
let constant_index = self.b();
|
let constant_index = self.b();
|
||||||
let jump = if self.c_as_boolean() {
|
let jump = self.c_as_boolean();
|
||||||
jump_offset = Some(1);
|
|
||||||
|
|
||||||
"&& JUMP"
|
if jump {
|
||||||
|
format!("R{register_index} = C{constant_index} && SKIP")
|
||||||
} else {
|
} else {
|
||||||
""
|
format!("R{register_index} = C{constant_index}")
|
||||||
};
|
}
|
||||||
|
|
||||||
Some(format!("R{register_index} = C{constant_index} {jump}",))
|
|
||||||
}
|
}
|
||||||
Operation::LoadList => {
|
Operation::LoadList => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
let first_index = self.b();
|
let first_index = self.b();
|
||||||
let last_index = self.c();
|
let last_index = self.c();
|
||||||
|
|
||||||
Some(format!("R{to_register} = [R{first_index}..=R{last_index}]",))
|
format!("R{to_register} = [R{first_index}..=R{last_index}]",)
|
||||||
}
|
}
|
||||||
Operation::LoadSelf => {
|
Operation::LoadSelf => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
@ -416,7 +409,7 @@ impl Instruction {
|
|||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Some(format!("R{to_register} = {name}"))
|
format!("R{to_register} = {name}")
|
||||||
}
|
}
|
||||||
Operation::DefineLocal => {
|
Operation::DefineLocal => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
@ -431,14 +424,12 @@ impl Instruction {
|
|||||||
};
|
};
|
||||||
let mutable_display = if self.c_as_boolean() { "mut" } else { "" };
|
let mutable_display = if self.c_as_boolean() { "mut" } else { "" };
|
||||||
|
|
||||||
Some(format!(
|
format!("L{local_index} = R{to_register} {mutable_display} {identifier_display}")
|
||||||
"L{local_index} = R{to_register} {mutable_display} {identifier_display}"
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Operation::GetLocal => {
|
Operation::GetLocal => {
|
||||||
let local_index = self.b();
|
let local_index = self.b();
|
||||||
|
|
||||||
Some(format!("R{} = L{}", self.a(), local_index))
|
format!("R{} = L{}", self.a(), local_index)
|
||||||
}
|
}
|
||||||
Operation::SetLocal => {
|
Operation::SetLocal => {
|
||||||
let local_index = self.b();
|
let local_index = self.b();
|
||||||
@ -451,60 +442,43 @@ impl Instruction {
|
|||||||
"???".to_string()
|
"???".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(format!(
|
format!("L{} = R{} {}", local_index, self.a(), identifier_display)
|
||||||
"L{} = R{} {}",
|
|
||||||
local_index,
|
|
||||||
self.a(),
|
|
||||||
identifier_display
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Operation::Add => {
|
Operation::Add => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
Some(format!(
|
format!("R{to_register} = {first_argument} + {second_argument}",)
|
||||||
"R{to_register} = {first_argument} + {second_argument}",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Operation::Subtract => {
|
Operation::Subtract => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
Some(format!(
|
format!("R{to_register} = {first_argument} - {second_argument}",)
|
||||||
"R{to_register} = {first_argument} - {second_argument}",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Operation::Multiply => {
|
Operation::Multiply => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
Some(format!(
|
format!("R{to_register} = {first_argument} * {second_argument}",)
|
||||||
"R{to_register} = {first_argument} * {second_argument}",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Operation::Divide => {
|
Operation::Divide => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
Some(format!(
|
format!("R{to_register} = {first_argument} / {second_argument}",)
|
||||||
"R{to_register} = {first_argument} / {second_argument}",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Operation::Modulo => {
|
Operation::Modulo => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
Some(format!(
|
format!("R{to_register} = {first_argument} % {second_argument}",)
|
||||||
"R{to_register} = {first_argument} % {second_argument}",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Operation::Test => {
|
Operation::Test => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
let test_value = self.c_as_boolean();
|
let test_value = self.c_as_boolean();
|
||||||
|
|
||||||
jump_offset = Some(1);
|
format!("if R{to_register} != {test_value} {{ SKIP }}")
|
||||||
|
|
||||||
Some(format!("if R{to_register} != {test_value} {{ JUMP }}",))
|
|
||||||
}
|
}
|
||||||
Operation::TestSet => {
|
Operation::TestSet => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
@ -512,39 +486,26 @@ impl Instruction {
|
|||||||
let test_value = self.c_as_boolean();
|
let test_value = self.c_as_boolean();
|
||||||
let bang = if test_value { "" } else { "!" };
|
let bang = if test_value { "" } else { "!" };
|
||||||
|
|
||||||
jump_offset = Some(1);
|
format!("if {bang}R{to_register} {{ R{to_register} = R{argument} }}",)
|
||||||
|
|
||||||
Some(format!(
|
|
||||||
"if {bang}R{to_register} {{ R{to_register} = R{argument} }}",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Operation::Equal => {
|
Operation::Equal => {
|
||||||
let comparison_symbol = if self.a_as_boolean() { "==" } else { "!=" };
|
let comparison_symbol = if self.a_as_boolean() { "==" } else { "!=" };
|
||||||
|
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
jump_offset = Some(1);
|
|
||||||
|
|
||||||
Some(format!(
|
format!("if {first_argument} {comparison_symbol} {second_argument} {{ SKIP }}")
|
||||||
"if {first_argument} {comparison_symbol} {second_argument} {{ JUMP }}",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Operation::Less => {
|
Operation::Less => {
|
||||||
let comparison_symbol = if self.a_as_boolean() { "<" } else { ">=" };
|
let comparison_symbol = if self.a_as_boolean() { "<" } else { ">=" };
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
jump_offset = Some(1);
|
|
||||||
|
|
||||||
Some(format!(
|
format!("if {first_argument} {comparison_symbol} {second_argument} {{ SKIP }}")
|
||||||
"if {first_argument} {comparison_symbol} {second_argument}",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Operation::LessEqual => {
|
Operation::LessEqual => {
|
||||||
let comparison_symbol = if self.a_as_boolean() { "<=" } else { ">" };
|
let comparison_symbol = if self.a_as_boolean() { "<=" } else { ">" };
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
jump_offset = Some(1);
|
|
||||||
|
|
||||||
Some(format!(
|
format!("if {first_argument} {comparison_symbol} {second_argument} {{ SKIP }}")
|
||||||
"if {first_argument} {comparison_symbol} {second_argument}",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Operation::Negate => {
|
Operation::Negate => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
@ -554,7 +515,7 @@ impl Instruction {
|
|||||||
format!("R{}", self.b())
|
format!("R{}", self.b())
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(format!("R{to_register} = -{argument}"))
|
format!("R{to_register} = -{argument}")
|
||||||
}
|
}
|
||||||
Operation::Not => {
|
Operation::Not => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
@ -564,19 +525,12 @@ impl Instruction {
|
|||||||
format!("R{}", self.b())
|
format!("R{}", self.b())
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(format!("R{to_register} = !{argument}"))
|
format!("R{to_register} = !{argument}")
|
||||||
}
|
}
|
||||||
Operation::Jump => {
|
Operation::Jump => {
|
||||||
let offset = self.b() as isize;
|
let jump_to = self.b();
|
||||||
let is_positive = self.c_as_boolean();
|
|
||||||
|
|
||||||
if is_positive {
|
format!("JUMP TO {jump_to}")
|
||||||
jump_offset = Some(offset);
|
|
||||||
} else {
|
|
||||||
jump_offset = Some(-offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
Operation::Call => {
|
Operation::Call => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
@ -598,12 +552,18 @@ impl Instruction {
|
|||||||
|
|
||||||
output.push(')');
|
output.push(')');
|
||||||
|
|
||||||
Some(output)
|
output
|
||||||
}
|
}
|
||||||
Operation::Return => None,
|
Operation::Return => {
|
||||||
};
|
let should_return_value = self.b_as_boolean();
|
||||||
|
|
||||||
(info, jump_offset)
|
if should_return_value {
|
||||||
|
"->".to_string()
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -818,11 +778,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn jump() {
|
fn jump() {
|
||||||
let instruction = Instruction::jump(4, true);
|
let instruction = Instruction::jump(4);
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Jump);
|
assert_eq!(instruction.operation(), Operation::Jump);
|
||||||
assert_eq!(instruction.b(), 4);
|
assert_eq!(instruction.b(), 4);
|
||||||
assert!(instruction.c_as_boolean());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -171,6 +171,31 @@ impl<'src> Parser<'src> {
|
|||||||
Some(operations)
|
Some(operations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_last_jump_mut(&mut self) -> Option<&mut Instruction> {
|
||||||
|
self.current_statement
|
||||||
|
.iter_mut()
|
||||||
|
.rev()
|
||||||
|
.find_map(|(instruction, _)| {
|
||||||
|
if let Operation::Jump = instruction.operation() {
|
||||||
|
Some(instruction)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
self.chunk
|
||||||
|
.instructions_mut()
|
||||||
|
.iter_mut()
|
||||||
|
.find_map(|(instruction, _)| {
|
||||||
|
if let Operation::Jump = instruction.operation() {
|
||||||
|
Some(instruction)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), ParseError> {
|
fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), ParseError> {
|
||||||
let constant_index = self.chunk.push_constant(value, position)?;
|
let constant_index = self.chunk.push_constant(value, position)?;
|
||||||
let register = self.next_register();
|
let register = self.next_register();
|
||||||
@ -620,7 +645,10 @@ impl<'src> Parser<'src> {
|
|||||||
let register = self.next_register();
|
let register = self.next_register();
|
||||||
|
|
||||||
self.emit_instruction(instruction, operator_position);
|
self.emit_instruction(instruction, operator_position);
|
||||||
self.emit_instruction(Instruction::jump(1, true), operator_position);
|
|
||||||
|
let jump_to = (self.chunk.len() + self.current_statement.len() + 2) as u8;
|
||||||
|
|
||||||
|
self.emit_instruction(Instruction::jump(jump_to), operator_position);
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
Instruction::load_boolean(register, true, true),
|
Instruction::load_boolean(register, true, true),
|
||||||
operator_position,
|
operator_position,
|
||||||
@ -661,7 +689,10 @@ impl<'src> Parser<'src> {
|
|||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.emit_instruction(left_instruction, left_position);
|
self.emit_instruction(left_instruction, left_position);
|
||||||
self.emit_instruction(instruction, operator_position);
|
self.emit_instruction(instruction, operator_position);
|
||||||
self.emit_instruction(Instruction::jump(1, true), operator_position);
|
|
||||||
|
let jump_to = (self.chunk.len() + self.current_statement.len() + 2) as u8;
|
||||||
|
|
||||||
|
self.emit_instruction(Instruction::jump(jump_to), operator_position);
|
||||||
self.parse_sub_expression(&rule.precedence)?;
|
self.parse_sub_expression(&rule.precedence)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -863,6 +894,12 @@ impl<'src> Parser<'src> {
|
|||||||
self.parse_block(block_allowed)?;
|
self.parse_block(block_allowed)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let if_end = self.chunk.len() + self.current_statement.len();
|
||||||
|
|
||||||
|
if let Some(if_jump) = self.get_last_jump_mut() {
|
||||||
|
if_jump.set_b(if_end as u8);
|
||||||
|
}
|
||||||
|
|
||||||
let last_operation = self
|
let last_operation = self
|
||||||
.current_statement
|
.current_statement
|
||||||
.last()
|
.last()
|
||||||
@ -891,6 +928,22 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
if let Token::LeftCurlyBrace = self.current_token {
|
if let Token::LeftCurlyBrace = self.current_token {
|
||||||
self.parse_block(block_allowed)?;
|
self.parse_block(block_allowed)?;
|
||||||
|
self.commit_current_statement();
|
||||||
|
|
||||||
|
let else_end = (self.chunk.len() + self.current_statement.len()) as u8;
|
||||||
|
|
||||||
|
if let Operation::LoadBoolean | Operation::LoadConstant = self
|
||||||
|
.chunk
|
||||||
|
.get_instruction(if_end, self.current_position)
|
||||||
|
.map(|(instruction, _)| instruction.operation())?
|
||||||
|
{
|
||||||
|
} else {
|
||||||
|
self.chunk.insert_instruction(
|
||||||
|
if_end,
|
||||||
|
Instruction::jump(else_end + 1),
|
||||||
|
self.current_position,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -908,6 +961,9 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
fn parse_while(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
fn parse_while(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
|
let jump_start = (self.chunk.len() + self.current_statement.len()) as u8;
|
||||||
|
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
if let Some(
|
if let Some(
|
||||||
@ -916,30 +972,25 @@ impl<'src> Parser<'src> {
|
|||||||
{
|
{
|
||||||
self.current_statement.pop();
|
self.current_statement.pop();
|
||||||
self.current_statement.pop();
|
self.current_statement.pop();
|
||||||
self.current_statement.pop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let jump_start = self.chunk.len() + self.current_statement.len();
|
|
||||||
|
|
||||||
self.parse_block(Allowed {
|
self.parse_block(Allowed {
|
||||||
assignment: true,
|
assignment: true,
|
||||||
explicit_return: allowed.explicit_return,
|
explicit_return: allowed.explicit_return,
|
||||||
implicit_return: false,
|
implicit_return: false,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let jump_end = self.chunk.len() + self.current_statement.len();
|
let jump_end = (self.chunk.len() + self.current_statement.len()) as u8;
|
||||||
let jump_distance = jump_end.abs_diff(jump_start) as u8 + 1;
|
|
||||||
let jump_back = Instruction::jump(jump_distance + 1, false);
|
if let Some(jump) = self.get_last_jump_mut() {
|
||||||
|
jump.set_b(jump_end + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let jump_back = Instruction::jump(jump_start);
|
||||||
|
|
||||||
self.emit_instruction(jump_back, self.current_position);
|
self.emit_instruction(jump_back, self.current_position);
|
||||||
self.commit_current_statement();
|
self.commit_current_statement();
|
||||||
|
|
||||||
self.chunk.insert_instruction(
|
|
||||||
jump_start,
|
|
||||||
Instruction::jump(jump_distance, true),
|
|
||||||
self.current_position,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -956,6 +1007,7 @@ impl<'src> Parser<'src> {
|
|||||||
let parsed_expression = self
|
let parsed_expression = self
|
||||||
.current_statement
|
.current_statement
|
||||||
.last()
|
.last()
|
||||||
|
.or_else(|| self.chunk.instructions().last())
|
||||||
.map(|(instruction, _)| instruction.yields_value())
|
.map(|(instruction, _)| instruction.yields_value())
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
let end_of_statement = matches!(
|
let end_of_statement = matches!(
|
||||||
|
@ -58,10 +58,7 @@ impl Vm {
|
|||||||
self.ip - 1,
|
self.ip - 1,
|
||||||
position,
|
position,
|
||||||
instruction.operation(),
|
instruction.operation(),
|
||||||
instruction
|
instruction.disassembly_info(Some(&self.chunk))
|
||||||
.disassembly_info(Some(&self.chunk))
|
|
||||||
.0
|
|
||||||
.unwrap_or_default()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
match instruction.operation() {
|
match instruction.operation() {
|
||||||
@ -348,15 +345,9 @@ impl Vm {
|
|||||||
self.set(instruction.a(), not, position)?;
|
self.set(instruction.a(), not, position)?;
|
||||||
}
|
}
|
||||||
Operation::Jump => {
|
Operation::Jump => {
|
||||||
let offset = instruction.b();
|
let jump_to = instruction.b();
|
||||||
let is_positive = instruction.c_as_boolean();
|
|
||||||
let new_ip = if is_positive {
|
|
||||||
self.ip + offset as usize
|
|
||||||
} else {
|
|
||||||
self.ip - (offset + 1) as usize
|
|
||||||
};
|
|
||||||
|
|
||||||
self.ip = new_ip;
|
self.ip = jump_to as usize;
|
||||||
}
|
}
|
||||||
Operation::Call => {
|
Operation::Call => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
|
@ -59,7 +59,7 @@ fn and() {
|
|||||||
vec![
|
vec![
|
||||||
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
||||||
(Instruction::test(0, false), Span(5, 7)),
|
(Instruction::test(0, false), Span(5, 7)),
|
||||||
(Instruction::jump(1, true), Span(5, 7)),
|
(Instruction::jump(4), Span(5, 7)),
|
||||||
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
||||||
(Instruction::r#return(true), Span(13, 13)),
|
(Instruction::r#return(true), Span(13, 13)),
|
||||||
],
|
],
|
||||||
@ -258,7 +258,7 @@ fn equal() {
|
|||||||
.set_c_is_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 4)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 4)),
|
(Instruction::jump(3), 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::r#return(true), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
@ -286,7 +286,7 @@ fn equality_assignment_long() {
|
|||||||
.set_c_is_constant(),
|
.set_c_is_constant(),
|
||||||
Span(13, 15)
|
Span(13, 15)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(13, 15)),
|
(Instruction::jump(3), Span(13, 15)),
|
||||||
(Instruction::load_boolean(0, true, true), Span(20, 24)),
|
(Instruction::load_boolean(0, true, true), Span(20, 24)),
|
||||||
(Instruction::load_boolean(0, false, false), Span(34, 39)),
|
(Instruction::load_boolean(0, false, false), Span(34, 39)),
|
||||||
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
||||||
@ -316,7 +316,7 @@ fn equality_assignment_short() {
|
|||||||
.set_c_is_constant(),
|
.set_c_is_constant(),
|
||||||
Span(10, 12)
|
Span(10, 12)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(10, 12)),
|
(Instruction::jump(3), Span(10, 12)),
|
||||||
(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)),
|
||||||
@ -479,7 +479,7 @@ fn greater() {
|
|||||||
.set_c_is_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 3)),
|
(Instruction::jump(3), 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::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
@ -507,7 +507,7 @@ fn greater_than_or_equal() {
|
|||||||
.set_c_is_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 4)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 4)),
|
(Instruction::jump(3), 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::r#return(true), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
@ -521,7 +521,54 @@ fn greater_than_or_equal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn if_else_expression() {
|
fn if_else_complex() {
|
||||||
|
let source = "
|
||||||
|
if 1 == 1 {
|
||||||
|
1; 2; 3; 4;
|
||||||
|
} else {
|
||||||
|
1; 2; 3; 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(9, 11)
|
||||||
|
),
|
||||||
|
(Instruction::jump(7), Span(9, 11)),
|
||||||
|
(Instruction::load_constant(0, 0, false), Span(9, 11)),
|
||||||
|
(Instruction::load_constant(1, 1, false), Span(9, 11)),
|
||||||
|
(Instruction::load_constant(2, 2, false), Span(9, 11)),
|
||||||
|
(Instruction::load_constant(3, 3, false), Span(9, 11)),
|
||||||
|
(Instruction::jump(11), Span(9, 11)),
|
||||||
|
(Instruction::load_constant(0, 0, false), Span(9, 11)),
|
||||||
|
(Instruction::load_constant(1, 1, false), Span(9, 11)),
|
||||||
|
(Instruction::load_constant(2, 2, false), Span(9, 11)),
|
||||||
|
(Instruction::load_constant(3, 3, false), Span(9, 11)),
|
||||||
|
(Instruction::r#return(true), Span(11, 11)),
|
||||||
|
],
|
||||||
|
vec![
|
||||||
|
Value::integer(1),
|
||||||
|
Value::integer(2),
|
||||||
|
Value::integer(3),
|
||||||
|
Value::integer(4),
|
||||||
|
Value::integer(1),
|
||||||
|
Value::integer(2),
|
||||||
|
Value::integer(3),
|
||||||
|
Value::integer(4)
|
||||||
|
],
|
||||||
|
vec![]
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn if_else_simple() {
|
||||||
let source = "if 1 == 1 { 2 } else { 3 }";
|
let source = "if 1 == 1 { 2 } else { 3 }";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -535,7 +582,7 @@ fn if_else_expression() {
|
|||||||
.set_c_is_constant(),
|
.set_c_is_constant(),
|
||||||
Span(5, 7)
|
Span(5, 7)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(5, 7)),
|
(Instruction::jump(3), Span(5, 7)),
|
||||||
(Instruction::load_constant(0, 2, true), Span(12, 13)),
|
(Instruction::load_constant(0, 2, true), Span(12, 13)),
|
||||||
(Instruction::load_constant(1, 3, false), Span(23, 24)),
|
(Instruction::load_constant(1, 3, false), Span(23, 24)),
|
||||||
(Instruction::r#return(true), Span(26, 26)),
|
(Instruction::r#return(true), Span(26, 26)),
|
||||||
@ -568,7 +615,7 @@ fn if_expression_false() {
|
|||||||
.set_c_is_constant(),
|
.set_c_is_constant(),
|
||||||
Span(5, 7)
|
Span(5, 7)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(5, 7)),
|
(Instruction::jump(3), Span(5, 7)),
|
||||||
(Instruction::load_constant(0, 2, false), Span(12, 13)),
|
(Instruction::load_constant(0, 2, false), Span(12, 13)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2), Value::integer(2)],
|
||||||
@ -594,7 +641,7 @@ fn if_expression_true() {
|
|||||||
.set_c_is_constant(),
|
.set_c_is_constant(),
|
||||||
Span(5, 7)
|
Span(5, 7)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(5, 7)),
|
(Instruction::jump(3), Span(5, 7)),
|
||||||
(Instruction::load_constant(0, 2, false), Span(12, 13)),
|
(Instruction::load_constant(0, 2, false), Span(12, 13)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(1), Value::integer(2)],
|
||||||
@ -620,7 +667,7 @@ fn less_than() {
|
|||||||
.set_c_is_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 3)),
|
(Instruction::jump(3), 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::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
@ -648,7 +695,7 @@ fn less_than_or_equal() {
|
|||||||
.set_c_is_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 4)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 4)),
|
(Instruction::jump(3), 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::r#return(true), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
@ -907,7 +954,7 @@ fn not_equal() {
|
|||||||
.set_c_is_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 4)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 4)),
|
(Instruction::jump(3), 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::r#return(true), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
@ -931,7 +978,7 @@ fn or() {
|
|||||||
vec![
|
vec![
|
||||||
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
||||||
(Instruction::test(0, true), Span(5, 7)),
|
(Instruction::test(0, true), Span(5, 7)),
|
||||||
(Instruction::jump(1, true), Span(5, 7)),
|
(Instruction::jump(4), Span(5, 7)),
|
||||||
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
||||||
(Instruction::r#return(true), Span(13, 13)),
|
(Instruction::r#return(true), Span(13, 13)),
|
||||||
],
|
],
|
||||||
@ -1062,7 +1109,7 @@ fn variable_and() {
|
|||||||
(Instruction::define_local(1, 1, false), Span(18, 19)),
|
(Instruction::define_local(1, 1, false), Span(18, 19)),
|
||||||
(Instruction::get_local(2, 0), Span(29, 30)),
|
(Instruction::get_local(2, 0), Span(29, 30)),
|
||||||
(Instruction::test(2, false), Span(31, 33)),
|
(Instruction::test(2, false), Span(31, 33)),
|
||||||
(Instruction::jump(1, true), Span(31, 33)),
|
(Instruction::jump(8), Span(31, 33)),
|
||||||
(Instruction::get_local(3, 1), Span(34, 35)),
|
(Instruction::get_local(3, 1), Span(34, 35)),
|
||||||
(Instruction::r#return(true), Span(35, 35)),
|
(Instruction::r#return(true), Span(35, 35)),
|
||||||
],
|
],
|
||||||
@ -1092,9 +1139,9 @@ fn r#while() {
|
|||||||
*Instruction::less(true, 0, 1).set_c_is_constant(),
|
*Instruction::less(true, 0, 1).set_c_is_constant(),
|
||||||
Span(23, 24)
|
Span(23, 24)
|
||||||
),
|
),
|
||||||
(Instruction::jump(2, true), Span(41, 42)),
|
(Instruction::jump(7), Span(23, 24)),
|
||||||
(*Instruction::add(0, 0, 2).set_c_is_constant(), Span(39, 40)),
|
(*Instruction::add(0, 0, 2).set_c_is_constant(), Span(39, 40)),
|
||||||
(Instruction::jump(3, false), Span(41, 42)),
|
(Instruction::jump(2), Span(41, 42)),
|
||||||
(Instruction::get_local(1, 0), Span(41, 42)),
|
(Instruction::get_local(1, 0), Span(41, 42)),
|
||||||
(Instruction::r#return(true), Span(42, 42)),
|
(Instruction::r#return(true), Span(42, 42)),
|
||||||
],
|
],
|
||||||
|
11
examples/assets/fibonacci.js
Normal file
11
examples/assets/fibonacci.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
function fib(n) {
|
||||||
|
if (n <= 0) {
|
||||||
|
return 0;
|
||||||
|
} else if (n === 1) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return fib(n - 1) + fib(n - 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(fib(10));
|
@ -1,11 +1,11 @@
|
|||||||
fn fib (n: int) -> int {
|
fn fib (n: int) -> int {
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
return 0;
|
0
|
||||||
} else if n == 1 {
|
} else if n == 1 {
|
||||||
return 1;
|
1
|
||||||
} else {
|
} else {
|
||||||
return fib(n - 1) + fib(n - 2);
|
fib(n - 1) + fib(n - 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fib(10)
|
fib(2)
|
||||||
|
Loading…
Reference in New Issue
Block a user