Refactor to use index references to avoid cloning values
This commit is contained in:
parent
c1e372d7cf
commit
6e1ef77192
@ -36,10 +36,6 @@ impl Chunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scope_depth(&self) -> usize {
|
|
||||||
self.scope_depth
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.instructions.len()
|
self.instructions.len()
|
||||||
}
|
}
|
||||||
@ -55,7 +51,7 @@ impl Chunk {
|
|||||||
) -> Result<&(Instruction, Span), ChunkError> {
|
) -> Result<&(Instruction, Span), ChunkError> {
|
||||||
self.instructions
|
self.instructions
|
||||||
.get(offset)
|
.get(offset)
|
||||||
.ok_or(ChunkError::CodeIndexOfBounds { offset, position })
|
.ok_or(ChunkError::InstructionIndexOfBounds { offset, position })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_instruction(&mut self, index: usize) -> (Instruction, Span) {
|
pub fn remove_instruction(&mut self, index: usize) -> (Instruction, Span) {
|
||||||
@ -410,7 +406,7 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
pub fn disassemble(&self) -> String {
|
pub fn disassemble(&self) -> String {
|
||||||
let mut disassembly = String::with_capacity(self.predict_length());
|
let mut disassembly = String::with_capacity(self.predict_length());
|
||||||
let indent = "│ ".repeat(self.indent);
|
let indent = "│ ".repeat(self.indent);
|
||||||
let top_border = "┌".to_string() + &"─".repeat(self.width) + "┐";
|
let top_border = "┌".to_string() + &"─".repeat(self.width - 2) + "┐";
|
||||||
|
|
||||||
disassembly.push_str(&indent);
|
disassembly.push_str(&indent);
|
||||||
disassembly.push_str(&top_border);
|
disassembly.push_str(&top_border);
|
||||||
@ -418,9 +414,13 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
|
|
||||||
let center_and_style = |line: &str, style: bool| {
|
let center_and_style = |line: &str, style: bool| {
|
||||||
if style {
|
if style {
|
||||||
format!("│{line:^width$}│", line = line.bold(), width = self.width)
|
format!(
|
||||||
|
"│{line:^width$}│",
|
||||||
|
line = line.bold(),
|
||||||
|
width = self.width - 2
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
format!("│{line:^width$}│", width = self.width)
|
format!("│{line:^width$}│", width = self.width - 2)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut push = |line: &str, style: bool| {
|
let mut push = |line: &str, style: bool| {
|
||||||
@ -576,7 +576,7 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let indent = "│ ".repeat(self.indent);
|
let indent = "│ ".repeat(self.indent);
|
||||||
let bottom_border = "└".to_string() + &"─".repeat(self.width) + "┘";
|
let bottom_border = "└".to_string() + &"─".repeat(self.width - 2) + "┘";
|
||||||
|
|
||||||
disassembly.push_str(&indent);
|
disassembly.push_str(&indent);
|
||||||
disassembly.push_str(&bottom_border);
|
disassembly.push_str(&bottom_border);
|
||||||
@ -628,7 +628,7 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum ChunkError {
|
pub enum ChunkError {
|
||||||
CodeIndexOfBounds {
|
InstructionIndexOfBounds {
|
||||||
offset: usize,
|
offset: usize,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
@ -666,7 +666,7 @@ impl AnnotatedError for ChunkError {
|
|||||||
|
|
||||||
fn description(&self) -> &'static str {
|
fn description(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
ChunkError::CodeIndexOfBounds { .. } => "Code index out of bounds",
|
ChunkError::InstructionIndexOfBounds { .. } => "Instruction index out of bounds",
|
||||||
ChunkError::ConstantAlreadyUsed { .. } => "Constant already used",
|
ChunkError::ConstantAlreadyUsed { .. } => "Constant already used",
|
||||||
ChunkError::ConstantOverflow { .. } => "Constant overflow",
|
ChunkError::ConstantOverflow { .. } => "Constant overflow",
|
||||||
ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
|
ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
|
||||||
@ -679,7 +679,9 @@ impl AnnotatedError for ChunkError {
|
|||||||
|
|
||||||
fn details(&self) -> Option<String> {
|
fn details(&self) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
ChunkError::CodeIndexOfBounds { offset, .. } => Some(format!("Code index: {}", offset)),
|
ChunkError::InstructionIndexOfBounds { offset, .. } => {
|
||||||
|
Some(format!("Instruction index: {}", offset))
|
||||||
|
}
|
||||||
ChunkError::ConstantAlreadyUsed { index, .. } => {
|
ChunkError::ConstantAlreadyUsed { index, .. } => {
|
||||||
Some(format!("Constant index: {}", index))
|
Some(format!("Constant index: {}", index))
|
||||||
}
|
}
|
||||||
@ -700,7 +702,7 @@ impl AnnotatedError for ChunkError {
|
|||||||
|
|
||||||
fn position(&self) -> Span {
|
fn position(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
ChunkError::CodeIndexOfBounds { position, .. } => *position,
|
ChunkError::InstructionIndexOfBounds { position, .. } => *position,
|
||||||
ChunkError::ConstantAlreadyUsed { position, .. } => *position,
|
ChunkError::ConstantAlreadyUsed { position, .. } => *position,
|
||||||
ChunkError::ConstantIndexOutOfBounds { position, .. } => *position,
|
ChunkError::ConstantIndexOutOfBounds { position, .. } => *position,
|
||||||
ChunkError::IdentifierNotFound { position, .. } => *position,
|
ChunkError::IdentifierNotFound { position, .. } => *position,
|
||||||
|
@ -208,8 +208,12 @@ impl Instruction {
|
|||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#return() -> Instruction {
|
pub fn r#return(should_return_value: bool) -> Instruction {
|
||||||
Instruction(Operation::Return as u32)
|
let mut instruction = Instruction(Operation::Return as u32);
|
||||||
|
|
||||||
|
instruction.set_b(if should_return_value { 1 } else { 0 });
|
||||||
|
|
||||||
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn operation(&self) -> Operation {
|
pub fn operation(&self) -> Operation {
|
||||||
@ -777,8 +781,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn r#return() {
|
fn r#return() {
|
||||||
let instruction = Instruction::r#return();
|
let instruction = Instruction::r#return(true);
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Return);
|
assert_eq!(instruction.operation(), Operation::Return);
|
||||||
|
assert!(instruction.b_as_boolean());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ pub struct Parser<'src> {
|
|||||||
current_position: Span,
|
current_position: Span,
|
||||||
previous_token: Token<'src>,
|
previous_token: Token<'src>,
|
||||||
previous_position: Span,
|
previous_position: Span,
|
||||||
|
parsed_expression: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> Parser<'src> {
|
impl<'src> Parser<'src> {
|
||||||
@ -61,6 +62,7 @@ impl<'src> Parser<'src> {
|
|||||||
current_position,
|
current_position,
|
||||||
previous_token: Token::Eof,
|
previous_token: Token::Eof,
|
||||||
previous_position: Span(0, 0),
|
previous_position: Span(0, 0),
|
||||||
|
parsed_expression: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,6 +186,8 @@ impl<'src> Parser<'src> {
|
|||||||
position,
|
position,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
self.parsed_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,6 +207,8 @@ impl<'src> Parser<'src> {
|
|||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.parsed_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,6 +223,8 @@ impl<'src> Parser<'src> {
|
|||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.parsed_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,6 +245,8 @@ impl<'src> Parser<'src> {
|
|||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.parsed_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,6 +267,8 @@ impl<'src> Parser<'src> {
|
|||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.parsed_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,6 +283,8 @@ impl<'src> Parser<'src> {
|
|||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.parsed_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,7 +298,11 @@ impl<'src> Parser<'src> {
|
|||||||
},
|
},
|
||||||
Context::None,
|
Context::None,
|
||||||
)?;
|
)?;
|
||||||
self.expect(TokenKind::RightParenthesis)
|
self.expect(TokenKind::RightParenthesis)?;
|
||||||
|
|
||||||
|
self.parsed_expression = true;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_unary(&mut self, _: Allowed) -> Result<(), ParseError> {
|
fn parse_unary(&mut self, _: Allowed) -> Result<(), ParseError> {
|
||||||
@ -345,6 +363,8 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.emit_instruction(instruction, operator_position);
|
self.emit_instruction(instruction, operator_position);
|
||||||
|
|
||||||
|
self.parsed_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,16 +459,16 @@ impl<'src> Parser<'src> {
|
|||||||
current
|
current
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut new_instruction = match operator.kind() {
|
let (mut new_instruction, is_expression) = match operator.kind() {
|
||||||
TokenKind::Plus => Instruction::add(register, left, right),
|
TokenKind::Plus => (Instruction::add(register, left, right), true),
|
||||||
TokenKind::PlusEqual => Instruction::add(register, left, right),
|
TokenKind::PlusEqual => (Instruction::add(register, left, right), false),
|
||||||
TokenKind::Minus => Instruction::subtract(register, left, right),
|
TokenKind::Minus => (Instruction::subtract(register, left, right), true),
|
||||||
TokenKind::MinusEqual => Instruction::subtract(register, left, right),
|
TokenKind::MinusEqual => (Instruction::subtract(register, left, right), false),
|
||||||
TokenKind::Star => Instruction::multiply(register, left, right),
|
TokenKind::Star => (Instruction::multiply(register, left, right), true),
|
||||||
TokenKind::StarEqual => Instruction::multiply(register, left, right),
|
TokenKind::StarEqual => (Instruction::multiply(register, left, right), false),
|
||||||
TokenKind::Slash => Instruction::divide(register, left, right),
|
TokenKind::Slash => (Instruction::divide(register, left, right), true),
|
||||||
TokenKind::SlashEqual => Instruction::divide(register, left, right),
|
TokenKind::SlashEqual => (Instruction::divide(register, left, right), false),
|
||||||
TokenKind::Percent => Instruction::modulo(register, left, right),
|
TokenKind::Percent => (Instruction::modulo(register, left, right), true),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParseError::ExpectedTokenMultiple {
|
return Err(ParseError::ExpectedTokenMultiple {
|
||||||
expected: &[
|
expected: &[
|
||||||
@ -468,6 +488,8 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.parsed_expression = is_expression;
|
||||||
|
|
||||||
if left_is_constant {
|
if left_is_constant {
|
||||||
new_instruction.set_b_is_constant();
|
new_instruction.set_b_is_constant();
|
||||||
}
|
}
|
||||||
@ -549,6 +571,7 @@ impl<'src> Parser<'src> {
|
|||||||
Token::LessEqual => Instruction::less_equal(true, left.saturating_sub(1), right),
|
Token::LessEqual => Instruction::less_equal(true, left.saturating_sub(1), right),
|
||||||
Token::Greater => Instruction::less_equal(false, left.saturating_sub(1), right),
|
Token::Greater => Instruction::less_equal(false, left.saturating_sub(1), right),
|
||||||
Token::GreaterEqual => Instruction::less(false, left.saturating_sub(1), right),
|
Token::GreaterEqual => Instruction::less(false, left.saturating_sub(1), right),
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParseError::ExpectedTokenMultiple {
|
return Err(ParseError::ExpectedTokenMultiple {
|
||||||
expected: &[
|
expected: &[
|
||||||
@ -565,6 +588,8 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.parsed_expression = true;
|
||||||
|
|
||||||
if left_is_constant {
|
if left_is_constant {
|
||||||
instruction.set_b_is_constant();
|
instruction.set_b_is_constant();
|
||||||
}
|
}
|
||||||
@ -626,6 +651,8 @@ impl<'src> Parser<'src> {
|
|||||||
self.emit_instruction(Instruction::jump(1, true), operator_position);
|
self.emit_instruction(Instruction::jump(1, true), operator_position);
|
||||||
self.emit_instruction(right_instruction, right_position);
|
self.emit_instruction(right_instruction, right_position);
|
||||||
|
|
||||||
|
self.parsed_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -690,11 +717,15 @@ impl<'src> Parser<'src> {
|
|||||||
Instruction::set_local(self.current_register, local_index),
|
Instruction::set_local(self.current_register, local_index),
|
||||||
start_position,
|
start_position,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
self.parsed_expression = false;
|
||||||
} else {
|
} else {
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
Instruction::get_local(self.current_register, local_index),
|
Instruction::get_local(self.current_register, local_index),
|
||||||
self.previous_position,
|
self.previous_position,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
self.parsed_expression = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -793,6 +824,9 @@ impl<'src> Parser<'src> {
|
|||||||
Instruction::load_list(self.current_register, start_register, end_register),
|
Instruction::load_list(self.current_register, start_register, end_register),
|
||||||
Span(start, end),
|
Span(start, end),
|
||||||
);
|
);
|
||||||
|
self.increment_register()?;
|
||||||
|
|
||||||
|
self.parsed_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -847,7 +881,11 @@ 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.parsed_expression = true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
self.parsed_expression = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -888,6 +926,8 @@ impl<'src> Parser<'src> {
|
|||||||
self.chunk
|
self.chunk
|
||||||
.insert_instruction(jump_end, jump_back, self.current_position);
|
.insert_instruction(jump_end, jump_back, self.current_position);
|
||||||
|
|
||||||
|
self.parsed_expression = false;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -915,11 +955,10 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_expression = self.chunk.get_last_instruction()?.0.is_expression();
|
|
||||||
let has_semicolon = self.allow(TokenKind::Semicolon)?;
|
let has_semicolon = self.allow(TokenKind::Semicolon)?;
|
||||||
|
|
||||||
if !has_semicolon && is_expression && allowed.implicit_return {
|
if !has_semicolon && self.parsed_expression && allowed.implicit_return {
|
||||||
self.emit_instruction(Instruction::r#return(), self.current_position);
|
self.emit_instruction(Instruction::r#return(true), self.current_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -936,7 +975,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
if !matches!(
|
let has_return_value = if !matches!(
|
||||||
self.current_token,
|
self.current_token,
|
||||||
Token::Semicolon | Token::RightCurlyBrace
|
Token::Semicolon | Token::RightCurlyBrace
|
||||||
) {
|
) {
|
||||||
@ -948,11 +987,17 @@ impl<'src> Parser<'src> {
|
|||||||
},
|
},
|
||||||
Context::None,
|
Context::None,
|
||||||
)?;
|
)?;
|
||||||
}
|
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
let end = self.current_position.1;
|
let end = self.current_position.1;
|
||||||
|
|
||||||
self.emit_instruction(Instruction::r#return(), Span(start, end));
|
self.emit_instruction(Instruction::r#return(has_return_value), Span(start, end));
|
||||||
|
|
||||||
|
self.parsed_expression = false;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1015,6 +1060,8 @@ impl<'src> Parser<'src> {
|
|||||||
position,
|
position,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
self.parsed_expression = false;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1093,6 +1140,8 @@ impl<'src> Parser<'src> {
|
|||||||
self.lexer.skip_to(function_end);
|
self.lexer.skip_to(function_end);
|
||||||
self.emit_constant(function, Span(function_start, function_end))?;
|
self.emit_constant(function, Span(function_start, function_end))?;
|
||||||
|
|
||||||
|
self.parsed_expression = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
use std::mem::replace;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parse, AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, Local, Operation,
|
parse, AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, Operation, Span,
|
||||||
Span, Value, ValueError,
|
Value, ValueError,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||||
@ -14,9 +16,9 @@ pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
|||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct Vm {
|
pub struct Vm {
|
||||||
chunk: Chunk,
|
|
||||||
ip: usize,
|
ip: usize,
|
||||||
register_stack: Vec<Option<Value>>,
|
chunk: Chunk,
|
||||||
|
stack: Vec<Register>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vm {
|
impl Vm {
|
||||||
@ -24,9 +26,9 @@ impl Vm {
|
|||||||
|
|
||||||
pub fn new(chunk: Chunk) -> Self {
|
pub fn new(chunk: Chunk) -> Self {
|
||||||
Self {
|
Self {
|
||||||
chunk,
|
|
||||||
ip: 0,
|
ip: 0,
|
||||||
register_stack: Vec::new(),
|
chunk,
|
||||||
|
stack: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,26 +58,35 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() {
|
while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() {
|
||||||
let ip = self.ip - 1;
|
log::info!(
|
||||||
let operation = instruction.operation();
|
"{} | {} | {} | {}",
|
||||||
let position_display = position.to_string();
|
self.ip - 1,
|
||||||
|
position,
|
||||||
log::info!("{ip:^3} | {position_display:^10} | {operation}");
|
instruction.operation(),
|
||||||
|
instruction
|
||||||
|
.disassembly_info(Some(&self.chunk))
|
||||||
|
.0
|
||||||
|
.unwrap_or_default()
|
||||||
|
);
|
||||||
|
|
||||||
match instruction.operation() {
|
match instruction.operation() {
|
||||||
Operation::Move => {
|
Operation::Move => {
|
||||||
let from = instruction.b();
|
let to_register = instruction.a();
|
||||||
let to = instruction.a();
|
let from_register = instruction.b();
|
||||||
let value = self.take(from, position)?;
|
let value = self.take(from_register, to_register, position)?;
|
||||||
|
|
||||||
self.insert(value, to, position)?;
|
self.set(to_register, value, position)?;
|
||||||
}
|
}
|
||||||
Operation::Close => {
|
Operation::Close => {
|
||||||
let from = instruction.b();
|
let from_register = instruction.b();
|
||||||
let to = instruction.c();
|
let to_register = instruction.c();
|
||||||
|
|
||||||
for register_index in from..to {
|
if self.stack.len() < to_register as usize {
|
||||||
self.register_stack[register_index as usize] = None;
|
return Err(VmError::StackUnderflow { position });
|
||||||
|
}
|
||||||
|
|
||||||
|
for register_index in from_register..to_register {
|
||||||
|
self.stack[register_index as usize] = Register::Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::LoadBoolean => {
|
Operation::LoadBoolean => {
|
||||||
@ -84,7 +95,7 @@ impl Vm {
|
|||||||
let skip = instruction.c_as_boolean();
|
let skip = instruction.c_as_boolean();
|
||||||
let value = Value::boolean(boolean);
|
let value = Value::boolean(boolean);
|
||||||
|
|
||||||
self.insert(value, to_register, position)?;
|
self.set(to_register, value, position)?;
|
||||||
|
|
||||||
if skip {
|
if skip {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
@ -94,9 +105,8 @@ impl Vm {
|
|||||||
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 jump = instruction.c_as_boolean();
|
||||||
let value = self.chunk.take_constant(from_constant, position)?;
|
|
||||||
|
|
||||||
self.insert(value, to_register, position)?;
|
self.set_constant(to_register, from_constant, position)?;
|
||||||
|
|
||||||
if jump {
|
if jump {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
@ -110,7 +120,7 @@ impl Vm {
|
|||||||
let mut list = Vec::with_capacity(length as usize);
|
let mut list = Vec::with_capacity(length as usize);
|
||||||
|
|
||||||
for register_index in first_register..=last_register {
|
for register_index in first_register..=last_register {
|
||||||
let value = match self.take(register_index, position) {
|
let value = match self.take(register_index, to_register, position) {
|
||||||
Ok(value) => value,
|
Ok(value) => value,
|
||||||
Err(VmError::EmptyRegister { .. }) => continue,
|
Err(VmError::EmptyRegister { .. }) => continue,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
@ -119,7 +129,7 @@ impl Vm {
|
|||||||
list.push(value);
|
list.push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.insert(Value::list(list), to_register, position)?;
|
self.set(to_register, Value::list(list), position)?;
|
||||||
}
|
}
|
||||||
Operation::DefineLocal => {
|
Operation::DefineLocal => {
|
||||||
let from_register = instruction.a();
|
let from_register = instruction.a();
|
||||||
@ -128,22 +138,23 @@ impl Vm {
|
|||||||
self.chunk.define_local(to_local, from_register, position)?;
|
self.chunk.define_local(to_local, from_register, position)?;
|
||||||
}
|
}
|
||||||
Operation::GetLocal => {
|
Operation::GetLocal => {
|
||||||
let register_index = instruction.a();
|
let to_register = instruction.a();
|
||||||
let local_index = instruction.b();
|
let local_index = instruction.b();
|
||||||
let local = self.chunk.get_local(local_index, position)?;
|
let local = self.chunk.get_local(local_index, position)?;
|
||||||
let value = if let Some(index) = local.register_index {
|
let from_register =
|
||||||
self.take(index, position)?
|
local
|
||||||
} else {
|
.register_index
|
||||||
return Err(VmError::UndefinedVariable {
|
.ok_or_else(|| VmError::UndefinedVariable {
|
||||||
identifier: local.identifier.clone(),
|
identifier: local.identifier.clone(),
|
||||||
position,
|
position,
|
||||||
});
|
})?;
|
||||||
};
|
|
||||||
|
|
||||||
self.insert(value, register_index, position)?;
|
let value = self.take(from_register, to_register, position)?;
|
||||||
|
|
||||||
|
self.set(to_register, value, position)?;
|
||||||
}
|
}
|
||||||
Operation::SetLocal => {
|
Operation::SetLocal => {
|
||||||
let register_index = instruction.a();
|
let register = instruction.a();
|
||||||
let local_index = instruction.b();
|
let local_index = instruction.b();
|
||||||
let local = self.chunk.get_local(local_index, position)?;
|
let local = self.chunk.get_local(local_index, position)?;
|
||||||
|
|
||||||
@ -154,15 +165,7 @@ impl Vm {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_value = if instruction.b_is_constant() {
|
self.chunk.define_local(local_index, register, position)?;
|
||||||
self.chunk.take_constant(register_index, position)?
|
|
||||||
} else {
|
|
||||||
self.take(register_index, position)?
|
|
||||||
};
|
|
||||||
|
|
||||||
self.insert(new_value, register_index, position)?;
|
|
||||||
self.chunk
|
|
||||||
.define_local(local_index, register_index, position)?;
|
|
||||||
}
|
}
|
||||||
Operation::Add => {
|
Operation::Add => {
|
||||||
let (left, right) = get_arguments(self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
@ -170,7 +173,7 @@ impl Vm {
|
|||||||
.add(right)
|
.add(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.insert(sum, instruction.a(), position)?;
|
self.set(instruction.a(), sum, position)?;
|
||||||
}
|
}
|
||||||
Operation::Subtract => {
|
Operation::Subtract => {
|
||||||
let (left, right) = get_arguments(self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
@ -178,16 +181,15 @@ impl Vm {
|
|||||||
.subtract(right)
|
.subtract(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.insert(difference, instruction.a(), position)?;
|
self.set(instruction.a(), difference, position)?;
|
||||||
}
|
}
|
||||||
Operation::Multiply => {
|
Operation::Multiply => {
|
||||||
let (left, right) = get_arguments(self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
|
|
||||||
let product = left
|
let product = left
|
||||||
.multiply(right)
|
.multiply(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.insert(product, instruction.a(), position)?;
|
self.set(instruction.a(), product, position)?;
|
||||||
}
|
}
|
||||||
Operation::Divide => {
|
Operation::Divide => {
|
||||||
let (left, right) = get_arguments(self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
@ -195,7 +197,7 @@ impl Vm {
|
|||||||
.divide(right)
|
.divide(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.insert(quotient, instruction.a(), position)?;
|
self.set(instruction.a(), quotient, position)?;
|
||||||
}
|
}
|
||||||
Operation::Modulo => {
|
Operation::Modulo => {
|
||||||
let (left, right) = get_arguments(self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
@ -203,7 +205,7 @@ impl Vm {
|
|||||||
.modulo(right)
|
.modulo(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.insert(remainder, instruction.a(), position)?;
|
self.set(instruction.a(), remainder, position)?;
|
||||||
}
|
}
|
||||||
Operation::Test => {
|
Operation::Test => {
|
||||||
let register = instruction.a();
|
let register = instruction.a();
|
||||||
@ -222,22 +224,28 @@ impl Vm {
|
|||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let argument = instruction.b();
|
let argument = instruction.b();
|
||||||
let test_value = instruction.c_as_boolean();
|
let test_value = instruction.c_as_boolean();
|
||||||
let value = self.clone(argument, position)?;
|
let borrowed_value = self.get(argument, position)?;
|
||||||
let boolean = value.as_boolean().ok_or_else(|| VmError::ExpectedBoolean {
|
let boolean =
|
||||||
found: value.clone(),
|
borrowed_value
|
||||||
position,
|
.as_boolean()
|
||||||
})?;
|
.ok_or_else(|| VmError::ExpectedBoolean {
|
||||||
|
found: borrowed_value.clone(),
|
||||||
|
position,
|
||||||
|
})?;
|
||||||
|
|
||||||
if boolean == test_value {
|
if boolean == test_value {
|
||||||
self.insert(value, to_register, position)?;
|
let value = self.take(argument, to_register, position)?;
|
||||||
|
|
||||||
|
self.set(to_register, value, position)?;
|
||||||
} else {
|
} else {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Equal => {
|
Operation::Equal => {
|
||||||
let (jump, _) = *self.chunk.get_instruction(self.ip, position)?;
|
debug_assert_eq!(
|
||||||
|
self.chunk.get_instruction(self.ip, position)?.0.operation(),
|
||||||
debug_assert_eq!(jump.operation(), Operation::Jump);
|
Operation::Jump
|
||||||
|
);
|
||||||
|
|
||||||
let (left, right) = get_arguments(self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
|
|
||||||
@ -254,6 +262,7 @@ impl Vm {
|
|||||||
if boolean == compare_to {
|
if boolean == compare_to {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
} else {
|
} else {
|
||||||
|
let (jump, _) = *self.chunk.get_instruction(self.ip, position)?;
|
||||||
let jump_distance = jump.a();
|
let jump_distance = jump.a();
|
||||||
let is_positive = jump.b_as_boolean();
|
let is_positive = jump.b_as_boolean();
|
||||||
let new_ip = if is_positive {
|
let new_ip = if is_positive {
|
||||||
@ -266,12 +275,12 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Less => {
|
Operation::Less => {
|
||||||
let jump = self.chunk.get_instruction(self.ip, position)?.0;
|
debug_assert_eq!(
|
||||||
|
self.chunk.get_instruction(self.ip, position)?.0.operation(),
|
||||||
debug_assert_eq!(jump.operation(), Operation::Jump);
|
Operation::Jump
|
||||||
|
);
|
||||||
|
|
||||||
let (left, right) = get_arguments(self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
|
|
||||||
let boolean = left
|
let boolean = left
|
||||||
.less_than(right)
|
.less_than(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?
|
.map_err(|error| VmError::Value { error, position })?
|
||||||
@ -285,6 +294,7 @@ impl Vm {
|
|||||||
if boolean == compare_to {
|
if boolean == compare_to {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
} else {
|
} else {
|
||||||
|
let jump = self.chunk.get_instruction(self.ip, position)?.0;
|
||||||
let jump_distance = jump.a();
|
let jump_distance = jump.a();
|
||||||
let is_positive = jump.b_as_boolean();
|
let is_positive = jump.b_as_boolean();
|
||||||
let new_ip = if is_positive {
|
let new_ip = if is_positive {
|
||||||
@ -297,9 +307,10 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::LessEqual => {
|
Operation::LessEqual => {
|
||||||
let jump = self.chunk.get_instruction(self.ip, position)?.0;
|
debug_assert_eq!(
|
||||||
|
self.chunk.get_instruction(self.ip, position)?.0.operation(),
|
||||||
debug_assert_eq!(jump.operation(), Operation::Jump);
|
Operation::Jump
|
||||||
|
);
|
||||||
|
|
||||||
let (left, right) = get_arguments(self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
let boolean = left
|
let boolean = left
|
||||||
@ -315,6 +326,7 @@ impl Vm {
|
|||||||
if boolean == compare_to {
|
if boolean == compare_to {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
} else {
|
} else {
|
||||||
|
let jump = self.chunk.get_instruction(self.ip, position)?.0;
|
||||||
let jump_distance = jump.a();
|
let jump_distance = jump.a();
|
||||||
let is_positive = jump.b_as_boolean();
|
let is_positive = jump.b_as_boolean();
|
||||||
let new_ip = if is_positive {
|
let new_ip = if is_positive {
|
||||||
@ -336,7 +348,7 @@ impl Vm {
|
|||||||
.negate()
|
.negate()
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.insert(negated, instruction.a(), position)?;
|
self.set(instruction.a(), negated, position)?;
|
||||||
}
|
}
|
||||||
Operation::Not => {
|
Operation::Not => {
|
||||||
let value = if instruction.b_is_constant() {
|
let value = if instruction.b_is_constant() {
|
||||||
@ -348,7 +360,7 @@ impl Vm {
|
|||||||
.not()
|
.not()
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.insert(not, instruction.a(), position)?;
|
self.set(instruction.a(), not, position)?;
|
||||||
}
|
}
|
||||||
Operation::Jump => {
|
Operation::Jump => {
|
||||||
let offset = instruction.b();
|
let offset = instruction.b();
|
||||||
@ -362,7 +374,11 @@ impl Vm {
|
|||||||
self.ip = new_ip;
|
self.ip = new_ip;
|
||||||
}
|
}
|
||||||
Operation::Return => {
|
Operation::Return => {
|
||||||
return if let Some(Some(value)) = self.register_stack.pop() {
|
let should_return_value = instruction.b_as_boolean();
|
||||||
|
|
||||||
|
return if should_return_value {
|
||||||
|
let value = self.empty(self.stack.len() - 1, position)?;
|
||||||
|
|
||||||
Ok(Some(value))
|
Ok(Some(value))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
@ -374,124 +390,136 @@ impl Vm {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert(&mut self, value: Value, index: u8, position: Span) -> Result<(), VmError> {
|
fn set(&mut self, index: u8, value: Value, position: Span) -> Result<(), VmError> {
|
||||||
if self.register_stack.len() == Self::STACK_LIMIT {
|
let length = self.stack.len();
|
||||||
Err(VmError::StackOverflow { position })
|
|
||||||
} else {
|
|
||||||
let index = index as usize;
|
|
||||||
|
|
||||||
while index >= self.register_stack.len() {
|
|
||||||
self.register_stack.push(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.register_stack[index] = Some(value);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn take(&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 length == Self::STACK_LIMIT {
|
||||||
let value = register
|
return Err(VmError::StackOverflow { position });
|
||||||
.take()
|
|
||||||
.ok_or_else(|| VmError::EmptyRegister { index, position })?;
|
|
||||||
|
|
||||||
Ok(value)
|
|
||||||
} else {
|
|
||||||
Err(VmError::RegisterIndexOutOfBounds { position })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if index == length {
|
||||||
|
log::trace!("Push register {index} with value {value}");
|
||||||
|
|
||||||
|
self.stack.push(Register::Value(value));
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if index < length {
|
||||||
|
log::trace!("Replace register {index} with value {value}");
|
||||||
|
|
||||||
|
self.stack[index] = Register::Value(value);
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(VmError::SkippedRegister {
|
||||||
|
index,
|
||||||
|
length: self.stack.len(),
|
||||||
|
position,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_constant(
|
||||||
|
&mut self,
|
||||||
|
index: u8,
|
||||||
|
constant_index: u8,
|
||||||
|
position: Span,
|
||||||
|
) -> Result<(), VmError> {
|
||||||
|
let length = self.stack.len();
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
if length == Self::STACK_LIMIT {
|
||||||
|
return Err(VmError::StackOverflow { position });
|
||||||
|
}
|
||||||
|
|
||||||
|
if index == length {
|
||||||
|
log::trace!("Push register {index} with constant {constant_index}");
|
||||||
|
|
||||||
|
self.stack.push(Register::Constant(constant_index));
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if index < length {
|
||||||
|
log::trace!("Replace register {index} with constant {constant_index}");
|
||||||
|
|
||||||
|
self.stack[index] = Register::Constant(constant_index);
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(VmError::SkippedRegister {
|
||||||
|
index,
|
||||||
|
length: self.stack.len(),
|
||||||
|
position,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, index: u8, position: Span) -> Result<&Value, VmError> {
|
fn get(&self, index: u8, position: Span) -> Result<&Value, VmError> {
|
||||||
let index = index as usize;
|
let index = index as usize;
|
||||||
|
let register = self
|
||||||
|
.stack
|
||||||
|
.get(index)
|
||||||
|
.ok_or_else(|| VmError::RegisterIndexOutOfBounds { position })?;
|
||||||
|
|
||||||
if let Some(register) = self.register_stack.get(index) {
|
match register {
|
||||||
let value = register
|
Register::Value(value) => Ok(value),
|
||||||
.as_ref()
|
Register::Pointer(register_index) => {
|
||||||
.ok_or_else(|| VmError::EmptyRegister { index, position })?;
|
let value = self.get(*register_index, position)?;
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
} else {
|
}
|
||||||
Err(VmError::RegisterIndexOutOfBounds { position })
|
Register::Constant(constant_index) => {
|
||||||
|
let value = self.chunk.get_constant(*constant_index, position)?;
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
Register::Empty => Err(VmError::EmptyRegister { index, position }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
fn take(&mut self, index: u8, point_to: u8, position: Span) -> Result<Value, VmError> {
|
||||||
let index = index as usize;
|
let index = index as usize;
|
||||||
|
let register = replace(&mut self.stack[index], Register::Pointer(point_to));
|
||||||
|
|
||||||
if let Some(register) = self.register_stack.get_mut(index) {
|
match register {
|
||||||
let cloneable = if let Some(value) = register.take() {
|
Register::Value(value) => Ok(value),
|
||||||
if value.is_raw() {
|
Register::Pointer(register_index) => {
|
||||||
value.into_reference()
|
let value = self.take(register_index, point_to, position)?;
|
||||||
} else {
|
|
||||||
value
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(VmError::EmptyRegister { index, position });
|
|
||||||
};
|
|
||||||
|
|
||||||
*register = Some(cloneable.clone());
|
Ok(value)
|
||||||
|
}
|
||||||
|
Register::Constant(constant_index) => {
|
||||||
|
let value = self.chunk.take_constant(constant_index, position)?;
|
||||||
|
|
||||||
Ok(cloneable)
|
Ok(value)
|
||||||
} else {
|
}
|
||||||
Err(VmError::RegisterIndexOutOfBounds { position })
|
Register::Empty => Err(VmError::EmptyRegister { index, position }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _clone_mutable(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
fn empty(&mut self, index: usize, position: Span) -> Result<Value, VmError> {
|
||||||
let index = index as usize;
|
if index >= self.stack.len() {
|
||||||
|
return Err(VmError::RegisterIndexOutOfBounds { position });
|
||||||
if let Some(register) = self.register_stack.get_mut(index) {
|
|
||||||
let cloneable = if let Some(value) = register.take() {
|
|
||||||
value.into_mutable()
|
|
||||||
} else {
|
|
||||||
return Err(VmError::EmptyRegister { index, position });
|
|
||||||
};
|
|
||||||
|
|
||||||
*register = Some(cloneable.clone());
|
|
||||||
|
|
||||||
Ok(cloneable)
|
|
||||||
} else {
|
|
||||||
Err(VmError::RegisterIndexOutOfBounds { position })
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn _clone_as_variable(&mut self, local: Local, position: Span) -> Result<Value, VmError> {
|
let register = replace(&mut self.stack[index], Register::Empty);
|
||||||
let index = if let Some(index) = local.register_index {
|
|
||||||
index
|
|
||||||
} else {
|
|
||||||
return Err(VmError::UndefinedVariable {
|
|
||||||
identifier: local.identifier,
|
|
||||||
position,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
let clone_result = if local.is_mutable {
|
|
||||||
self._clone_mutable(index, position)
|
|
||||||
} else {
|
|
||||||
self.clone(index, position)
|
|
||||||
};
|
|
||||||
|
|
||||||
match clone_result {
|
match register {
|
||||||
Err(VmError::EmptyRegister { .. }) => Err(VmError::UndefinedVariable {
|
Register::Value(value) => Ok(value),
|
||||||
identifier: local.identifier,
|
Register::Pointer(register_index) => {
|
||||||
position,
|
let value = self.empty(register_index as usize, position)?;
|
||||||
}),
|
|
||||||
_ => clone_result,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _pop(&mut self, position: Span) -> Result<Value, VmError> {
|
Ok(value)
|
||||||
if let Some(register) = self.register_stack.pop() {
|
}
|
||||||
let value = register.ok_or(VmError::EmptyRegister {
|
Register::Constant(constant_index) => {
|
||||||
index: self.register_stack.len().saturating_sub(1),
|
let value = self.chunk.take_constant(constant_index, position)?;
|
||||||
position,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
} else {
|
}
|
||||||
Err(VmError::StackUnderflow { position })
|
Register::Empty => Err(VmError::EmptyRegister { index, position }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,6 +532,14 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
enum Register {
|
||||||
|
Empty,
|
||||||
|
Value(Value),
|
||||||
|
Pointer(u8),
|
||||||
|
Constant(u8),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum VmError {
|
pub enum VmError {
|
||||||
CannotMutateImmutableLocal {
|
CannotMutateImmutableLocal {
|
||||||
@ -525,6 +561,11 @@ pub enum VmError {
|
|||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
SkippedRegister {
|
||||||
|
index: usize,
|
||||||
|
length: usize,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
StackOverflow {
|
StackOverflow {
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
@ -562,11 +603,12 @@ impl AnnotatedError for VmError {
|
|||||||
Self::ExpectedBoolean { .. } => "Expected boolean",
|
Self::ExpectedBoolean { .. } => "Expected boolean",
|
||||||
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
||||||
Self::InvalidInstruction { .. } => "Invalid instruction",
|
Self::InvalidInstruction { .. } => "Invalid instruction",
|
||||||
|
Self::SkippedRegister { .. } => "Skipped register",
|
||||||
Self::StackOverflow { .. } => "Stack overflow",
|
Self::StackOverflow { .. } => "Stack overflow",
|
||||||
Self::StackUnderflow { .. } => "Stack underflow",
|
Self::StackUnderflow { .. } => "Stack underflow",
|
||||||
Self::UndefinedVariable { .. } => "Undefined variable",
|
Self::UndefinedVariable { .. } => "Undefined variable",
|
||||||
Self::Value { .. } => "Value error",
|
|
||||||
Self::Chunk(error) => error.description(),
|
Self::Chunk(error) => error.description(),
|
||||||
|
Self::Value { .. } => "Value error",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -589,6 +631,7 @@ impl AnnotatedError for VmError {
|
|||||||
Self::ExpectedBoolean { position, .. } => *position,
|
Self::ExpectedBoolean { position, .. } => *position,
|
||||||
Self::RegisterIndexOutOfBounds { position } => *position,
|
Self::RegisterIndexOutOfBounds { position } => *position,
|
||||||
Self::InvalidInstruction { position, .. } => *position,
|
Self::InvalidInstruction { position, .. } => *position,
|
||||||
|
Self::SkippedRegister { position, .. } => *position,
|
||||||
Self::StackUnderflow { position } => *position,
|
Self::StackUnderflow { position } => *position,
|
||||||
Self::StackOverflow { position } => *position,
|
Self::StackOverflow { position } => *position,
|
||||||
Self::UndefinedVariable { position, .. } => *position,
|
Self::UndefinedVariable { position, .. } => *position,
|
||||||
|
@ -14,7 +14,7 @@ fn add() {
|
|||||||
.set_c_is_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(), Span(5, 5))
|
(Instruction::r#return(true), Span(5, 5))
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -36,7 +36,7 @@ fn add_assign() {
|
|||||||
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
(*Instruction::add(0, 0, 1).set_c_is_constant(), Span(17, 19)),
|
(*Instruction::add(0, 0, 1).set_c_is_constant(), Span(17, 19)),
|
||||||
(Instruction::get_local(1, 0), Span(23, 24)),
|
(Instruction::get_local(1, 0), Span(23, 24)),
|
||||||
(Instruction::r#return(), Span(24, 24))
|
(Instruction::r#return(true), Span(24, 24))
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))]
|
vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))]
|
||||||
@ -58,7 +58,7 @@ 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::r#return(), Span(13, 13)),
|
(Instruction::r#return(true), Span(13, 13)),
|
||||||
],
|
],
|
||||||
vec![],
|
vec![],
|
||||||
vec![]
|
vec![]
|
||||||
@ -126,7 +126,7 @@ fn constant() {
|
|||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(0, 2)),
|
(Instruction::load_constant(0, 0, false), Span(0, 2)),
|
||||||
(Instruction::r#return(), Span(2, 2))
|
(Instruction::r#return(true), Span(2, 2))
|
||||||
],
|
],
|
||||||
vec![Value::integer(42)],
|
vec![Value::integer(42)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -169,7 +169,7 @@ fn divide() {
|
|||||||
.set_c_is_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(), Span(5, 5))
|
(Instruction::r#return(true), Span(5, 5))
|
||||||
],
|
],
|
||||||
vec![Value::integer(2), Value::integer(2)],
|
vec![Value::integer(2), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -194,7 +194,7 @@ fn divide_assign() {
|
|||||||
Span(17, 19)
|
Span(17, 19)
|
||||||
),
|
),
|
||||||
(Instruction::get_local(1, 0), Span(23, 24)),
|
(Instruction::get_local(1, 0), Span(23, 24)),
|
||||||
(Instruction::r#return(), Span(24, 24))
|
(Instruction::r#return(true), Span(24, 24))
|
||||||
],
|
],
|
||||||
vec![Value::integer(2), Value::integer(2)],
|
vec![Value::integer(2), Value::integer(2)],
|
||||||
vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))]
|
vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))]
|
||||||
@ -229,7 +229,7 @@ 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::r#return(), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -258,7 +258,7 @@ fn equality_assignment_long() {
|
|||||||
(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)),
|
||||||
(Instruction::get_local(1, 0), Span(43, 44)),
|
(Instruction::get_local(1, 0), Span(43, 44)),
|
||||||
(Instruction::r#return(), Span(44, 44)),
|
(Instruction::r#return(true), Span(44, 44)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(4), Value::integer(4)],
|
vec![Value::integer(4), Value::integer(4)],
|
||||||
vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))]
|
vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))]
|
||||||
@ -287,7 +287,7 @@ fn equality_assignment_short() {
|
|||||||
(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::get_local(1, 0), Span(15, 16)),
|
(Instruction::get_local(1, 0), Span(15, 16)),
|
||||||
(Instruction::r#return(), Span(16, 16)),
|
(Instruction::r#return(true), Span(16, 16)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(4), Value::integer(4)],
|
vec![Value::integer(4), Value::integer(4)],
|
||||||
vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))]
|
vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))]
|
||||||
@ -314,7 +314,7 @@ 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::r#return(), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -341,7 +341,7 @@ 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::r#return(), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -368,7 +368,7 @@ fn if_else_expression() {
|
|||||||
(Instruction::jump(1, true), Span(5, 7)),
|
(Instruction::jump(1, true), Span(5, 7)),
|
||||||
(Instruction::load_constant(0, 2, true), Span(12, 13)),
|
(Instruction::load_constant(0, 2, true), Span(12, 13)),
|
||||||
(Instruction::load_constant(0, 3, false), Span(23, 24)),
|
(Instruction::load_constant(0, 3, false), Span(23, 24)),
|
||||||
(Instruction::r#return(), Span(26, 26)),
|
(Instruction::r#return(true), Span(26, 26)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Value::integer(1),
|
Value::integer(1),
|
||||||
@ -399,7 +399,6 @@ fn if_expression_false() {
|
|||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(5, 7)),
|
(Instruction::jump(1, true), Span(5, 7)),
|
||||||
(Instruction::load_constant(0, 2, false), Span(12, 13)),
|
(Instruction::load_constant(0, 2, false), Span(12, 13)),
|
||||||
(Instruction::r#return(), Span(15, 15)),
|
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -425,14 +424,13 @@ fn if_expression_true() {
|
|||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(5, 7)),
|
(Instruction::jump(1, true), Span(5, 7)),
|
||||||
(Instruction::load_constant(0, 2, false), Span(12, 13)),
|
(Instruction::load_constant(0, 2, false), Span(12, 13)),
|
||||||
(Instruction::r#return(), 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![]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(Value::integer(2))));
|
assert_eq!(run(source), Ok(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -452,7 +450,7 @@ 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::r#return(), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -479,7 +477,7 @@ 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::r#return(), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -501,7 +499,7 @@ fn list() {
|
|||||||
(Instruction::load_constant(1, 1, false), Span(4, 5)),
|
(Instruction::load_constant(1, 1, false), Span(4, 5)),
|
||||||
(Instruction::load_constant(2, 2, false), 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::r#return(), Span(9, 9)),
|
(Instruction::r#return(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![]
|
||||||
@ -542,7 +540,7 @@ 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::r#return(), Span(18, 18)),
|
(Instruction::r#return(true), Span(18, 18)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Value::integer(1),
|
Value::integer(1),
|
||||||
@ -581,7 +579,7 @@ fn list_with_simple_expression() {
|
|||||||
),
|
),
|
||||||
(Instruction::load_constant(2, 3, false), 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::r#return(), Span(13, 13)),
|
(Instruction::r#return(true), Span(13, 13)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Value::integer(1),
|
Value::integer(1),
|
||||||
@ -628,7 +626,7 @@ 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::r#return(), Span(17, 17)),
|
(Instruction::r#return(true), Span(17, 17)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Value::integer(1),
|
Value::integer(1),
|
||||||
@ -658,7 +656,7 @@ fn multiply() {
|
|||||||
.set_c_is_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -683,7 +681,7 @@ fn multiply_assign() {
|
|||||||
Span(17, 19)
|
Span(17, 19)
|
||||||
),
|
),
|
||||||
(Instruction::get_local(1, 0), Span(22, 23)),
|
(Instruction::get_local(1, 0), Span(22, 23)),
|
||||||
(Instruction::r#return(), Span(23, 23))
|
(Instruction::r#return(true), Span(23, 23))
|
||||||
],
|
],
|
||||||
vec![Value::integer(2), Value::integer(3)],
|
vec![Value::integer(2), Value::integer(3)],
|
||||||
vec![Local::new(Identifier::new("a"), None, true, 0, Some(0)),]
|
vec![Local::new(Identifier::new("a"), None, true, 0, Some(0)),]
|
||||||
@ -702,7 +700,7 @@ fn negate() {
|
|||||||
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::r#return(), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(42)],
|
vec![Value::integer(42)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -722,7 +720,7 @@ 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::r#return(), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
vec![],
|
vec![],
|
||||||
vec![]
|
vec![]
|
||||||
@ -749,7 +747,7 @@ 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::r#return(), Span(6, 6)),
|
(Instruction::r#return(true), Span(6, 6)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -771,7 +769,7 @@ 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::r#return(), Span(13, 13)),
|
(Instruction::r#return(true), Span(13, 13)),
|
||||||
],
|
],
|
||||||
vec![],
|
vec![],
|
||||||
vec![]
|
vec![]
|
||||||
@ -799,7 +797,7 @@ 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::r#return(), Span(11, 11)),
|
(Instruction::r#return(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![]
|
||||||
@ -822,7 +820,7 @@ fn set_local() {
|
|||||||
(Instruction::load_constant(1, 1, false), 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::get_local(1, 0), Span(24, 25)),
|
(Instruction::get_local(1, 0), Span(24, 25)),
|
||||||
(Instruction::r#return(), Span(25, 25)),
|
(Instruction::r#return(true), Span(25, 25)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(41), Value::integer(42)],
|
vec![Value::integer(41), Value::integer(42)],
|
||||||
vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]
|
vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]
|
||||||
@ -846,7 +844,7 @@ fn subtract() {
|
|||||||
.set_c_is_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -871,7 +869,7 @@ fn subtract_assign() {
|
|||||||
Span(18, 20)
|
Span(18, 20)
|
||||||
),
|
),
|
||||||
(Instruction::get_local(1, 0), Span(24, 25)),
|
(Instruction::get_local(1, 0), Span(24, 25)),
|
||||||
(Instruction::r#return(), Span(25, 25)),
|
(Instruction::r#return(true), Span(25, 25)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(42), Value::integer(2)],
|
vec![Value::integer(42), Value::integer(2)],
|
||||||
vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]
|
vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]
|
||||||
@ -897,7 +895,7 @@ 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::r#return(), Span(35, 35)),
|
(Instruction::r#return(true), Span(35, 35)),
|
||||||
],
|
],
|
||||||
vec![],
|
vec![],
|
||||||
vec![
|
vec![
|
||||||
@ -928,7 +926,7 @@ fn r#while() {
|
|||||||
(*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(3, false), Span(41, 42)),
|
||||||
(Instruction::get_local(1, 0), Span(41, 42)),
|
(Instruction::get_local(1, 0), Span(41, 42)),
|
||||||
(Instruction::r#return(), Span(42, 42)),
|
(Instruction::r#return(true), Span(42, 42)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(0), Value::integer(5), Value::integer(1),],
|
vec![Value::integer(0), Value::integer(5), Value::integer(1),],
|
||||||
vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]
|
vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]
|
||||||
|
Loading…
Reference in New Issue
Block a user