diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs
index 4ee9c3c..cf0fd59 100644
--- a/dust-lang/src/chunk.rs
+++ b/dust-lang/src/chunk.rs
@@ -181,21 +181,20 @@ impl Chunk {
let name_length = name.len();
let buffer_length = 34_usize.saturating_sub(name_length + 2);
let name_buffer = " ".repeat(buffer_length / 2);
- let name_line = format!("\n{name_buffer}{name}{name_buffer}\n");
+ let name_line = format!("{name_buffer}{name}{name_buffer}\n");
let name_underline = format!("{name_buffer}{}{name_buffer}\n", "-".repeat(name_length));
output.push_str(&name_line);
output.push_str(&name_underline);
output.push_str("\n Code \n");
- output.push_str("------ ------------ ------------\n");
- output.push_str("OFFSET POSITION INSTRUCTION\n");
- output.push_str("------ ------------ ------------\n");
+ output.push_str("------ -------- ------------\n");
+ output.push_str("OFFSET POSITION INSTRUCTION\n");
+ output.push_str("------ -------- ------------\n");
for (offset, (instruction, position)) in self.code.iter().enumerate() {
let display = format!(
- "{offset:4} {:12} {}\n",
- position.to_string(),
- instruction.disassemble(self, offset)
+ "{offset:04} {position} {}\n",
+ instruction.disassemble(self)
);
output.push_str(&display);
diff --git a/dust-lang/src/dust_error.rs b/dust-lang/src/dust_error.rs
index a83200f..2fe243e 100644
--- a/dust-lang/src/dust_error.rs
+++ b/dust-lang/src/dust_error.rs
@@ -1,6 +1,6 @@
use annotate_snippets::{Level, Renderer, Snippet};
-use crate::{LexError, ParseError, Span};
+use crate::{vm::VmError, LexError, ParseError, Span};
#[derive(Debug, PartialEq)]
pub enum DustError<'src> {
@@ -12,6 +12,10 @@ pub enum DustError<'src> {
error: ParseError,
source: &'src str,
},
+ Runtime {
+ error: VmError,
+ source: &'src str,
+ },
}
impl<'src> DustError<'src> {
@@ -20,6 +24,20 @@ impl<'src> DustError<'src> {
let renderer = Renderer::styled();
match self {
+ DustError::Runtime { error, source } => {
+ let position = error.position();
+ let label = format!("Runtime error: {}", error.description());
+ let details = error
+ .details()
+ .unwrap_or_else(|| "While running this code".to_string());
+ let message = Level::Error.title(&label).snippet(
+ Snippet::source(source)
+ .fold(true)
+ .annotation(Level::Error.span(position.0..position.1).label(&details)),
+ );
+
+ report.push_str(&renderer.render(message).to_string());
+ }
DustError::Parse { error, source } => {
let position = error.position();
let label = format!("Parse error: {}", error.description());
diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs
index 684a3ae..d7fae59 100644
--- a/dust-lang/src/instruction.rs
+++ b/dust-lang/src/instruction.rs
@@ -1,16 +1,18 @@
+use std::fmt::{self, Display, Formatter};
+
use crate::{Chunk, Span};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Instruction {
- opcode: OpCode,
- to_register: u8,
- arguments: [u8; 2],
+ pub operation: Operation,
+ pub to_register: u8,
+ pub arguments: [u8; 2],
}
impl Instruction {
pub fn r#move(to_register: u8, from_register: u8) -> Instruction {
Instruction {
- opcode: OpCode::Move,
+ operation: Operation::Move,
to_register,
arguments: [from_register, 0],
}
@@ -18,7 +20,7 @@ impl Instruction {
pub fn close(to_register: u8) -> Instruction {
Instruction {
- opcode: OpCode::Close,
+ operation: Operation::Close,
to_register,
arguments: [0, 0],
}
@@ -26,7 +28,7 @@ impl Instruction {
pub fn load_constant(to_register: u8, constant_index: u16) -> Instruction {
Instruction {
- opcode: OpCode::LoadConstant,
+ operation: Operation::LoadConstant,
to_register,
arguments: constant_index.to_le_bytes(),
}
@@ -34,7 +36,7 @@ impl Instruction {
pub fn declare_variable(to_register: u8, variable_index: u16) -> Instruction {
Instruction {
- opcode: OpCode::DeclareVariable,
+ operation: Operation::DeclareVariable,
to_register,
arguments: variable_index.to_le_bytes(),
}
@@ -42,7 +44,7 @@ impl Instruction {
pub fn get_variable(to_register: u8, variable_index: u16) -> Instruction {
Instruction {
- opcode: OpCode::GetVariable,
+ operation: Operation::GetVariable,
to_register,
arguments: variable_index.to_le_bytes(),
}
@@ -50,7 +52,7 @@ impl Instruction {
pub fn set_variable(from_register: u8, variable_index: u16) -> Instruction {
Instruction {
- opcode: OpCode::SetVariable,
+ operation: Operation::SetVariable,
to_register: from_register,
arguments: variable_index.to_le_bytes(),
}
@@ -58,7 +60,7 @@ impl Instruction {
pub fn add(to_register: u8, left_register: u8, right_register: u8) -> Instruction {
Instruction {
- opcode: OpCode::Add,
+ operation: Operation::Add,
to_register,
arguments: [left_register, right_register],
}
@@ -66,7 +68,7 @@ impl Instruction {
pub fn subtract(to_register: u8, left_register: u8, right_register: u8) -> Instruction {
Instruction {
- opcode: OpCode::Subtract,
+ operation: Operation::Subtract,
to_register,
arguments: [left_register, right_register],
}
@@ -74,7 +76,7 @@ impl Instruction {
pub fn multiply(to_register: u8, left_register: u8, right_register: u8) -> Instruction {
Instruction {
- opcode: OpCode::Multiply,
+ operation: Operation::Multiply,
to_register,
arguments: [left_register, right_register],
}
@@ -82,7 +84,7 @@ impl Instruction {
pub fn divide(to_register: u8, left_register: u8, right_register: u8) -> Instruction {
Instruction {
- opcode: OpCode::Divide,
+ operation: Operation::Divide,
to_register,
arguments: [left_register, right_register],
}
@@ -90,7 +92,7 @@ impl Instruction {
pub fn negate(to_register: u8, from_register: u8) -> Instruction {
Instruction {
- opcode: OpCode::Negate,
+ operation: Operation::Negate,
to_register,
arguments: [from_register, 0],
}
@@ -98,84 +100,97 @@ impl Instruction {
pub fn r#return() -> Instruction {
Instruction {
- opcode: OpCode::Return,
+ operation: Operation::Return,
to_register: 0,
arguments: [0, 0],
}
}
- pub fn disassemble(&self, chunk: &Chunk, offset: usize) -> String {
- match self.opcode {
- OpCode::Move => format!(
- "{:04} MOVE R{} R{}",
- offset, self.to_register, self.arguments[0]
- ),
- OpCode::Close => {
- format!("{:04} CLOSE R{}", offset, self.to_register)
- }
- OpCode::LoadConstant => {
+ pub fn disassemble(&self, chunk: &Chunk) -> String {
+ match self.operation {
+ Operation::LoadConstant => {
let constant_index = u16::from_le_bytes(self.arguments);
let constant_display = match chunk.get_constant(constant_index, Span(0, 0)) {
Ok(value) => value.to_string(),
Err(error) => format!("{:?}", error),
};
- format!(
- "{:04} LOAD_CONSTANT R{} C{} {}",
- offset, self.to_register, constant_index, constant_display
- )
+ format!("{self} {constant_display}")
}
- OpCode::DeclareVariable => {
+ _ => format!("{self}"),
+ }
+ }
+}
+
+impl Display for Instruction {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match self.operation {
+ Operation::Move => write!(f, "MOVE R{} R{}", self.to_register, self.arguments[0]),
+ Operation::Close => write!(f, "CLOSE R{}", self.to_register),
+ Operation::LoadConstant => {
+ let constant_index = u16::from_le_bytes(self.arguments);
+
+ write!(f, "LOAD_CONSTANT R{} C{}", self.to_register, constant_index)
+ }
+ Operation::DeclareVariable => {
let variable_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]);
- format!(
- "{:04} DECLARE_VARIABLE V{} R{}",
- offset, variable_index, self.to_register
+ write!(
+ f,
+ "DECLARE_VARIABLE V{} R{}",
+ variable_index, self.to_register
)
}
- OpCode::GetVariable => {
+ Operation::GetVariable => {
let variable_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]);
- format!(
- "{:04} GET_VARIABLE R{} V{}",
- offset, self.to_register, variable_index
- )
+ write!(f, "GET_VARIABLE R{} V{}", self.to_register, variable_index)
}
- OpCode::SetVariable => {
+ Operation::SetVariable => {
let variable_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]);
- format!(
- "{:04} SET_VARIABLE V{} R{}",
- offset, variable_index, self.to_register
+ write!(f, "SET_VARIABLE V{} R{}", variable_index, self.to_register)
+ }
+ Operation::Add => {
+ write!(
+ f,
+ "ADD R{} = R{} + R{}",
+ self.to_register, self.arguments[0], self.arguments[1]
)
}
- OpCode::Add => format!(
- "{:04} ADD R{} = R{} + R{}",
- offset, self.to_register, self.arguments[0], self.arguments[1]
- ),
- OpCode::Subtract => format!(
- "{:04} SUBTRACT R{} = R{} - R{}",
- offset, self.to_register, self.arguments[0], self.arguments[1]
- ),
- OpCode::Multiply => format!(
- "{:04} MULTIPLY R{} = R{} * R{}",
- offset, self.to_register, self.arguments[0], self.arguments[1]
- ),
- OpCode::Divide => format!(
- "{:04} DIVIDE R{} = R{} / R{}",
- offset, self.to_register, self.arguments[0], self.arguments[1]
- ),
- OpCode::Negate => format!(
- "{:04} NEGATE R{} = !R{}",
- offset, self.to_register, self.arguments[0]
- ),
- OpCode::Return => format!("{:04} RETURN", offset),
+ Operation::Subtract => {
+ write!(
+ f,
+ "SUBTRACT R{} = R{} - R{}",
+ self.to_register, self.arguments[0], self.arguments[1]
+ )
+ }
+ Operation::Multiply => {
+ write!(
+ f,
+ "MULTIPLY R{} = R{} * R{}",
+ self.to_register, self.arguments[0], self.arguments[1]
+ )
+ }
+ Operation::Divide => {
+ write!(
+ f,
+ "DIVIDE R{} = R{} / R{}",
+ self.to_register, self.arguments[0], self.arguments[1]
+ )
+ }
+ Operation::Negate => {
+ write!(f, "NEGATE R{} = !R{}", self.to_register, self.arguments[0])
+ }
+ Operation::Return => {
+ write!(f, "RETURN")
+ }
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
-enum OpCode {
+pub enum Operation {
// Stack manipulation
Move,
Close,
@@ -201,6 +216,25 @@ enum OpCode {
Return,
}
+impl Display for Operation {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match self {
+ Operation::Move => write!(f, "MOVE"),
+ Operation::Close => write!(f, "CLOSE"),
+ Operation::LoadConstant => write!(f, "LOAD_CONSTANT"),
+ Operation::DeclareVariable => write!(f, "DECLARE_VARIABLE"),
+ Operation::GetVariable => write!(f, "GET_VARIABLE"),
+ Operation::SetVariable => write!(f, "SET_VARIABLE"),
+ Operation::Add => write!(f, "ADD"),
+ Operation::Subtract => write!(f, "SUBTRACT"),
+ Operation::Multiply => write!(f, "MULTIPLY"),
+ Operation::Divide => write!(f, "DIVIDE"),
+ Operation::Negate => write!(f, "NEGATE"),
+ Operation::Return => write!(f, "RETURN"),
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use std::mem::size_of;
diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs
index 14271aa..04668ee 100644
--- a/dust-lang/src/lib.rs
+++ b/dust-lang/src/lib.rs
@@ -8,6 +8,7 @@ mod parser;
mod token;
mod r#type;
mod value;
+mod vm;
use std::fmt::Display;
@@ -15,12 +16,13 @@ pub use chunk::{Chunk, ChunkError};
pub use constructor::Constructor;
pub use dust_error::{AnnotatedError, DustError};
pub use identifier::Identifier;
-pub use instruction::Instruction;
+pub use instruction::{Instruction, Operation};
pub use lexer::{lex, LexError, Lexer};
pub use parser::{parse, ParseError, Parser};
pub use r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict};
pub use token::{Token, TokenKind, TokenOwned};
-pub use value::{Enum, Function, Struct, Value};
+pub use value::{Enum, Function, Struct, Value, ValueError};
+pub use vm::{run, Vm, VmError};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Span(pub usize, pub usize);
diff --git a/dust-lang/src/parser/mod.rs b/dust-lang/src/parser/mod.rs
index b792d4c..4caeb3c 100644
--- a/dust-lang/src/parser/mod.rs
+++ b/dust-lang/src/parser/mod.rs
@@ -214,6 +214,7 @@ impl<'src> Parser<'src> {
}
};
+ self.increment_register()?;
self.parse_expression()?;
self.emit_instruction(byte, operator_position);
@@ -234,8 +235,8 @@ impl<'src> Parser<'src> {
} else {
self.current_register
};
- let left_register = to_register - 1;
- let right_register = to_register - 2;
+ let left_register = to_register - 2;
+ let right_register = to_register - 1;
let byte = match operator {
TokenKind::Plus => Instruction::add(to_register, left_register, right_register),
TokenKind::Minus => Instruction::subtract(to_register, left_register, right_register),
@@ -255,6 +256,7 @@ impl<'src> Parser<'src> {
}
};
+ self.increment_register()?;
self.emit_instruction(byte, operator_position);
Ok(())
@@ -319,19 +321,10 @@ impl<'src> Parser<'src> {
}
self.chunk.end_scope();
-
- while self
- .chunk
- .identifiers()
- .iter()
- .next_back()
- .map_or(false, |local| local.depth > self.chunk.scope_depth())
- {
- self.emit_instruction(
- Instruction::close(self.current_register),
- self.current_position,
- );
- }
+ self.emit_instruction(
+ Instruction::close(self.current_register),
+ self.current_position,
+ );
Ok(())
}
@@ -392,7 +385,7 @@ impl<'src> Parser<'src> {
let identifier_index = self.chunk.declare_variable(identifier, position)?;
self.emit_instruction(
- Instruction::set_variable(self.current_register, identifier_index),
+ Instruction::declare_variable(self.current_register, identifier_index),
position,
);
self.increment_register()?;
@@ -714,7 +707,7 @@ impl AnnotatedError for ParseError {
}
Self::RegisterOverflow { .. } => None,
Self::Chunk(error) => error.details(),
- Self::Lex(error) => Some(error.to_string()),
+ Self::Lex(error) => error.details(),
Self::ParseIntError { error, .. } => Some(error.to_string()),
}
}
diff --git a/dust-lang/src/parser/tests.rs b/dust-lang/src/parser/tests.rs
index eced36a..79ef460 100644
--- a/dust-lang/src/parser/tests.rs
+++ b/dust-lang/src/parser/tests.rs
@@ -23,7 +23,24 @@ fn add() {
vec![
(Instruction::load_constant(0, 0), Span(0, 1)),
(Instruction::load_constant(1, 1), Span(4, 5)),
- (Instruction::add(2, 1, 0), Span(2, 3)),
+ (Instruction::add(2, 0, 1), Span(2, 3)),
+ (Instruction::r#return(), Span(0, 5)),
+ ],
+ vec![Value::integer(1), Value::integer(2),],
+ vec![]
+ ))
+ );
+}
+
+#[test]
+fn subtract() {
+ assert_eq!(
+ parse("1 - 2"),
+ Ok(Chunk::with_data(
+ vec![
+ (Instruction::load_constant(0, 0), Span(0, 1)),
+ (Instruction::load_constant(1, 1), Span(4, 5)),
+ (Instruction::subtract(2, 0, 1), Span(2, 3)),
(Instruction::r#return(), Span(0, 5)),
],
vec![Value::integer(1), Value::integer(2),],
diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs
new file mode 100644
index 0000000..14659a3
--- /dev/null
+++ b/dust-lang/src/vm.rs
@@ -0,0 +1,231 @@
+use crate::{
+ dust_error::AnnotatedError, parse, Chunk, ChunkError, DustError, Identifier, Instruction,
+ Operation, Span, Value, ValueError,
+};
+
+pub fn run(source: &str) -> Result