Refine LoadList instruction; Improve logs; Refactor
This commit is contained in:
parent
60df8b4d64
commit
daca836db1
@ -76,11 +76,18 @@ impl Chunk {
|
||||
.ok_or(ChunkError::InstructionUnderflow { position })
|
||||
}
|
||||
|
||||
pub fn get_last_instruction(&self) -> Option<&(Instruction, Span)> {
|
||||
self.instructions.last()
|
||||
pub fn get_last_instruction(&self) -> Result<(&Instruction, &Span), ChunkError> {
|
||||
let (instruction, position) =
|
||||
self.instructions
|
||||
.last()
|
||||
.ok_or_else(|| ChunkError::InstructionUnderflow {
|
||||
position: Span(0, 0),
|
||||
})?;
|
||||
|
||||
Ok((instruction, position))
|
||||
}
|
||||
|
||||
pub fn get_last_operation(&self) -> Option<Operation> {
|
||||
pub fn get_last_operation(&self) -> Result<Operation, ChunkError> {
|
||||
self.get_last_instruction()
|
||||
.map(|(instruction, _)| instruction.operation())
|
||||
}
|
||||
|
@ -1,17 +1,15 @@
|
||||
//! Key used to identify a value or type.
|
||||
//!
|
||||
//! Identifiers are used to uniquely identify values and types in Dust programs. They are
|
||||
//! cached to avoid duplication. This means that two identifiers with the same text are the same
|
||||
//! object in memory.
|
||||
//! Identifiers are used to uniquely identify values and types in Dust programs.
|
||||
//!
|
||||
//! # Examples
|
||||
//! ```
|
||||
//! # use dust_lang::Identifier;
|
||||
//! let foo = Identifier::new("foo");
|
||||
//! let also_foo = Identifier::new("foo");
|
||||
//! let another_foo = Identifier::new("foo");
|
||||
//! let also_foo = foo.clone();
|
||||
//! let another_foo = also_foo.clone();
|
||||
//!
|
||||
//! assert_eq!(foo.strong_count(), 4); // One for each of the above and one for the cache.
|
||||
//! assert_eq!(foo.strong_count(), 3); // One for each of the above.
|
||||
//! ```
|
||||
use std::{
|
||||
fmt::{self, Display, Formatter},
|
||||
@ -27,7 +25,6 @@ use serde::{de::Visitor, Deserialize, Serialize};
|
||||
pub struct Identifier(Arc<String>);
|
||||
|
||||
impl Identifier {
|
||||
/// Creates a new identifier or returns a clone of an existing one from a cache.
|
||||
pub fn new<T: ToString>(text: T) -> Self {
|
||||
let string = text.to_string();
|
||||
|
||||
|
@ -41,12 +41,12 @@ impl Instruction {
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn load_list(to_register: u8, start_register: u8, list_length: u8) -> Instruction {
|
||||
pub fn load_list(to_register: u8, start_register: u8, end_register: u8) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::LoadList as u32);
|
||||
|
||||
instruction.set_a(to_register);
|
||||
instruction.set_b(start_register);
|
||||
instruction.set_c(list_length);
|
||||
instruction.set_c(end_register);
|
||||
|
||||
instruction
|
||||
}
|
||||
@ -371,12 +371,9 @@ impl Instruction {
|
||||
Operation::LoadList => {
|
||||
let destination = self.a();
|
||||
let first_index = self.b();
|
||||
let last_index = destination.saturating_sub(1);
|
||||
let last_index = self.c();
|
||||
|
||||
Some(format!(
|
||||
"R{} = [R{}..=R{}]",
|
||||
destination, first_index, last_index
|
||||
))
|
||||
Some(format!("R{destination} = [R{first_index}..=R{last_index}]",))
|
||||
}
|
||||
Operation::DefineLocal => {
|
||||
let destination = self.a();
|
||||
|
@ -40,7 +40,7 @@ impl<'src> Parser<'src> {
|
||||
pub fn new(mut lexer: Lexer<'src>) -> Result<Self, ParseError> {
|
||||
let (current_token, current_position) = lexer.next_token()?;
|
||||
|
||||
log::trace!("Starting parser with token \"{current_token}\" at {current_position}");
|
||||
log::info!("Starting parser with token \"{current_token}\" at {current_position}");
|
||||
|
||||
Ok(Parser {
|
||||
lexer,
|
||||
@ -96,7 +96,7 @@ impl<'src> Parser<'src> {
|
||||
|
||||
let (new_token, position) = self.lexer.next_token()?;
|
||||
|
||||
log::trace!("Parsing \"{new_token}\" at {position}");
|
||||
log::info!("Parsing \"{new_token}\" at {position}");
|
||||
|
||||
self.previous_token = replace(&mut self.current_token, new_token);
|
||||
self.previous_position = replace(&mut self.current_position, position);
|
||||
@ -438,7 +438,7 @@ impl<'src> Parser<'src> {
|
||||
]
|
||||
};
|
||||
|
||||
while let Some(operation) = self.chunk.get_last_operation() {
|
||||
while let Ok(operation) = self.chunk.get_last_operation() {
|
||||
if operation.is_math() {
|
||||
let (instruction, position) = self.chunk.pop_instruction(self.current_position)?;
|
||||
|
||||
@ -688,14 +688,13 @@ impl<'src> Parser<'src> {
|
||||
self.advance()?;
|
||||
|
||||
let start_register = self.current_register;
|
||||
let mut length = 0;
|
||||
|
||||
while !self.allow(TokenKind::RightSquareBrace)? && !self.is_eof() {
|
||||
let next_register = self.current_register;
|
||||
|
||||
self.parse(Precedence::Assignment)?; // Do not allow assignment
|
||||
|
||||
if let Some(Operation::LoadConstant) = self.chunk.get_last_operation() {
|
||||
if let Operation::LoadConstant = self.chunk.get_last_operation()? {
|
||||
self.increment_register()?;
|
||||
}
|
||||
|
||||
@ -706,19 +705,16 @@ impl<'src> Parser<'src> {
|
||||
);
|
||||
}
|
||||
|
||||
length += 1;
|
||||
|
||||
if !self.allow(TokenKind::Comma)? {
|
||||
self.expect(TokenKind::RightSquareBrace)?;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let end_register = self.current_register - 1;
|
||||
let end = self.current_position.1;
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::load_list(self.current_register, start_register, length),
|
||||
Instruction::load_list(self.current_register, start_register, end_register),
|
||||
Span(start, end),
|
||||
);
|
||||
self.increment_register()?;
|
||||
@ -864,17 +860,11 @@ impl<'src> Parser<'src> {
|
||||
self.parse_expression()?;
|
||||
self.increment_register()?;
|
||||
|
||||
let (previous_instruction, previous_position) = *self
|
||||
.chunk
|
||||
.get_last_instruction()
|
||||
.ok_or_else(|| ParseError::ExpectedExpression {
|
||||
found: self.current_token.to_owned(),
|
||||
position,
|
||||
})?;
|
||||
let (previous_instruction, previous_position) = self.chunk.get_last_instruction()?;
|
||||
let register = previous_instruction.a();
|
||||
let local_index =
|
||||
self.chunk
|
||||
.declare_local(identifier, is_mutable, register, previous_position)?;
|
||||
.declare_local(identifier, is_mutable, register, *previous_position)?;
|
||||
|
||||
// Optimize for assignment to a comparison
|
||||
// if let Operation::Jump = previous_instruction.operation() {
|
||||
@ -909,8 +899,8 @@ impl<'src> Parser<'src> {
|
||||
let allow_return = precedence == Precedence::None;
|
||||
|
||||
if let Some(prefix_parser) = ParseRule::from(&self.current_token.kind()).prefix {
|
||||
log::trace!(
|
||||
"Parsing \"{}\" as prefix at precedence {precedence}",
|
||||
log::debug!(
|
||||
"Prefix \"{}\" has precedence {precedence}",
|
||||
self.current_token,
|
||||
);
|
||||
|
||||
@ -921,8 +911,8 @@ impl<'src> Parser<'src> {
|
||||
|
||||
while precedence <= infix_rule.precedence {
|
||||
if let Some(infix_parser) = infix_rule.infix {
|
||||
log::trace!(
|
||||
"Parsing \"{}\" as infix at precedence {precedence}",
|
||||
log::debug!(
|
||||
"Infix \"{}\" has precedence {precedence}",
|
||||
self.current_token,
|
||||
);
|
||||
|
||||
|
@ -321,7 +321,7 @@ fn list_with_complex_expression() {
|
||||
),
|
||||
(Instruction::subtract(3, 1, 2), Span(10, 11)),
|
||||
(Instruction::close(1, 3), Span(17, 18)),
|
||||
(Instruction::load_list(4, 0, 2), Span(0, 18)),
|
||||
(Instruction::load_list(4, 0, 3), Span(0, 18)),
|
||||
(Instruction::end(true), Span(18, 18)),
|
||||
],
|
||||
vec![
|
||||
@ -352,7 +352,7 @@ fn list_with_simple_expression() {
|
||||
Span(6, 7)
|
||||
),
|
||||
(Instruction::load_constant(2, 3), Span(11, 12)),
|
||||
(Instruction::load_list(3, 0, 3), Span(0, 13)),
|
||||
(Instruction::load_list(3, 0, 2), Span(0, 13)),
|
||||
(Instruction::end(true), Span(13, 13)),
|
||||
],
|
||||
vec![
|
||||
@ -377,7 +377,7 @@ fn list() {
|
||||
(Instruction::load_constant(0, 0), Span(1, 2)),
|
||||
(Instruction::load_constant(1, 1), Span(4, 5)),
|
||||
(Instruction::load_constant(2, 2), Span(7, 8)),
|
||||
(Instruction::load_list(3, 0, 3), Span(0, 9)),
|
||||
(Instruction::load_list(3, 0, 2), Span(0, 9)),
|
||||
(Instruction::end(true), Span(9, 9)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2), Value::integer(3),],
|
||||
|
@ -56,7 +56,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() {
|
||||
log::trace!(
|
||||
log::info!(
|
||||
"Running IP {} {} at {position}",
|
||||
self.ip - 1,
|
||||
instruction.operation()
|
||||
@ -68,6 +68,8 @@ impl Vm {
|
||||
let to = instruction.a();
|
||||
let value = self.take(from, position)?;
|
||||
|
||||
log::debug!("R{from} -{{{value}}}-> R{to}");
|
||||
|
||||
self.insert(value, to, position)?;
|
||||
}
|
||||
Operation::Close => {
|
||||
@ -100,13 +102,14 @@ impl Vm {
|
||||
Operation::LoadList => {
|
||||
let to_register = instruction.a();
|
||||
let first_register = instruction.b();
|
||||
let length = instruction.c();
|
||||
let last_register = first_register + length + 1;
|
||||
|
||||
let last_register = instruction.c();
|
||||
let length = last_register - first_register + 1;
|
||||
let mut list = Vec::with_capacity(length as usize);
|
||||
|
||||
log::debug!("R{to_register} = [R{first_register}..=R{last_register}]");
|
||||
|
||||
for register_index in first_register..=last_register {
|
||||
let value = match self.clone(register_index, position) {
|
||||
let value = match self.take(register_index, position) {
|
||||
Ok(value) => value,
|
||||
Err(VmError::EmptyRegister { .. }) => continue,
|
||||
Err(error) => return Err(error),
|
||||
@ -126,81 +129,91 @@ impl Vm {
|
||||
Operation::GetLocal => {
|
||||
let register_index = instruction.a();
|
||||
let local_index = instruction.b();
|
||||
let local = self.chunk.get_local(local_index, position)?.clone();
|
||||
let value = self.clone_as_variable(local, position)?;
|
||||
let local = self.chunk.get_local(local_index, position)?;
|
||||
let value = if let Some(index) = local.register_index {
|
||||
self.take(index, position)?
|
||||
} else {
|
||||
return Err(VmError::UndefinedVariable {
|
||||
identifier: local.identifier.clone(),
|
||||
position,
|
||||
});
|
||||
};
|
||||
|
||||
self.insert(value, register_index, position)?;
|
||||
}
|
||||
Operation::SetLocal => {
|
||||
let register_index = instruction.a();
|
||||
let local_index = instruction.b();
|
||||
let local = self.chunk.get_local(local_index, position)?.clone();
|
||||
let value = self.clone_as_variable(local, position)?;
|
||||
let local = self.chunk.get_local(local_index, position)?;
|
||||
let value = if let Some(index) = local.register_index {
|
||||
self.take(index, position)?
|
||||
} else {
|
||||
return Err(VmError::UndefinedVariable {
|
||||
identifier: local.identifier.clone(),
|
||||
position,
|
||||
});
|
||||
};
|
||||
|
||||
let new_value = if instruction.b_is_constant() {
|
||||
self.chunk.take_constant(register_index, position)?
|
||||
} else {
|
||||
self.clone(register_index, position)?
|
||||
self.take(register_index, position)?
|
||||
};
|
||||
|
||||
value
|
||||
.mutate(new_value)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.insert(value, register_index, position)?;
|
||||
}
|
||||
Operation::Add => {
|
||||
let (left, right) = get_arguments(self, instruction, position)?;
|
||||
|
||||
log::debug!("{left} + {right}");
|
||||
|
||||
let sum = left
|
||||
.add(right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
log::debug!("{left} + {right} = {sum}");
|
||||
|
||||
self.insert(sum, instruction.a(), position)?;
|
||||
}
|
||||
Operation::Subtract => {
|
||||
let (left, right) = get_arguments(self, instruction, position)?;
|
||||
|
||||
log::debug!("{left} - {right}");
|
||||
|
||||
let difference = left
|
||||
.subtract(right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
log::debug!("{left} - {right} = {difference}");
|
||||
|
||||
self.insert(difference, instruction.a(), position)?;
|
||||
}
|
||||
Operation::Multiply => {
|
||||
let (left, right) = get_arguments(self, instruction, position)?;
|
||||
|
||||
log::debug!("{left} * {right}");
|
||||
|
||||
let product = left
|
||||
.multiply(right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
log::debug!("{position} {left} * {right}");
|
||||
|
||||
self.insert(product, instruction.a(), position)?;
|
||||
}
|
||||
Operation::Divide => {
|
||||
let (left, right) = get_arguments(self, instruction, position)?;
|
||||
|
||||
log::debug!("{left} / {right}");
|
||||
|
||||
let quotient = left
|
||||
.divide(right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
log::debug!("{left} / {right} = {quotient}");
|
||||
|
||||
self.insert(quotient, instruction.a(), position)?;
|
||||
}
|
||||
Operation::Modulo => {
|
||||
let (left, right) = get_arguments(self, instruction, position)?;
|
||||
|
||||
log::debug!("{left} % {right}");
|
||||
|
||||
let remainder = left
|
||||
.modulo(right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
log::debug!("{left} % {right} = {remainder}");
|
||||
|
||||
self.insert(remainder, instruction.a(), position)?;
|
||||
}
|
||||
Operation::Test => {
|
||||
@ -238,6 +251,7 @@ impl Vm {
|
||||
debug_assert_eq!(jump.operation(), Operation::Jump);
|
||||
|
||||
let (left, right) = get_arguments(self, instruction, position)?;
|
||||
|
||||
let boolean = left
|
||||
.equal(right)
|
||||
.map_err(|error| VmError::Value { error, position })?
|
||||
@ -248,6 +262,8 @@ impl Vm {
|
||||
})?;
|
||||
let compare_to = instruction.a_as_boolean();
|
||||
|
||||
log::debug!("{left} == {right} = {boolean} == {compare_to}");
|
||||
|
||||
if boolean == compare_to {
|
||||
self.ip += 1;
|
||||
} else {
|
||||
@ -263,11 +279,12 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
Operation::Less => {
|
||||
let (jump, _) = *self.chunk.get_instruction(self.ip, position)?;
|
||||
let jump = self.chunk.get_instruction(self.ip, position)?.0;
|
||||
|
||||
debug_assert_eq!(jump.operation(), Operation::Jump);
|
||||
|
||||
let (left, right) = get_arguments(self, instruction, position)?;
|
||||
|
||||
let boolean = left
|
||||
.less_than(right)
|
||||
.map_err(|error| VmError::Value { error, position })?
|
||||
@ -278,6 +295,8 @@ impl Vm {
|
||||
})?;
|
||||
let compare_to = instruction.a_as_boolean();
|
||||
|
||||
log::debug!("{left} < {right} = {boolean} == {compare_to}");
|
||||
|
||||
if boolean == compare_to {
|
||||
self.ip += 1;
|
||||
} else {
|
||||
@ -293,7 +312,7 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
Operation::LessEqual => {
|
||||
let (jump, _) = *self.chunk.get_instruction(self.ip, position)?;
|
||||
let jump = self.chunk.get_instruction(self.ip, position)?.0;
|
||||
|
||||
debug_assert_eq!(jump.operation(), Operation::Jump);
|
||||
|
||||
@ -308,6 +327,8 @@ impl Vm {
|
||||
})?;
|
||||
let compare_to = instruction.a_as_boolean();
|
||||
|
||||
log::debug!("{left} <= {right} = {boolean} == {compare_to}");
|
||||
|
||||
if boolean == compare_to {
|
||||
self.ip += 1;
|
||||
} else {
|
||||
@ -332,6 +353,8 @@ impl Vm {
|
||||
.negate()
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
log::debug!("-({value}) = {negated}");
|
||||
|
||||
self.insert(negated, instruction.a(), position)?;
|
||||
}
|
||||
Operation::Not => {
|
||||
@ -344,6 +367,8 @@ impl Vm {
|
||||
.not()
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
log::debug!("!{value} = {not}");
|
||||
|
||||
self.insert(not, instruction.a(), position)?;
|
||||
}
|
||||
Operation::Jump => {
|
||||
|
@ -24,21 +24,21 @@ fn main() {
|
||||
.parse_env("DUST_LOG")
|
||||
.format(|buf, record| {
|
||||
let level = match record.level() {
|
||||
Level::Error => "ERROR".red(),
|
||||
Level::Warn => "WARN".yellow(),
|
||||
Level::Info => "INFO".white(),
|
||||
Level::Debug => "DEBUG".blue(),
|
||||
Level::Trace => "TRACE".purple(),
|
||||
Level::Info => "INFO".white().bold(),
|
||||
Level::Debug => "DEBUG".blue().bold(),
|
||||
Level::Warn => "WARN".yellow().bold(),
|
||||
Level::Error => "ERROR".red().bold(),
|
||||
Level::Trace => "TRACE".purple().bold(),
|
||||
}
|
||||
.bold();
|
||||
let level_display = format!("[{level:^5}]").white().on_black();
|
||||
let level_display = format!("{level:<5}");
|
||||
let module = record
|
||||
.module_path()
|
||||
.map(|path| path.split("::").last().unwrap_or("unknown"))
|
||||
.map(|path| path.split("::").last().unwrap_or(path))
|
||||
.unwrap_or("unknown")
|
||||
.dimmed();
|
||||
|
||||
writeln!(buf, "{level_display} {module:^6} {}", record.args())
|
||||
writeln!(buf, "{level_display:^10} {module:^6} {}", record.args())
|
||||
})
|
||||
.init();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user