Add implicit returns and fix variable declaration and resolution
This commit is contained in:
parent
f936c30b4f
commit
4ba3a47ae5
@ -2,7 +2,9 @@ use std::fmt::{self, Debug, Display, Formatter};
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{identifier_stack::Local, Identifier, IdentifierStack, Instruction, Span, Value};
|
use crate::{
|
||||||
|
identifier_stack::Local, Identifier, IdentifierStack, Instruction, Span, Value, ValueLocation,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
@ -82,6 +84,12 @@ impl Chunk {
|
|||||||
self.identifiers.contains(identifier)
|
self.identifiers.contains(identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_local(&self, index: u8) -> Result<&Local, ChunkError> {
|
||||||
|
self.identifiers
|
||||||
|
.get(index as usize)
|
||||||
|
.ok_or(ChunkError::IdentifierIndexOutOfBounds(index))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_identifier(&self, index: u8) -> Result<&Identifier, ChunkError> {
|
pub fn get_identifier(&self, index: u8) -> Result<&Identifier, ChunkError> {
|
||||||
self.identifiers
|
self.identifiers
|
||||||
.get(index as usize)
|
.get(index as usize)
|
||||||
@ -95,13 +103,27 @@ impl Chunk {
|
|||||||
.ok_or(ChunkError::IdentifierNotFound(identifier.clone()))
|
.ok_or(ChunkError::IdentifierNotFound(identifier.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_identifier(&mut self, identifier: Identifier) -> Result<u8, ChunkError> {
|
pub fn push_constant_identifier(&mut self, identifier: Identifier) -> Result<u8, ChunkError> {
|
||||||
let starting_length = self.identifiers.local_count();
|
let starting_length = self.identifiers.local_count();
|
||||||
|
|
||||||
if starting_length + 1 > (u8::MAX as usize) {
|
if starting_length + 1 > (u8::MAX as usize) {
|
||||||
Err(ChunkError::IdentifierOverflow)
|
Err(ChunkError::IdentifierOverflow)
|
||||||
} else {
|
} else {
|
||||||
self.identifiers.declare(identifier);
|
self.identifiers
|
||||||
|
.declare(identifier, ValueLocation::ConstantStack);
|
||||||
|
|
||||||
|
Ok(starting_length as u8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_runtime_identifier(&mut self, identifier: Identifier) -> Result<u8, ChunkError> {
|
||||||
|
let starting_length = self.identifiers.local_count();
|
||||||
|
|
||||||
|
if starting_length + 1 > (u8::MAX as usize) {
|
||||||
|
Err(ChunkError::IdentifierOverflow)
|
||||||
|
} else {
|
||||||
|
self.identifiers
|
||||||
|
.declare(identifier, ValueLocation::RuntimeStack);
|
||||||
|
|
||||||
Ok(starting_length as u8)
|
Ok(starting_length as u8)
|
||||||
}
|
}
|
||||||
@ -116,17 +138,19 @@ impl Chunk {
|
|||||||
pub fn disassemble(&self, name: &str) -> String {
|
pub fn disassemble(&self, name: &str) -> String {
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
|
|
||||||
output.push_str("== ");
|
output.push_str("# ");
|
||||||
output.push_str(name);
|
output.push_str(name);
|
||||||
output.push_str(" ==\n--Code--\n");
|
output.push_str("\n\n## Code\n");
|
||||||
output.push_str("OFFSET INSTRUCTION POSITION\n");
|
output.push_str("------ ------------ ------------\n");
|
||||||
|
output.push_str("OFFSET POSITION INSTRUCTION\n");
|
||||||
|
output.push_str("------ ------------ ------------\n");
|
||||||
|
|
||||||
let mut previous = None;
|
let mut previous = None;
|
||||||
|
|
||||||
for (offset, (byte, position)) in self.code.iter().enumerate() {
|
for (offset, (byte, position)) in self.code.iter().enumerate() {
|
||||||
if let Some(
|
if let Some(
|
||||||
Instruction::Constant
|
Instruction::Constant
|
||||||
| Instruction::DefineVariable
|
| Instruction::DefineVariableConstant
|
||||||
| Instruction::GetVariable
|
| Instruction::GetVariable
|
||||||
| Instruction::SetVariable,
|
| Instruction::SetVariable,
|
||||||
) = previous
|
) = previous
|
||||||
@ -137,16 +161,21 @@ impl Chunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let instruction = Instruction::from_byte(*byte).unwrap();
|
let instruction = Instruction::from_byte(*byte).unwrap();
|
||||||
let display = format!("{offset:04} {}", instruction.disassemble(self, offset));
|
let display = format!(
|
||||||
let display_with_postion = format!("{display:27} {position}\n");
|
"{offset:4} {:12} {}\n",
|
||||||
|
position.to_string(),
|
||||||
|
instruction.disassemble(self, offset)
|
||||||
|
);
|
||||||
|
|
||||||
previous = Some(instruction);
|
previous = Some(instruction);
|
||||||
|
|
||||||
output.push_str(&display_with_postion);
|
output.push_str(&display);
|
||||||
}
|
}
|
||||||
|
|
||||||
output.push_str("--Constants--\n");
|
output.push_str("\n## Constants\n");
|
||||||
|
output.push_str("----- ---- -----\n");
|
||||||
output.push_str("INDEX KIND VALUE\n");
|
output.push_str("INDEX KIND VALUE\n");
|
||||||
|
output.push_str("----- ---- -----\n");
|
||||||
|
|
||||||
for (index, value) in self.constants.iter().enumerate() {
|
for (index, value) in self.constants.iter().enumerate() {
|
||||||
let value_kind_display = match value {
|
let value_kind_display = match value {
|
||||||
@ -154,16 +183,29 @@ impl Chunk {
|
|||||||
Value::Reference(_) => "REF ",
|
Value::Reference(_) => "REF ",
|
||||||
Value::Mutable(_) => "MUT ",
|
Value::Mutable(_) => "MUT ",
|
||||||
};
|
};
|
||||||
let display = format!("{index:04} {value_kind_display} {value}\n");
|
let display = format!("{index:3} {value_kind_display} {value}\n");
|
||||||
|
|
||||||
output.push_str(&display);
|
output.push_str(&display);
|
||||||
}
|
}
|
||||||
|
|
||||||
output.push_str("--Identifiers--\n");
|
output.push_str("\n## Identifiers\n");
|
||||||
output.push_str("INDEX IDENTIFIER DEPTH\n");
|
output.push_str("----- ---------- -------- -----\n");
|
||||||
|
output.push_str("INDEX IDENTIFIER LOCATION DEPTH\n");
|
||||||
|
output.push_str("----- ---------- -------- -----\n");
|
||||||
|
|
||||||
for (index, Local { identifier, depth }) in self.identifiers.iter().enumerate() {
|
for (
|
||||||
let display = format!("{index:04} {:10} {depth}\n", identifier.as_str());
|
index,
|
||||||
|
Local {
|
||||||
|
identifier,
|
||||||
|
depth,
|
||||||
|
value_location,
|
||||||
|
},
|
||||||
|
) in self.identifiers.iter().enumerate()
|
||||||
|
{
|
||||||
|
let display = format!(
|
||||||
|
"{index:3} {:10} {value_location} {depth}\n",
|
||||||
|
identifier.as_str()
|
||||||
|
);
|
||||||
output.push_str(&display);
|
output.push_str(&display);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,26 +241,22 @@ pub enum ChunkError {
|
|||||||
IdentifierNotFound(Identifier),
|
IdentifierNotFound(Identifier),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChunkError {
|
impl Display for ChunkError {
|
||||||
pub fn title(&self) -> &'static str {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
"Chunk Error"
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn description(&self) -> String {
|
|
||||||
match self {
|
match self {
|
||||||
Self::CodeIndexOfBounds(offset) => format!("{offset} is out of bounds",),
|
ChunkError::CodeIndexOfBounds(offset) => {
|
||||||
Self::ConstantOverflow => "More than 256 constants declared in one chunk".to_string(),
|
write!(f, "Code index out of bounds: {}", offset)
|
||||||
Self::ConstantIndexOutOfBounds(index) => {
|
|
||||||
format!("{index} is out of bounds")
|
|
||||||
}
|
}
|
||||||
Self::IdentifierIndexOutOfBounds(index) => {
|
ChunkError::ConstantOverflow => write!(f, "Constant overflow"),
|
||||||
format!("{index} is out of bounds")
|
ChunkError::ConstantIndexOutOfBounds(index) => {
|
||||||
|
write!(f, "Constant index out of bounds: {}", index)
|
||||||
}
|
}
|
||||||
Self::IdentifierOverflow => {
|
ChunkError::IdentifierIndexOutOfBounds(index) => {
|
||||||
"More than 256 identifiers declared in one chunk".to_string()
|
write!(f, "Identifier index out of bounds: {}", index)
|
||||||
}
|
}
|
||||||
Self::IdentifierNotFound(identifier) => {
|
ChunkError::IdentifierOverflow => write!(f, "Identifier overflow"),
|
||||||
format!("{} does not exist in this scope", identifier)
|
ChunkError::IdentifierNotFound(identifier) => {
|
||||||
|
write!(f, "Identifier not found: {}", identifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use annotate_snippets::{Level, Renderer, Snippet};
|
use annotate_snippets::{Level, Renderer, Snippet};
|
||||||
|
|
||||||
use crate::{vm::VmError, LexError, ParseError};
|
use crate::{vm::VmError, LexError, ParseError, Span};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum DustError<'src> {
|
pub enum DustError<'src> {
|
||||||
@ -26,26 +26,28 @@ impl<'src> DustError<'src> {
|
|||||||
match self {
|
match self {
|
||||||
DustError::Runtime { error, source } => {
|
DustError::Runtime { error, source } => {
|
||||||
let position = error.position();
|
let position = error.position();
|
||||||
let description = error.description();
|
let label = format!("Runtime error: {}", error.description());
|
||||||
let message = Level::Error.title(VmError::title()).snippet(
|
let details = error
|
||||||
Snippet::source(source).fold(true).annotation(
|
.details()
|
||||||
Level::Error
|
.unwrap_or_else(|| "While running this code".to_string());
|
||||||
.span(position.0..position.1)
|
let message = Level::Error.title(&label).snippet(
|
||||||
.label(&description),
|
Snippet::source(source)
|
||||||
),
|
.fold(true)
|
||||||
|
.annotation(Level::Error.span(position.0..position.1).label(&details)),
|
||||||
);
|
);
|
||||||
|
|
||||||
report.push_str(&renderer.render(message).to_string());
|
report.push_str(&renderer.render(message).to_string());
|
||||||
}
|
}
|
||||||
DustError::Parse { error, source } => {
|
DustError::Parse { error, source } => {
|
||||||
let position = error.position();
|
let position = error.position();
|
||||||
let description = error.description();
|
let label = format!("Parse error: {}", error.description());
|
||||||
let message = Level::Error.title(ParseError::title()).snippet(
|
let details = error
|
||||||
Snippet::source(source).fold(true).annotation(
|
.details()
|
||||||
Level::Error
|
.unwrap_or_else(|| "While parsing this code".to_string());
|
||||||
.span(position.0..position.1)
|
let message = Level::Error.title(&label).snippet(
|
||||||
.label(&description),
|
Snippet::source(source)
|
||||||
),
|
.fold(true)
|
||||||
|
.annotation(Level::Error.span(position.0..position.1).label(&details)),
|
||||||
);
|
);
|
||||||
|
|
||||||
report.push_str(&renderer.render(message).to_string());
|
report.push_str(&renderer.render(message).to_string());
|
||||||
@ -56,3 +58,10 @@ impl<'src> DustError<'src> {
|
|||||||
report
|
report
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait AnnotatedError {
|
||||||
|
fn title() -> &'static str;
|
||||||
|
fn description(&self) -> &'static str;
|
||||||
|
fn details(&self) -> Option<String>;
|
||||||
|
fn position(&self) -> Span;
|
||||||
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::Identifier;
|
use crate::Identifier;
|
||||||
@ -68,10 +70,11 @@ impl IdentifierStack {
|
|||||||
self.scope_depth -= 1;
|
self.scope_depth -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declare(&mut self, identifier: Identifier) {
|
pub fn declare(&mut self, identifier: Identifier, value_location: ValueLocation) {
|
||||||
self.locals.push(Local {
|
self.locals.push(Local {
|
||||||
identifier,
|
identifier,
|
||||||
depth: self.scope_depth,
|
depth: self.scope_depth,
|
||||||
|
value_location,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,4 +101,20 @@ impl PartialEq for IdentifierStack {
|
|||||||
pub struct Local {
|
pub struct Local {
|
||||||
pub identifier: Identifier,
|
pub identifier: Identifier,
|
||||||
pub depth: usize,
|
pub depth: usize,
|
||||||
|
pub value_location: ValueLocation,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum ValueLocation {
|
||||||
|
ConstantStack,
|
||||||
|
RuntimeStack,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ValueLocation {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ValueLocation::ConstantStack => write!(f, "constant"),
|
||||||
|
ValueLocation::RuntimeStack => write!(f, "runtime "),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,27 +11,28 @@ pub enum Instruction {
|
|||||||
Pop = 2,
|
Pop = 2,
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
DefineVariable = 3,
|
DefineVariableRuntime = 3,
|
||||||
GetVariable = 4,
|
DefineVariableConstant = 4,
|
||||||
SetVariable = 5,
|
GetVariable = 5,
|
||||||
|
SetVariable = 6,
|
||||||
|
|
||||||
// Unary
|
// Unary
|
||||||
Negate = 6,
|
Negate = 7,
|
||||||
Not = 7,
|
Not = 8,
|
||||||
|
|
||||||
// Binary
|
// Binary
|
||||||
Add = 8,
|
Add = 9,
|
||||||
Subtract = 9,
|
Subtract = 10,
|
||||||
Multiply = 10,
|
Multiply = 11,
|
||||||
Divide = 11,
|
Divide = 12,
|
||||||
Greater = 12,
|
Greater = 13,
|
||||||
Less = 13,
|
Less = 14,
|
||||||
GreaterEqual = 14,
|
GreaterEqual = 15,
|
||||||
LessEqual = 15,
|
LessEqual = 16,
|
||||||
Equal = 16,
|
Equal = 17,
|
||||||
NotEqual = 17,
|
NotEqual = 18,
|
||||||
And = 18,
|
And = 19,
|
||||||
Or = 19,
|
Or = 20,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instruction {
|
impl Instruction {
|
||||||
@ -40,23 +41,24 @@ impl Instruction {
|
|||||||
0 => Some(Instruction::Constant),
|
0 => Some(Instruction::Constant),
|
||||||
1 => Some(Instruction::Return),
|
1 => Some(Instruction::Return),
|
||||||
2 => Some(Instruction::Pop),
|
2 => Some(Instruction::Pop),
|
||||||
3 => Some(Instruction::DefineVariable),
|
3 => Some(Instruction::DefineVariableRuntime),
|
||||||
4 => Some(Instruction::GetVariable),
|
4 => Some(Instruction::DefineVariableConstant),
|
||||||
5 => Some(Instruction::SetVariable),
|
5 => Some(Instruction::GetVariable),
|
||||||
6 => Some(Instruction::Negate),
|
6 => Some(Instruction::SetVariable),
|
||||||
7 => Some(Instruction::Not),
|
7 => Some(Instruction::Negate),
|
||||||
8 => Some(Instruction::Add),
|
8 => Some(Instruction::Not),
|
||||||
9 => Some(Instruction::Subtract),
|
9 => Some(Instruction::Add),
|
||||||
10 => Some(Instruction::Multiply),
|
10 => Some(Instruction::Subtract),
|
||||||
11 => Some(Instruction::Divide),
|
11 => Some(Instruction::Multiply),
|
||||||
12 => Some(Instruction::Greater),
|
12 => Some(Instruction::Divide),
|
||||||
13 => Some(Instruction::Less),
|
13 => Some(Instruction::Greater),
|
||||||
14 => Some(Instruction::GreaterEqual),
|
14 => Some(Instruction::Less),
|
||||||
15 => Some(Instruction::LessEqual),
|
15 => Some(Instruction::GreaterEqual),
|
||||||
16 => Some(Instruction::Equal),
|
16 => Some(Instruction::LessEqual),
|
||||||
17 => Some(Instruction::NotEqual),
|
17 => Some(Instruction::Equal),
|
||||||
18 => Some(Instruction::And),
|
18 => Some(Instruction::NotEqual),
|
||||||
19 => Some(Instruction::Or),
|
19 => Some(Instruction::And),
|
||||||
|
20 => Some(Instruction::Or),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,55 +66,46 @@ impl Instruction {
|
|||||||
pub fn disassemble(&self, chunk: &Chunk, offset: usize) -> String {
|
pub fn disassemble(&self, chunk: &Chunk, offset: usize) -> String {
|
||||||
match self {
|
match self {
|
||||||
Instruction::Constant => {
|
Instruction::Constant => {
|
||||||
let (index_display, value_display) =
|
let (argument, _) = chunk.get_code(offset + 1).unwrap();
|
||||||
if let Ok((index, _)) = chunk.get_code(offset + 1) {
|
let value_display = chunk
|
||||||
let index_string = index.to_string();
|
.get_constant(*argument)
|
||||||
let value_string = chunk
|
|
||||||
.get_constant(*index)
|
|
||||||
.map(|value| value.to_string())
|
.map(|value| value.to_string())
|
||||||
.unwrap_or_else(|error| format!("{:?}", error));
|
.unwrap_or_else(|error| error.to_string());
|
||||||
|
|
||||||
(index_string, value_string)
|
format!("CONSTANT {argument} {value_display}")
|
||||||
} else {
|
|
||||||
let index = "ERROR".to_string();
|
|
||||||
let value = "ERROR".to_string();
|
|
||||||
|
|
||||||
(index, value)
|
|
||||||
};
|
|
||||||
|
|
||||||
format!("CONSTANT {index_display} {value_display}")
|
|
||||||
}
|
}
|
||||||
Instruction::Return => format!("{offset:04} RETURN"),
|
Instruction::Return => "RETURN".to_string(),
|
||||||
Instruction::Pop => format!("{offset:04} POP"),
|
Instruction::Pop => "POP".to_string(),
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
Instruction::DefineVariable => {
|
Instruction::DefineVariableRuntime => "DEFINE_VARIABLE_RUNTIME".to_string(),
|
||||||
let (index, _) = chunk.get_code(offset + 1).unwrap();
|
Instruction::DefineVariableConstant => {
|
||||||
let identifier_display = match chunk.get_identifier(*index) {
|
let (argument, _) = chunk.get_code(offset + 1).unwrap();
|
||||||
|
let identifier_display = match chunk.get_identifier(*argument) {
|
||||||
Ok(identifier) => identifier.to_string(),
|
Ok(identifier) => identifier.to_string(),
|
||||||
Err(error) => format!("{:?}", error),
|
Err(error) => error.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
format!("DEFINE_VARIABLE {identifier_display} {index}")
|
format!("DEFINE_VARIABLE_CONSTANT {argument} {identifier_display}")
|
||||||
}
|
}
|
||||||
Instruction::GetVariable => {
|
Instruction::GetVariable => {
|
||||||
let (index, _) = chunk.get_code(offset + 1).unwrap();
|
let (argument, _) = chunk.get_code(offset + 1).unwrap();
|
||||||
let identifier_display = match chunk.get_identifier(*index) {
|
let identifier_display = match chunk.get_identifier(*argument) {
|
||||||
Ok(identifier) => identifier.to_string(),
|
Ok(identifier) => identifier.to_string(),
|
||||||
Err(error) => format!("{:?}", error),
|
Err(error) => error.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
format!("GET_VARIABLE {identifier_display} {index}")
|
format!("GET_VARIABLE {argument} {identifier_display}")
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction::SetVariable => {
|
Instruction::SetVariable => {
|
||||||
let (index, _) = chunk.get_code(offset + 1).unwrap();
|
let (argument, _) = chunk.get_code(offset + 1).unwrap();
|
||||||
let identifier_display = match chunk.get_identifier(*index) {
|
let identifier_display = match chunk.get_identifier(*argument) {
|
||||||
Ok(identifier) => identifier.to_string(),
|
Ok(identifier) => identifier.to_string(),
|
||||||
Err(error) => format!("{:?}", error),
|
Err(error) => error.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
format!("SET_VARIABLE {identifier_display} {index}")
|
format!("SET_VARIABLE {identifier_display}")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unary
|
// Unary
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
//! - [`Lexer`], which lexes the input a token at a time
|
//! - [`Lexer`], which lexes the input a token at a time
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Span, Token};
|
use crate::{dust_error::AnnotatedError, Span, Token};
|
||||||
|
|
||||||
/// Lexes the input and return a vector of tokens and their positions.
|
/// Lexes the input and return a vector of tokens and their positions.
|
||||||
///
|
///
|
||||||
@ -529,26 +529,35 @@ pub enum LexError {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LexError {
|
impl AnnotatedError for LexError {
|
||||||
pub fn title() -> &'static str {
|
fn title() -> &'static str {
|
||||||
"Lex Error"
|
"Lex Error"
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn description(&self) -> String {
|
fn description(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::ExpectedCharacter {
|
Self::ExpectedCharacter { .. } => "Expected character",
|
||||||
expected, actual, ..
|
Self::UnexpectedCharacter { .. } => "Unexpected character",
|
||||||
} => {
|
Self::UnexpectedEndOfFile { .. } => "Unexpected end of file",
|
||||||
format!("Expected character \"{}\", found \"{}\"", expected, actual)
|
|
||||||
}
|
|
||||||
Self::UnexpectedCharacter { actual, .. } => {
|
|
||||||
format!("Unexpected character \"{}\"", actual)
|
|
||||||
}
|
|
||||||
Self::UnexpectedEndOfFile { .. } => "Unexpected end of file".to_string(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn position(&self) -> Span {
|
fn details(&self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
Self::ExpectedCharacter {
|
||||||
|
expected, actual, ..
|
||||||
|
} => Some(format!(
|
||||||
|
"Expected character \"{}\", found \"{}\"",
|
||||||
|
expected, actual
|
||||||
|
)),
|
||||||
|
Self::UnexpectedCharacter { actual, .. } => {
|
||||||
|
Some(format!("Unexpected character \"{}\"", actual))
|
||||||
|
}
|
||||||
|
Self::UnexpectedEndOfFile { .. } => Some("Unexpected end of file".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn position(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Self::ExpectedCharacter { position, .. } => Span(*position, *position),
|
Self::ExpectedCharacter { position, .. } => Span(*position, *position),
|
||||||
Self::UnexpectedCharacter { position, .. } => Span(*position, *position),
|
Self::UnexpectedCharacter { position, .. } => Span(*position, *position),
|
||||||
|
@ -30,9 +30,9 @@ pub mod vm;
|
|||||||
|
|
||||||
pub use chunk::{Chunk, ChunkError};
|
pub use chunk::{Chunk, ChunkError};
|
||||||
pub use constructor::{ConstructError, Constructor};
|
pub use constructor::{ConstructError, Constructor};
|
||||||
pub use dust_error::DustError;
|
pub use dust_error::{AnnotatedError, DustError};
|
||||||
pub use identifier::Identifier;
|
pub use identifier::Identifier;
|
||||||
pub use identifier_stack::IdentifierStack;
|
pub use identifier_stack::{IdentifierStack, Local, ValueLocation};
|
||||||
pub use instruction::Instruction;
|
pub use instruction::Instruction;
|
||||||
pub use lexer::{lex, LexError, Lexer};
|
pub use lexer::{lex, LexError, Lexer};
|
||||||
pub use parser::{parse, ParseError, Parser};
|
pub use parser::{parse, ParseError, Parser};
|
||||||
|
@ -5,8 +5,8 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Chunk, ChunkError, DustError, Identifier, Instruction, LexError, Lexer, Span, Token, TokenKind,
|
dust_error::AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, LexError,
|
||||||
TokenOwned, Value,
|
Lexer, Span, Token, TokenKind, TokenOwned, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parse(source: &str) -> Result<Chunk, DustError> {
|
pub fn parse(source: &str) -> Result<Chunk, DustError> {
|
||||||
@ -294,10 +294,14 @@ impl<'src> Parser<'src> {
|
|||||||
};
|
};
|
||||||
let has_semicolon = self.allow(TokenKind::Semicolon)?;
|
let has_semicolon = self.allow(TokenKind::Semicolon)?;
|
||||||
|
|
||||||
if is_expression_statement && has_semicolon {
|
if is_expression_statement {
|
||||||
let end = self.previous_position.1;
|
let end = self.previous_position.1;
|
||||||
|
|
||||||
|
if has_semicolon {
|
||||||
self.emit_byte(Instruction::Pop as u8, Span(start, end));
|
self.emit_byte(Instruction::Pop as u8, Span(start, end));
|
||||||
|
} else {
|
||||||
|
self.emit_byte(Instruction::Return as u8, Span(start, end));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -307,15 +311,10 @@ impl<'src> Parser<'src> {
|
|||||||
self.expect(TokenKind::Let)?;
|
self.expect(TokenKind::Let)?;
|
||||||
|
|
||||||
let position = self.current_position;
|
let position = self.current_position;
|
||||||
|
let identifier = if let Token::Identifier(text) = self.current_token {
|
||||||
let identifier_index = if let Token::Identifier(text) = self.current_token {
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let identifier = Identifier::new(text);
|
Identifier::new(text)
|
||||||
|
|
||||||
self.chunk
|
|
||||||
.push_identifier(identifier)
|
|
||||||
.map_err(|error| ParseError::Chunk { error, position })?
|
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::ExpectedToken {
|
return Err(ParseError::ExpectedToken {
|
||||||
expected: TokenKind::Identifier,
|
expected: TokenKind::Identifier,
|
||||||
@ -324,10 +323,35 @@ impl<'src> Parser<'src> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
self.emit_byte(Instruction::DefineVariable as u8, position);
|
|
||||||
self.emit_byte(identifier_index, position);
|
|
||||||
self.expect(TokenKind::Equal)?;
|
self.expect(TokenKind::Equal)?;
|
||||||
|
|
||||||
|
let is_constant = matches!(
|
||||||
|
self.current_token,
|
||||||
|
Token::Boolean(_)
|
||||||
|
| Token::Byte(_)
|
||||||
|
| Token::Character(_)
|
||||||
|
| Token::Float(_)
|
||||||
|
| Token::Integer(_)
|
||||||
|
| Token::String(_)
|
||||||
|
);
|
||||||
|
|
||||||
|
let identifier_index = if is_constant {
|
||||||
|
self.chunk.push_constant_identifier(identifier)
|
||||||
|
} else {
|
||||||
|
self.chunk.push_runtime_identifier(identifier)
|
||||||
|
}
|
||||||
|
.map_err(|error| ParseError::Chunk { error, position })?;
|
||||||
|
|
||||||
|
if is_constant {
|
||||||
|
self.emit_byte(Instruction::DefineVariableConstant as u8, position);
|
||||||
|
self.emit_byte(identifier_index, position);
|
||||||
|
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
} else {
|
||||||
|
self.parse_expression()?;
|
||||||
|
|
||||||
|
self.emit_byte(Instruction::DefineVariableRuntime as u8, position);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -592,34 +616,44 @@ pub enum ParseError {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParseError {
|
impl AnnotatedError for ParseError {
|
||||||
pub fn title() -> &'static str {
|
fn title() -> &'static str {
|
||||||
"Parse Error"
|
"Parse Error"
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn description(&self) -> String {
|
fn description(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::ExpectedExpression { found, .. } => {
|
Self::ExpectedExpression { .. } => "Expected an expression",
|
||||||
format!("Expected an expression, found \"{found}\"")
|
Self::ExpectedToken { .. } => "Expected a specific token",
|
||||||
}
|
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
|
||||||
Self::ExpectedToken {
|
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
|
||||||
expected, found, ..
|
Self::Chunk { .. } => "Chunk error",
|
||||||
} => {
|
Self::Lex(_) => "Lex error",
|
||||||
format!("Expected \"{expected}\", found \"{found}\"")
|
Self::ParseIntError { .. } => "Failed to parse integer",
|
||||||
}
|
|
||||||
Self::ExpectedTokenMultiple {
|
|
||||||
expected, found, ..
|
|
||||||
} => format!("Expected one of {:?}, found \"{found}\"", expected,),
|
|
||||||
Self::InvalidAssignmentTarget { found, .. } => {
|
|
||||||
format!("Invalid assignment target \"{found}\"")
|
|
||||||
}
|
|
||||||
Self::Chunk { error, .. } => error.description(),
|
|
||||||
Self::Lex(error) => error.description(),
|
|
||||||
Self::ParseIntError { error, .. } => error.to_string(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn position(&self) -> Span {
|
fn details(&self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
Self::ExpectedExpression { found, .. } => {
|
||||||
|
Some(format!("Expected an expression, found \"{found}\""))
|
||||||
|
}
|
||||||
|
Self::ExpectedToken {
|
||||||
|
expected, found, ..
|
||||||
|
} => Some(format!("Expected \"{expected}\", found \"{found}\"")),
|
||||||
|
Self::ExpectedTokenMultiple {
|
||||||
|
expected, found, ..
|
||||||
|
} => Some(format!("Expected one of {expected:?}, found \"{found}\"")),
|
||||||
|
Self::InvalidAssignmentTarget { found, .. } => {
|
||||||
|
Some(format!("Invalid assignment target \"{found}\""))
|
||||||
|
}
|
||||||
|
Self::Chunk { error, .. } => Some(error.to_string()),
|
||||||
|
Self::Lex(error) => Some(error.to_string()),
|
||||||
|
Self::ParseIntError { error, .. } => Some(error.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn position(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Self::ExpectedExpression { position, .. } => *position,
|
Self::ExpectedExpression { position, .. } => *position,
|
||||||
Self::ExpectedToken { position, .. } => *position,
|
Self::ExpectedToken { position, .. } => *position,
|
||||||
@ -640,7 +674,7 @@ impl From<LexError> for ParseError {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::identifier_stack::Local;
|
use crate::{identifier_stack::Local, ValueLocation};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -653,11 +687,11 @@ mod tests {
|
|||||||
test_chunk,
|
test_chunk,
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![
|
||||||
(Instruction::DefineVariable as u8, Span(4, 5)),
|
(Instruction::DefineVariableConstant as u8, Span(4, 5)),
|
||||||
(0, Span(4, 5)),
|
(0, Span(4, 5)),
|
||||||
(Instruction::Constant as u8, Span(8, 10)),
|
(Instruction::Constant as u8, Span(8, 10)),
|
||||||
(0, Span(8, 10)),
|
(0, Span(8, 10)),
|
||||||
(Instruction::DefineVariable as u8, Span(16, 17)),
|
(Instruction::DefineVariableConstant as u8, Span(16, 17)),
|
||||||
(1, Span(16, 17)),
|
(1, Span(16, 17)),
|
||||||
(Instruction::Constant as u8, Span(20, 22)),
|
(Instruction::Constant as u8, Span(20, 22)),
|
||||||
(1, Span(20, 22)),
|
(1, Span(20, 22)),
|
||||||
@ -665,17 +699,20 @@ mod tests {
|
|||||||
(0, Span(24, 25)),
|
(0, Span(24, 25)),
|
||||||
(Instruction::GetVariable as u8, Span(28, 29)),
|
(Instruction::GetVariable as u8, Span(28, 29)),
|
||||||
(1, Span(28, 29)),
|
(1, Span(28, 29)),
|
||||||
(Instruction::Add as u8, Span(26, 27))
|
(Instruction::Add as u8, Span(26, 27)),
|
||||||
|
(Instruction::Return as u8, Span(24, 29)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(42), Value::integer(42)],
|
vec![Value::integer(42), Value::integer(42)],
|
||||||
vec![
|
vec![
|
||||||
Local {
|
Local {
|
||||||
identifier: Identifier::new("x"),
|
identifier: Identifier::new("x"),
|
||||||
depth: 0
|
depth: 0,
|
||||||
|
value_location: ValueLocation::ConstantStack,
|
||||||
},
|
},
|
||||||
Local {
|
Local {
|
||||||
identifier: Identifier::new("y"),
|
identifier: Identifier::new("y"),
|
||||||
depth: 0
|
depth: 0,
|
||||||
|
value_location: ValueLocation::ConstantStack,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
))
|
))
|
||||||
@ -691,7 +728,7 @@ mod tests {
|
|||||||
test_chunk,
|
test_chunk,
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![
|
||||||
(Instruction::DefineVariable as u8, Span(4, 5)),
|
(Instruction::DefineVariableConstant as u8, Span(4, 5)),
|
||||||
(0, Span(4, 5)),
|
(0, Span(4, 5)),
|
||||||
(Instruction::Constant as u8, Span(8, 10)),
|
(Instruction::Constant as u8, Span(8, 10)),
|
||||||
(0, Span(8, 10)),
|
(0, Span(8, 10)),
|
||||||
@ -699,7 +736,8 @@ mod tests {
|
|||||||
vec![Value::integer(42)],
|
vec![Value::integer(42)],
|
||||||
vec![Local {
|
vec![Local {
|
||||||
identifier: Identifier::new("x"),
|
identifier: Identifier::new("x"),
|
||||||
depth: 0
|
depth: 0,
|
||||||
|
value_location: ValueLocation::ConstantStack,
|
||||||
}],
|
}],
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
@ -713,7 +751,11 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
test_chunk,
|
test_chunk,
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![(Instruction::Constant as u8, Span(0, 15)), (0, Span(0, 15))],
|
vec![
|
||||||
|
(Instruction::Constant as u8, Span(0, 15)),
|
||||||
|
(0, Span(0, 15)),
|
||||||
|
(Instruction::Return as u8, Span(0, 15)),
|
||||||
|
],
|
||||||
vec![Value::string("Hello, World!")],
|
vec![Value::string("Hello, World!")],
|
||||||
vec![],
|
vec![],
|
||||||
))
|
))
|
||||||
@ -728,7 +770,11 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
test_chunk,
|
test_chunk,
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![(Instruction::Constant as u8, Span(0, 2)), (0, Span(0, 2))],
|
vec![
|
||||||
|
(Instruction::Constant as u8, Span(0, 2)),
|
||||||
|
(0, Span(0, 2)),
|
||||||
|
(Instruction::Return as u8, Span(0, 2)),
|
||||||
|
],
|
||||||
vec![Value::integer(42)],
|
vec![Value::integer(42)],
|
||||||
vec![],
|
vec![],
|
||||||
))
|
))
|
||||||
@ -743,7 +789,11 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
test_chunk,
|
test_chunk,
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![(Instruction::Constant as u8, Span(0, 4)), (0, Span(0, 4))],
|
vec![
|
||||||
|
(Instruction::Constant as u8, Span(0, 4)),
|
||||||
|
(0, Span(0, 4)),
|
||||||
|
(Instruction::Return as u8, Span(0, 4)),
|
||||||
|
],
|
||||||
vec![Value::boolean(true)],
|
vec![Value::boolean(true)],
|
||||||
vec![],
|
vec![],
|
||||||
))
|
))
|
||||||
@ -767,6 +817,7 @@ mod tests {
|
|||||||
(Instruction::Constant as u8, Span(12, 13)),
|
(Instruction::Constant as u8, Span(12, 13)),
|
||||||
(2, Span(12, 13)),
|
(2, Span(12, 13)),
|
||||||
(Instruction::Multiply as u8, Span(10, 11)),
|
(Instruction::Multiply as u8, Span(10, 11)),
|
||||||
|
(Instruction::Return as u8, Span(0, 13)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(42), Value::integer(42), Value::integer(2)],
|
vec![Value::integer(42), Value::integer(42), Value::integer(2)],
|
||||||
vec![],
|
vec![],
|
||||||
@ -786,6 +837,7 @@ mod tests {
|
|||||||
(Instruction::Constant as u8, Span(2, 4)),
|
(Instruction::Constant as u8, Span(2, 4)),
|
||||||
(0, Span(2, 4)),
|
(0, Span(2, 4)),
|
||||||
(Instruction::Negate as u8, Span(0, 1)),
|
(Instruction::Negate as u8, Span(0, 1)),
|
||||||
|
(Instruction::Return as u8, Span(0, 5)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(42)],
|
vec![Value::integer(42)],
|
||||||
vec![],
|
vec![],
|
||||||
@ -807,6 +859,7 @@ mod tests {
|
|||||||
(Instruction::Constant as u8, Span(5, 7)),
|
(Instruction::Constant as u8, Span(5, 7)),
|
||||||
(1, Span(5, 7)),
|
(1, Span(5, 7)),
|
||||||
(Instruction::Add as u8, Span(3, 4)),
|
(Instruction::Add as u8, Span(3, 4)),
|
||||||
|
(Instruction::Return as u8, Span(0, 7)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(42), Value::integer(42)],
|
vec![Value::integer(42), Value::integer(42)],
|
||||||
vec![],
|
vec![],
|
||||||
@ -828,6 +881,7 @@ mod tests {
|
|||||||
(Instruction::Constant as u8, Span(5, 7)),
|
(Instruction::Constant as u8, Span(5, 7)),
|
||||||
(1, Span(5, 7)),
|
(1, Span(5, 7)),
|
||||||
(Instruction::Subtract as u8, Span(3, 4)),
|
(Instruction::Subtract as u8, Span(3, 4)),
|
||||||
|
(Instruction::Return as u8, Span(0, 7)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(42), Value::integer(42)],
|
vec![Value::integer(42), Value::integer(42)],
|
||||||
vec![],
|
vec![],
|
||||||
@ -849,6 +903,7 @@ mod tests {
|
|||||||
(Instruction::Constant as u8, Span(5, 7)),
|
(Instruction::Constant as u8, Span(5, 7)),
|
||||||
(1, Span(5, 7)),
|
(1, Span(5, 7)),
|
||||||
(Instruction::Multiply as u8, Span(3, 4)),
|
(Instruction::Multiply as u8, Span(3, 4)),
|
||||||
|
(Instruction::Return as u8, Span(0, 7)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(42), Value::integer(42)],
|
vec![Value::integer(42), Value::integer(42)],
|
||||||
vec![],
|
vec![],
|
||||||
@ -870,6 +925,7 @@ mod tests {
|
|||||||
(Instruction::Constant as u8, Span(5, 7)),
|
(Instruction::Constant as u8, Span(5, 7)),
|
||||||
(1, Span(5, 7)),
|
(1, Span(5, 7)),
|
||||||
(Instruction::Divide as u8, Span(3, 4)),
|
(Instruction::Divide as u8, Span(3, 4)),
|
||||||
|
(Instruction::Return as u8, Span(0, 7)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(42), Value::integer(42)],
|
vec![Value::integer(42), Value::integer(42)],
|
||||||
vec![],
|
vec![],
|
||||||
|
@ -1783,66 +1783,6 @@ pub enum ValueError {
|
|||||||
IndexOutOfBounds { value: Value, index: i64 },
|
IndexOutOfBounds { value: Value, index: i64 },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValueError {
|
|
||||||
pub fn title(&self) -> &'static str {
|
|
||||||
"Value Error"
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn description(&self) -> String {
|
|
||||||
match self {
|
|
||||||
ValueError::CannotAdd(left, right) => format!("Cannot add {} and {}", left, right),
|
|
||||||
ValueError::CannotAnd(left, right) => {
|
|
||||||
format!(
|
|
||||||
"Cannot use logical \"and\" operation on {} and {}",
|
|
||||||
left, right
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ValueError::CannotDivide(left, right) => {
|
|
||||||
format!("Cannot divide {} by {}", left, right)
|
|
||||||
}
|
|
||||||
ValueError::CannotGreaterThan(left, right) => {
|
|
||||||
format!("Cannot compare {} and {}", left, right)
|
|
||||||
}
|
|
||||||
ValueError::CannotGreaterThanOrEqual(left, right) => {
|
|
||||||
format!("Cannot compare {} and {}", left, right)
|
|
||||||
}
|
|
||||||
ValueError::CannotIndex { value, index } => {
|
|
||||||
format!("Cannot index {} with {}", value, index)
|
|
||||||
}
|
|
||||||
ValueError::CannotLessThan(left, right) => {
|
|
||||||
format!("Cannot compare {} and {}", left, right)
|
|
||||||
}
|
|
||||||
ValueError::CannotLessThanOrEqual(left, right) => {
|
|
||||||
format!("Cannot compare {} and {}", left, right)
|
|
||||||
}
|
|
||||||
ValueError::CannotMakeMutable => "Cannot make this value mutable".to_string(),
|
|
||||||
ValueError::CannotModulo(left, right) => {
|
|
||||||
format!("Cannot modulo {} by {}", left, right)
|
|
||||||
}
|
|
||||||
ValueError::CannotMultiply(left, right) => {
|
|
||||||
format!("Cannot multiply {} and {}", left, right)
|
|
||||||
}
|
|
||||||
ValueError::CannotMutate(value) => format!("Cannot mutate {}", value),
|
|
||||||
ValueError::CannotNegate(value) => format!("Cannot negate {}", value),
|
|
||||||
ValueError::CannotNot(value) => {
|
|
||||||
format!("Cannot use logical not operation on {}", value)
|
|
||||||
}
|
|
||||||
ValueError::CannotSubtract(left, right) => {
|
|
||||||
format!("Cannot subtract {} and {}", left, right)
|
|
||||||
}
|
|
||||||
ValueError::CannotOr(left, right) => {
|
|
||||||
format!(
|
|
||||||
"Cannot use logical \"or\" operation on {} and {}",
|
|
||||||
left, right
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ValueError::IndexOutOfBounds { value, index } => {
|
|
||||||
format!("Index out of bounds: {} with index {}", value, index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ValueError {
|
impl Display for ValueError {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parse, Chunk, ChunkError, DustError, Identifier, Instruction, Span, Value, ValueError,
|
dust_error::AnnotatedError, parse, Chunk, ChunkError, DustError, Identifier, Instruction, Span,
|
||||||
|
Value, ValueError, ValueLocation,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||||
@ -13,7 +14,7 @@ pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
|||||||
.map_err(|error| DustError::Runtime { error, source })
|
.map_err(|error| DustError::Runtime { error, source })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct Vm {
|
pub struct Vm {
|
||||||
chunk: Rc<Chunk>,
|
chunk: Rc<Chunk>,
|
||||||
ip: usize,
|
ip: usize,
|
||||||
@ -44,12 +45,19 @@ impl Vm {
|
|||||||
|
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::Constant => {
|
Instruction::Constant => {
|
||||||
let (index, _) = self.read(position).copied()?;
|
let (argument, _) = self.read(position).copied()?;
|
||||||
|
|
||||||
self.push_constant_value(index, position)?;
|
self.push_constant_value(argument, position)?;
|
||||||
}
|
}
|
||||||
Instruction::Return => {
|
Instruction::Return => {
|
||||||
let value = self.pop(position)?.resolve(&self.chunk, position)?.clone();
|
let stacked = self.pop(position)?;
|
||||||
|
let value = match stacked {
|
||||||
|
StackedValue::Runtime(value) => value,
|
||||||
|
StackedValue::Constant(index) => Rc::get_mut(&mut self.chunk)
|
||||||
|
.unwrap()
|
||||||
|
.remove_constant(index)
|
||||||
|
.map_err(|error| VmError::Chunk { error, position })?,
|
||||||
|
};
|
||||||
|
|
||||||
return Ok(Some(value));
|
return Ok(Some(value));
|
||||||
}
|
}
|
||||||
@ -58,32 +66,55 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
Instruction::DefineVariable => {
|
Instruction::DefineVariableRuntime => {
|
||||||
let (index, _) = *self.read(position)?;
|
let value = self.pop(position)?.resolve(&self.chunk, position)?.clone();
|
||||||
|
|
||||||
self.stack
|
self.push_runtime_value(value, position)?;
|
||||||
.insert(index as usize, StackedValue::Constant(index));
|
}
|
||||||
|
Instruction::DefineVariableConstant => {
|
||||||
|
let (argument, _) = *self.read(position)?;
|
||||||
|
|
||||||
|
self.push_constant_value(argument, position)?;
|
||||||
}
|
}
|
||||||
Instruction::GetVariable => {
|
Instruction::GetVariable => {
|
||||||
let (index, _) = *self.read(position)?;
|
let (argument, _) = *self.read(position)?;
|
||||||
|
|
||||||
self.push_constant_value(index, position)?;
|
let local = self
|
||||||
}
|
|
||||||
Instruction::SetVariable => {
|
|
||||||
let (index, _) = *self.read(position)?;
|
|
||||||
let identifier = self
|
|
||||||
.chunk
|
.chunk
|
||||||
.get_identifier(index)
|
.get_local(argument)
|
||||||
|
.map_err(|error| VmError::Chunk { error, position })?;
|
||||||
|
|
||||||
|
match local.value_location {
|
||||||
|
ValueLocation::ConstantStack => {
|
||||||
|
let value = self
|
||||||
|
.chunk
|
||||||
|
.get_constant(argument)
|
||||||
.map_err(|error| VmError::Chunk { error, position })?
|
.map_err(|error| VmError::Chunk { error, position })?
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
if !self.chunk.contains_identifier(&identifier) {
|
self.push_runtime_value(value, position)?;
|
||||||
return Err(VmError::UndefinedVariable(identifier, position));
|
}
|
||||||
|
ValueLocation::RuntimeStack => {
|
||||||
|
let value = self.pop(position)?.resolve(&self.chunk, position)?.clone();
|
||||||
|
|
||||||
|
self.push_runtime_value(value, position)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Instruction::SetVariable => {
|
||||||
|
let (argument, _) = *self.read(position)?;
|
||||||
|
let identifier = self
|
||||||
|
.chunk
|
||||||
|
.get_identifier(argument)
|
||||||
|
.map_err(|error| VmError::Chunk { error, position })?;
|
||||||
|
|
||||||
|
if !self.chunk.contains_identifier(identifier) {
|
||||||
|
return Err(VmError::UndefinedVariable(identifier.clone(), position));
|
||||||
}
|
}
|
||||||
|
|
||||||
let stacked = self.pop(position)?;
|
let stacked = self.pop(position)?;
|
||||||
|
|
||||||
self.stack[index as usize] = stacked;
|
self.stack[argument as usize] = stacked;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unary
|
// Unary
|
||||||
@ -261,6 +292,12 @@ impl Vm {
|
|||||||
if self.stack.len() == Self::STACK_SIZE {
|
if self.stack.len() == Self::STACK_SIZE {
|
||||||
Err(VmError::StackOverflow(position))
|
Err(VmError::StackOverflow(position))
|
||||||
} else {
|
} else {
|
||||||
|
let value = if value.is_raw() {
|
||||||
|
value.into_reference()
|
||||||
|
} else {
|
||||||
|
value
|
||||||
|
};
|
||||||
|
|
||||||
self.stack.push(StackedValue::Runtime(value));
|
self.stack.push(StackedValue::Runtime(value));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -334,28 +371,41 @@ impl VmError {
|
|||||||
pub fn value(error: ValueError, position: Span) -> Self {
|
pub fn value(error: ValueError, position: Span) -> Self {
|
||||||
Self::Value { error, position }
|
Self::Value { error, position }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn title() -> &'static str {
|
impl AnnotatedError for VmError {
|
||||||
|
fn title() -> &'static str {
|
||||||
"Runtime Error"
|
"Runtime Error"
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn description(&self) -> String {
|
fn description(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::InvalidInstruction(byte, _) => {
|
Self::InvalidInstruction(_, _) => "Invalid instruction",
|
||||||
format!("The byte {byte} does not correspond to a valid instruction")
|
Self::StackOverflow(_) => "Stack overflow",
|
||||||
|
Self::StackUnderflow(_) => "Stack underflow",
|
||||||
|
Self::UndefinedVariable(_, _) => "Undefined variable",
|
||||||
|
Self::Chunk { .. } => "Chunk error",
|
||||||
|
Self::Value { .. } => "Value error",
|
||||||
}
|
}
|
||||||
Self::StackOverflow(position) => format!("Stack overflow at {position}"),
|
}
|
||||||
Self::StackUnderflow(position) => format!("Stack underflow at {position}"),
|
|
||||||
|
fn details(&self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
Self::InvalidInstruction(byte, _) => Some(format!(
|
||||||
|
"The byte {byte} does not correspond to a valid instruction"
|
||||||
|
)),
|
||||||
|
Self::StackOverflow(position) => Some(format!("Stack overflow at {position}")),
|
||||||
|
Self::StackUnderflow(position) => Some(format!("Stack underflow at {position}")),
|
||||||
Self::UndefinedVariable(identifier, position) => {
|
Self::UndefinedVariable(identifier, position) => {
|
||||||
format!("{identifier} is not in scope at {position}")
|
Some(format!("{identifier} is not in scope at {position}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::Chunk { error, .. } => error.description(),
|
Self::Chunk { error, .. } => Some(error.to_string()),
|
||||||
Self::Value { error, .. } => error.description(),
|
Self::Value { error, .. } => Some(error.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn position(&self) -> Span {
|
fn position(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Self::InvalidInstruction(_, position) => *position,
|
Self::InvalidInstruction(_, position) => *position,
|
||||||
Self::StackUnderflow(position) => *position,
|
Self::StackUnderflow(position) => *position,
|
||||||
|
@ -2,6 +2,7 @@ use std::fs::read_to_string;
|
|||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use dust_lang::{parse, run};
|
use dust_lang::{parse, run};
|
||||||
|
use env_logger::WriteStyle;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
@ -15,7 +16,11 @@ struct Cli {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::init();
|
env_logger::builder()
|
||||||
|
.parse_env("DUST_LOG")
|
||||||
|
.format_timestamp_secs()
|
||||||
|
.write_style(WriteStyle::Always)
|
||||||
|
.init();
|
||||||
|
|
||||||
let args = Cli::parse();
|
let args = Cli::parse();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user