Refactor and clean up; Pass tests
This commit is contained in:
parent
413cb70731
commit
5b3232c723
@ -271,8 +271,8 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
"",
|
"",
|
||||||
"Instructions",
|
"Instructions",
|
||||||
"------------",
|
"------------",
|
||||||
"INDEX OPERATION INFO POSITION",
|
"INDEX OPERATION INFO POSITION",
|
||||||
"----- -------------- ------------------------- --------",
|
"----- --------------- ------------------------------ --------",
|
||||||
];
|
];
|
||||||
|
|
||||||
const CONSTANT_HEADER: [&'static str; 5] = [
|
const CONSTANT_HEADER: [&'static str; 5] = [
|
||||||
@ -293,14 +293,10 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
|
|
||||||
/// The default width of the disassembly output. To correctly align the output, this should
|
/// The default width of the disassembly output. To correctly align the output, this should
|
||||||
/// return the width of the longest line that the disassembler is guaranteed to produce.
|
/// return the width of the longest line that the disassembler is guaranteed to produce.
|
||||||
pub fn default_width(styled: bool) -> usize {
|
pub fn default_width() -> usize {
|
||||||
let longest_line = Self::INSTRUCTION_HEADER[4];
|
let longest_line = Self::INSTRUCTION_HEADER[4];
|
||||||
|
|
||||||
if styled {
|
longest_line.chars().count()
|
||||||
longest_line.bold().chars().count()
|
|
||||||
} else {
|
|
||||||
longest_line.chars().count()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(name: &'a str, chunk: &'a Chunk) -> Self {
|
pub fn new(name: &'a str, chunk: &'a Chunk) -> Self {
|
||||||
@ -325,10 +321,7 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn disassemble(&self) -> String {
|
pub fn disassemble(&self) -> String {
|
||||||
let width = self
|
let width = self.width.unwrap_or_else(Self::default_width);
|
||||||
.width
|
|
||||||
.unwrap_or_else(|| Self::default_width(self.styled))
|
|
||||||
+ 1;
|
|
||||||
let center = |line: &str| format!("{line:^width$}\n");
|
let center = |line: &str| format!("{line:^width$}\n");
|
||||||
let style = |line: String| {
|
let style = |line: String| {
|
||||||
if self.styled {
|
if self.styled {
|
||||||
@ -369,9 +362,9 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
let info_option = instruction.disassembly_info(Some(self.chunk));
|
let info_option = instruction.disassembly_info(Some(self.chunk));
|
||||||
|
|
||||||
let instruction_display = if let Some(info) = info_option {
|
let instruction_display = if let Some(info) = info_option {
|
||||||
format!("{index:<5} {operation:14} {info:25} {position:8}")
|
format!("{index:<5} {operation:15} {info:30} {position:8}")
|
||||||
} else {
|
} else {
|
||||||
format!("{index:<5} {operation:14} {:25} {position:8}", " ")
|
format!("{index:<5} {operation:15} {:30} {position:8}", " ")
|
||||||
};
|
};
|
||||||
|
|
||||||
disassembly.push_str(¢er(&instruction_display));
|
disassembly.push_str(¢er(&instruction_display));
|
||||||
@ -462,10 +455,7 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
let dynamic_line_count =
|
let dynamic_line_count =
|
||||||
self.chunk.instructions.len() + self.chunk.constants.len() + self.chunk.locals.len();
|
self.chunk.instructions.len() + self.chunk.constants.len() + self.chunk.locals.len();
|
||||||
let total_line_count = static_line_count + dynamic_line_count;
|
let total_line_count = static_line_count + dynamic_line_count;
|
||||||
let width = self
|
let width = self.width.unwrap_or_else(Self::default_width) + 1;
|
||||||
.width
|
|
||||||
.unwrap_or_else(|| Self::default_width(self.styled))
|
|
||||||
+ 1;
|
|
||||||
|
|
||||||
total_line_count * width
|
total_line_count * width
|
||||||
}
|
}
|
||||||
|
@ -207,6 +207,12 @@ impl Instruction {
|
|||||||
(self.0 >> 24) != 0
|
(self.0 >> 24) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_destination_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
||||||
|
self.set_destination(if boolean { 1 } else { 0 });
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_destination(&mut self, destination: u8) {
|
pub fn set_destination(&mut self, destination: u8) {
|
||||||
self.0 &= 0x00FFFFFF;
|
self.0 &= 0x00FFFFFF;
|
||||||
self.0 |= (destination as u32) << 24;
|
self.0 |= (destination as u32) << 24;
|
||||||
@ -224,6 +230,12 @@ impl Instruction {
|
|||||||
self.first_argument() != 0
|
self.first_argument() != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_first_argument_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
||||||
|
self.set_first_argument(if boolean { 1 } else { 0 });
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_first_argument_to_constant(&mut self) -> &mut Self {
|
pub fn set_first_argument_to_constant(&mut self) -> &mut Self {
|
||||||
self.0 |= 0b1000_0000;
|
self.0 |= 0b1000_0000;
|
||||||
|
|
||||||
@ -246,6 +258,12 @@ impl Instruction {
|
|||||||
self.second_argument() != 0
|
self.second_argument() != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_second_argument_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
||||||
|
self.set_second_argument(if boolean { 1 } else { 0 });
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_second_argument_to_constant(&mut self) -> &mut Self {
|
pub fn set_second_argument_to_constant(&mut self) -> &mut Self {
|
||||||
self.0 |= 0b0100_0000;
|
self.0 |= 0b0100_0000;
|
||||||
|
|
||||||
@ -269,14 +287,14 @@ impl Instruction {
|
|||||||
pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> Option<String> {
|
pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> Option<String> {
|
||||||
let format_arguments = || {
|
let format_arguments = || {
|
||||||
let first_argument = if self.first_argument_is_constant() {
|
let first_argument = if self.first_argument_is_constant() {
|
||||||
format!("C({})", self.first_argument())
|
format!("C{}", self.first_argument())
|
||||||
} else {
|
} else {
|
||||||
format!("R({})", self.first_argument())
|
format!("R{}", self.first_argument())
|
||||||
};
|
};
|
||||||
let second_argument = if self.second_argument_is_constant() {
|
let second_argument = if self.second_argument_is_constant() {
|
||||||
format!("C({})", self.second_argument())
|
format!("C{}", self.second_argument())
|
||||||
} else {
|
} else {
|
||||||
format!("R({})", self.second_argument())
|
format!("R{}", self.second_argument())
|
||||||
};
|
};
|
||||||
|
|
||||||
(first_argument, second_argument)
|
(first_argument, second_argument)
|
||||||
@ -284,13 +302,13 @@ impl Instruction {
|
|||||||
|
|
||||||
let info = match self.operation() {
|
let info = match self.operation() {
|
||||||
Operation::Move => {
|
Operation::Move => {
|
||||||
format!("R({}) = R({})", self.destination(), self.first_argument())
|
format!("R{} = R{}", self.destination(), self.first_argument())
|
||||||
}
|
}
|
||||||
Operation::Close => {
|
Operation::Close => {
|
||||||
let from_register = self.first_argument();
|
let from_register = self.first_argument();
|
||||||
let to_register = self.second_argument().saturating_sub(1);
|
let to_register = self.second_argument().saturating_sub(1);
|
||||||
|
|
||||||
format!("R({from_register})..=R({to_register})")
|
format!("R{from_register}..=R{to_register}")
|
||||||
}
|
}
|
||||||
Operation::LoadBoolean => {
|
Operation::LoadBoolean => {
|
||||||
let to_register = self.destination();
|
let to_register = self.destination();
|
||||||
@ -301,7 +319,7 @@ impl Instruction {
|
|||||||
""
|
""
|
||||||
};
|
};
|
||||||
|
|
||||||
format!("R({to_register}) = {boolean} {skip_display}",)
|
format!("R{to_register} = {boolean} {skip_display}",)
|
||||||
}
|
}
|
||||||
Operation::LoadConstant => {
|
Operation::LoadConstant => {
|
||||||
let constant_index = self.first_argument();
|
let constant_index = self.first_argument();
|
||||||
@ -309,22 +327,14 @@ impl Instruction {
|
|||||||
if let Some(chunk) = chunk {
|
if let Some(chunk) = chunk {
|
||||||
match chunk.get_constant(constant_index, Span(0, 0)) {
|
match chunk.get_constant(constant_index, Span(0, 0)) {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
format!(
|
format!("R{} = C{} {}", self.destination(), constant_index, value)
|
||||||
"R({}) = C({}) {}",
|
}
|
||||||
self.destination(),
|
Err(error) => {
|
||||||
constant_index,
|
format!("R{} = C{} {:?}", self.destination(), constant_index, error)
|
||||||
value
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Err(error) => format!(
|
|
||||||
"R({}) = C({}) {:?}",
|
|
||||||
self.destination(),
|
|
||||||
constant_index,
|
|
||||||
error
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
format!("R({}) = C({})", self.destination(), constant_index)
|
format!("R{} = C{}", self.destination(), constant_index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::LoadList => {
|
Operation::LoadList => {
|
||||||
@ -332,12 +342,10 @@ impl Instruction {
|
|||||||
let first_index = destination - self.first_argument();
|
let first_index = destination - self.first_argument();
|
||||||
let last_index = destination - 1;
|
let last_index = destination - 1;
|
||||||
|
|
||||||
format!(
|
format!("R{} = [R{}..=R{}]", destination, first_index, last_index)
|
||||||
"R({}) = [R({})..=R({})]",
|
|
||||||
destination, first_index, last_index
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Operation::DefineLocal => {
|
Operation::DefineLocal => {
|
||||||
|
let destination = self.destination();
|
||||||
let local_index = self.first_argument();
|
let local_index = self.first_argument();
|
||||||
let identifier_display = if let Some(chunk) = chunk {
|
let identifier_display = if let Some(chunk) = chunk {
|
||||||
match chunk.get_identifier(local_index) {
|
match chunk.get_identifier(local_index) {
|
||||||
@ -347,18 +355,18 @@ impl Instruction {
|
|||||||
} else {
|
} else {
|
||||||
"???".to_string()
|
"???".to_string()
|
||||||
};
|
};
|
||||||
|
let mutable_display = if self.second_argument_as_boolean() {
|
||||||
|
"mut "
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
format!(
|
format!("L{local_index} = R{destination} {mutable_display}{identifier_display}")
|
||||||
"L({}) = R({}) {}",
|
|
||||||
local_index,
|
|
||||||
self.destination(),
|
|
||||||
identifier_display
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Operation::GetLocal => {
|
Operation::GetLocal => {
|
||||||
let local_index = self.first_argument();
|
let local_index = self.first_argument();
|
||||||
|
|
||||||
format!("R({}) = L({})", self.destination(), local_index)
|
format!("R{} = L{}", self.destination(), local_index)
|
||||||
}
|
}
|
||||||
Operation::SetLocal => {
|
Operation::SetLocal => {
|
||||||
let local_index = self.first_argument();
|
let local_index = self.first_argument();
|
||||||
@ -372,7 +380,7 @@ impl Instruction {
|
|||||||
};
|
};
|
||||||
|
|
||||||
format!(
|
format!(
|
||||||
"L({}) = R({}) {}",
|
"L{} = R{} {}",
|
||||||
local_index,
|
local_index,
|
||||||
self.destination(),
|
self.destination(),
|
||||||
identifier_display
|
identifier_display
|
||||||
@ -429,7 +437,7 @@ impl Instruction {
|
|||||||
|
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
format!("if {first_argument} {comparison_symbol} {second_argument} IP++",)
|
format!("if {first_argument} {comparison_symbol} {second_argument} {{ IP += 1 }}",)
|
||||||
}
|
}
|
||||||
Operation::Less => {
|
Operation::Less => {
|
||||||
let comparison_symbol = if self.destination_as_boolean() {
|
let comparison_symbol = if self.destination_as_boolean() {
|
||||||
@ -454,22 +462,22 @@ impl Instruction {
|
|||||||
Operation::Negate => {
|
Operation::Negate => {
|
||||||
let destination = self.destination();
|
let destination = self.destination();
|
||||||
let argument = if self.first_argument_is_constant() {
|
let argument = if self.first_argument_is_constant() {
|
||||||
format!("C({})", self.first_argument())
|
format!("C{}", self.first_argument())
|
||||||
} else {
|
} else {
|
||||||
format!("R({})", self.first_argument())
|
format!("R{}", self.first_argument())
|
||||||
};
|
};
|
||||||
|
|
||||||
format!("R({destination}) = -{argument}")
|
format!("R{destination} = -{argument}")
|
||||||
}
|
}
|
||||||
Operation::Not => {
|
Operation::Not => {
|
||||||
let destination = self.destination();
|
let destination = self.destination();
|
||||||
let argument = if self.first_argument_is_constant() {
|
let argument = if self.first_argument_is_constant() {
|
||||||
format!("C({})", self.first_argument())
|
format!("C{}", self.first_argument())
|
||||||
} else {
|
} else {
|
||||||
format!("R({})", self.first_argument())
|
format!("R{}", self.first_argument())
|
||||||
};
|
};
|
||||||
|
|
||||||
format!("R({destination}) = !{argument}")
|
format!("R{destination} = !{argument}")
|
||||||
}
|
}
|
||||||
Operation::Jump => {
|
Operation::Jump => {
|
||||||
let offset = self.first_argument();
|
let offset = self.first_argument();
|
||||||
@ -483,8 +491,15 @@ impl Instruction {
|
|||||||
}
|
}
|
||||||
Operation::Return => return None,
|
Operation::Return => return None,
|
||||||
};
|
};
|
||||||
|
let trucated_length = 30;
|
||||||
|
let with_elipsis = trucated_length - 3;
|
||||||
|
let truncated_info = if info.len() > with_elipsis {
|
||||||
|
format!("{info:.<trucated_length$.with_elipsis$}")
|
||||||
|
} else {
|
||||||
|
info
|
||||||
|
};
|
||||||
|
|
||||||
Some(info)
|
Some(truncated_info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ mod vm;
|
|||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
pub use chunk::{Chunk, ChunkError, Local};
|
pub use chunk::{Chunk, ChunkDisassembler, ChunkError, Local};
|
||||||
pub use constructor::Constructor;
|
pub use constructor::Constructor;
|
||||||
pub use dust_error::{AnnotatedError, DustError};
|
pub use dust_error::{AnnotatedError, DustError};
|
||||||
pub use identifier::Identifier;
|
pub use identifier::Identifier;
|
||||||
|
@ -149,11 +149,27 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
let boolean = text.parse::<bool>().unwrap();
|
let boolean = text.parse::<bool>().unwrap();
|
||||||
|
|
||||||
self.emit_instruction(
|
if let Ok((last_instruction, _)) =
|
||||||
Instruction::load_boolean(self.current_register, boolean, false),
|
self.chunk.get_last_instruction(self.current_position)
|
||||||
self.previous_position,
|
{
|
||||||
);
|
let skip = last_instruction.operation() == Operation::Jump;
|
||||||
self.increment_register()?;
|
let destination = if let Operation::LoadBoolean = last_instruction.operation() {
|
||||||
|
last_instruction.destination()
|
||||||
|
} else {
|
||||||
|
self.current_register
|
||||||
|
};
|
||||||
|
|
||||||
|
self.emit_instruction(
|
||||||
|
Instruction::load_boolean(destination, boolean, skip),
|
||||||
|
self.previous_position,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.emit_instruction(
|
||||||
|
Instruction::load_boolean(self.current_register, boolean, false),
|
||||||
|
self.previous_position,
|
||||||
|
);
|
||||||
|
self.increment_register()?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -419,10 +435,19 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.emit_instruction(instruction, operator_position);
|
self.emit_instruction(instruction, operator_position);
|
||||||
self.increment_register()?;
|
|
||||||
|
|
||||||
if let TokenKind::DoubleEqual = operator.kind() {
|
if operator == Token::DoubleEqual {
|
||||||
self.emit_instruction(Instruction::jump(2, true), operator_position);
|
let distance = if push_back_left && push_back_right {
|
||||||
|
3
|
||||||
|
} else if push_back_left || push_back_right {
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
|
||||||
|
self.emit_instruction(Instruction::jump(distance, true), operator_position);
|
||||||
|
} else {
|
||||||
|
self.increment_register()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -436,10 +461,20 @@ impl<'src> Parser<'src> {
|
|||||||
let token = self.current_token.to_owned();
|
let token = self.current_token.to_owned();
|
||||||
let start_position = self.current_position;
|
let start_position = self.current_position;
|
||||||
let local_index = self.parse_identifier_from(token, start_position)?;
|
let local_index = self.parse_identifier_from(token, start_position)?;
|
||||||
|
let is_mutable = self.chunk.get_local(local_index, start_position)?.mutable;
|
||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
if allow_assignment && self.allow(TokenKind::Equal)? {
|
if allow_assignment && self.allow(TokenKind::Equal)? {
|
||||||
|
if !is_mutable {
|
||||||
|
let identifier = self.chunk.get_identifier(local_index).cloned().unwrap();
|
||||||
|
|
||||||
|
return Err(ParseError::CannotMutateImmutableVariable {
|
||||||
|
identifier,
|
||||||
|
position: start_position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
let (mut previous_instruction, previous_position) =
|
let (mut previous_instruction, previous_position) =
|
||||||
@ -510,37 +545,12 @@ impl<'src> Parser<'src> {
|
|||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.chunk.begin_scope();
|
self.chunk.begin_scope();
|
||||||
|
|
||||||
let start = self.current_position.0;
|
|
||||||
let start_register = self.current_register;
|
|
||||||
let mut ends_with_semicolon = false;
|
|
||||||
|
|
||||||
while !self.allow(TokenKind::RightCurlyBrace)? && !self.is_eof() {
|
while !self.allow(TokenKind::RightCurlyBrace)? && !self.is_eof() {
|
||||||
self.parse_statement()?;
|
self.parse_statement()?;
|
||||||
|
|
||||||
if self.previous_token == Token::Semicolon {
|
|
||||||
ends_with_semicolon = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.chunk.end_scope();
|
self.chunk.end_scope();
|
||||||
|
|
||||||
if self.current_token == Token::Semicolon {
|
|
||||||
ends_with_semicolon = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let end = self.current_position.1;
|
|
||||||
|
|
||||||
if ends_with_semicolon {
|
|
||||||
let end_register = self.current_register;
|
|
||||||
|
|
||||||
self.emit_instruction(
|
|
||||||
Instruction::close(start_register, end_register),
|
|
||||||
Span(start, end),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
self.emit_instruction(Instruction::r#return(), Span(start, end));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -582,13 +592,13 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_if(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
fn parse_if(&mut self, allow_assignment: bool) -> Result<(), ParseError> {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
self.parse_block(false)?;
|
self.parse_block(allow_assignment)?;
|
||||||
|
|
||||||
if self.allow(TokenKind::Else)? {
|
if self.allow(TokenKind::Else)? {
|
||||||
self.parse_block(false)?;
|
self.parse_block(allow_assignment)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -939,6 +949,10 @@ impl From<&TokenKind> for ParseRule<'_> {
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
|
CannotMutateImmutableVariable {
|
||||||
|
identifier: Identifier,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
ExpectedExpression {
|
ExpectedExpression {
|
||||||
found: TokenOwned,
|
found: TokenOwned,
|
||||||
position: Span,
|
position: Span,
|
||||||
@ -994,6 +1008,7 @@ impl AnnotatedError for ParseError {
|
|||||||
|
|
||||||
fn description(&self) -> &'static str {
|
fn description(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
|
Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable",
|
||||||
Self::ExpectedExpression { .. } => "Expected an expression",
|
Self::ExpectedExpression { .. } => "Expected an expression",
|
||||||
Self::ExpectedToken { .. } => "Expected a specific token",
|
Self::ExpectedToken { .. } => "Expected a specific token",
|
||||||
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
|
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
|
||||||
@ -1010,6 +1025,9 @@ impl AnnotatedError for ParseError {
|
|||||||
|
|
||||||
fn details(&self) -> Option<String> {
|
fn details(&self) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
|
Self::CannotMutateImmutableVariable { identifier, .. } => {
|
||||||
|
Some(format!("Cannot mutate immutable variable \"{identifier}\""))
|
||||||
|
}
|
||||||
Self::ExpectedExpression { found, .. } => Some(format!("Found \"{found}\"")),
|
Self::ExpectedExpression { found, .. } => Some(format!("Found \"{found}\"")),
|
||||||
Self::ExpectedToken {
|
Self::ExpectedToken {
|
||||||
expected, found, ..
|
expected, found, ..
|
||||||
@ -1034,6 +1052,7 @@ impl AnnotatedError for ParseError {
|
|||||||
|
|
||||||
fn position(&self) -> Span {
|
fn position(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
|
Self::CannotMutateImmutableVariable { position, .. } => *position,
|
||||||
Self::ExpectedExpression { position, .. } => *position,
|
Self::ExpectedExpression { position, .. } => *position,
|
||||||
Self::ExpectedToken { position, .. } => *position,
|
Self::ExpectedToken { position, .. } => *position,
|
||||||
Self::ExpectedTokenMultiple { position, .. } => *position,
|
Self::ExpectedTokenMultiple { position, .. } => *position,
|
||||||
|
@ -76,10 +76,8 @@ fn block_scope() {
|
|||||||
(Instruction::define_local(1, 1, false), Span(46, 47)),
|
(Instruction::define_local(1, 1, false), Span(46, 47)),
|
||||||
(Instruction::load_constant(2, 2), Span(92, 93)),
|
(Instruction::load_constant(2, 2), Span(92, 93)),
|
||||||
(Instruction::define_local(2, 2, false), Span(88, 89)),
|
(Instruction::define_local(2, 2, false), Span(88, 89)),
|
||||||
(Instruction::close(2, 3), Span(84, 124)),
|
|
||||||
(Instruction::load_constant(3, 3), Span(129, 130)),
|
(Instruction::load_constant(3, 3), Span(129, 130)),
|
||||||
(Instruction::define_local(3, 3, false), Span(125, 126)),
|
(Instruction::define_local(3, 3, false), Span(125, 126)),
|
||||||
(Instruction::close(1, 4), Span(42, 153)),
|
|
||||||
(Instruction::load_constant(4, 4), Span(158, 159)),
|
(Instruction::load_constant(4, 4), Span(158, 159)),
|
||||||
(Instruction::define_local(4, 4, false), Span(154, 155)),
|
(Instruction::define_local(4, 4, false), Span(154, 155)),
|
||||||
],
|
],
|
||||||
@ -109,16 +107,16 @@ fn empty() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn set_local() {
|
fn set_local() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("let x = 41; x = 42;"),
|
parse("let mut x = 41; x = 42;"),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0), Span(8, 10)),
|
(Instruction::load_constant(0, 0), Span(12, 14)),
|
||||||
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
(Instruction::load_constant(1, 1), Span(16, 18)),
|
(Instruction::load_constant(1, 1), Span(20, 22)),
|
||||||
(Instruction::set_local(1, 0), Span(12, 13)),
|
(Instruction::set_local(1, 0), Span(16, 17)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(41), Value::integer(42)],
|
vec![Value::integer(41), Value::integer(42)],
|
||||||
vec![Local::new(Identifier::new("x"), false, 0, Some(0)),]
|
vec![Local::new(Identifier::new("x"), true, 0, Some(0)),]
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user