From fcfcb4a429ec57ed4eb5947589eea76006970527 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 9 Sep 2024 23:24:22 -0400 Subject: [PATCH] Restructure, clean up and add tests --- dust-lang/src/chunk.rs | 26 ++---- dust-lang/src/instruction.rs | 143 +++++++++++++++++++++++++++++++++ dust-lang/src/lib.rs | 6 +- dust-lang/src/vm.rs | 151 +++-------------------------------- dust-lang/tests/math.rs | 9 +++ dust-lang/tests/values.rs | 49 ++++++++++++ dust-lang/tests/variables.rs | 17 ++++ dust-shell/src/main.rs | 2 +- 8 files changed, 239 insertions(+), 164 deletions(-) create mode 100644 dust-lang/src/instruction.rs create mode 100644 dust-lang/tests/math.rs create mode 100644 dust-lang/tests/values.rs create mode 100644 dust-lang/tests/variables.rs diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 40b27cb..63f31d0 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -108,33 +108,21 @@ impl Chunk { output.push_str("== "); output.push_str(name); - output.push_str(" ==\n--code--\n"); + output.push_str(" ==\n--Code--\n"); output.push_str("OFFSET INSTRUCTION POSITION\n"); let mut previous = None; for (offset, (byte, position)) in self.code.iter().enumerate() { - if let Some(Instruction::Constant) = previous { - let display = format!("{offset:04} CONSTANT_INDEX {byte}"); - let display_with_postion = format!("{display:27} {position}\n"); - previous = None; - - output.push_str(&display_with_postion); - - continue; - } - if let Some( - Instruction::DefineVariable | Instruction::GetVariable | Instruction::SetVariable, + Instruction::Constant + | Instruction::DefineVariable + | Instruction::GetVariable + | Instruction::SetVariable, ) = previous { - let display = format!("{offset:04} IDENTIFIER_INDEX {byte}"); - let display_with_postion = format!("{display:27} {position}\n"); - previous = None; - output.push_str(&display_with_postion); - continue; } @@ -147,7 +135,7 @@ impl Chunk { output.push_str(&display_with_postion); } - output.push_str("--constants--\n"); + output.push_str("--Constants--\n"); output.push_str("INDEX KIND VALUE\n"); for (index, value) in self.constants.iter().enumerate() { @@ -161,7 +149,7 @@ impl Chunk { output.push_str(&display); } - output.push_str("--identifiers--\n"); + output.push_str("--Identifiers--\n"); output.push_str("INDEX IDENTIFIER DEPTH\n"); for (index, Local { identifier, depth }) in self.identifiers.iter().enumerate() { diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs new file mode 100644 index 0000000..118a858 --- /dev/null +++ b/dust-lang/src/instruction.rs @@ -0,0 +1,143 @@ +use std::fmt::{self, Display, Formatter}; + +use serde::{Deserialize, Serialize}; + +use crate::Chunk; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)] +pub enum Instruction { + Constant = 0, + Return = 1, + Pop = 2, + + // Variables + DefineVariable = 3, + GetVariable = 4, + SetVariable = 5, + + // Unary + Negate = 6, + Not = 7, + + // Binary + Add = 8, + Subtract = 9, + Multiply = 10, + Divide = 11, + Greater = 12, + Less = 13, + GreaterEqual = 14, + LessEqual = 15, + Equal = 16, + NotEqual = 17, + And = 18, + Or = 19, +} + +impl Instruction { + pub fn from_byte(byte: u8) -> Option { + match byte { + 0 => Some(Instruction::Constant), + 1 => Some(Instruction::Return), + 2 => Some(Instruction::Pop), + 3 => Some(Instruction::DefineVariable), + 4 => Some(Instruction::GetVariable), + 5 => Some(Instruction::SetVariable), + 6 => Some(Instruction::Negate), + 7 => Some(Instruction::Not), + 8 => Some(Instruction::Add), + 9 => Some(Instruction::Subtract), + 10 => Some(Instruction::Multiply), + 11 => Some(Instruction::Divide), + 12 => Some(Instruction::Greater), + 13 => Some(Instruction::Less), + 14 => Some(Instruction::GreaterEqual), + 15 => Some(Instruction::LessEqual), + 16 => Some(Instruction::Equal), + 17 => Some(Instruction::NotEqual), + 18 => Some(Instruction::And), + 19 => Some(Instruction::Or), + _ => None, + } + } + + pub fn disassemble(&self, chunk: &Chunk, offset: usize) -> String { + match self { + Instruction::Constant => { + let (index_display, value_display) = + if let Ok((index, _)) = chunk.get_code(offset + 1) { + let index_string = index.to_string(); + let value_string = chunk + .get_constant(*index) + .map(|value| value.to_string()) + .unwrap_or_else(|error| format!("{:?}", error)); + + (index_string, value_string) + } 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::Pop => format!("{offset:04} POP"), + + // Variables + Instruction::DefineVariable => { + let (index, _) = chunk.get_code(offset + 1).unwrap(); + let identifier_display = match chunk.get_identifier(*index) { + Ok(identifier) => identifier.to_string(), + Err(error) => format!("{:?}", error), + }; + + format!("DEFINE_VARIABLE {identifier_display} {index}") + } + Instruction::GetVariable => { + let (index, _) = chunk.get_code(offset + 1).unwrap(); + let identifier_display = match chunk.get_identifier(*index) { + Ok(identifier) => identifier.to_string(), + Err(error) => format!("{:?}", error), + }; + + format!("GET_VARIABLE {identifier_display} {index}") + } + + Instruction::SetVariable => { + let (index, _) = chunk.get_code(offset + 1).unwrap(); + let identifier_display = match chunk.get_identifier(*index) { + Ok(identifier) => identifier.to_string(), + Err(error) => format!("{:?}", error), + }; + + format!("SET_VARIABLE {identifier_display} {index}") + } + + // Unary + Instruction::Negate => "NEGATE".to_string(), + Instruction::Not => "NOT".to_string(), + + // Binary + Instruction::Add => "ADD".to_string(), + Instruction::Subtract => "SUBTRACT".to_string(), + Instruction::Multiply => "MULTIPLY".to_string(), + Instruction::Divide => "DIVIDE".to_string(), + Instruction::Greater => "GREATER".to_string(), + Instruction::Less => "LESS".to_string(), + Instruction::GreaterEqual => "GREATER_EQUAL".to_string(), + Instruction::LessEqual => "LESS_EQUAL".to_string(), + Instruction::Equal => "EQUAL".to_string(), + Instruction::NotEqual => "NOT_EQUAL".to_string(), + Instruction::And => "AND".to_string(), + Instruction::Or => "OR".to_string(), + } + } +} + +impl Display for Instruction { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{self:?}") + } +} diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 63bac84..d36f5ab 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -20,9 +20,9 @@ pub mod constructor; pub mod dust_error; pub mod identifier; pub mod identifier_stack; +pub mod instruction; pub mod lexer; pub mod parser; -pub mod run; pub mod token; pub mod r#type; pub mod value; @@ -33,13 +33,13 @@ pub use constructor::{ConstructError, Constructor}; pub use dust_error::DustError; pub use identifier::Identifier; pub use identifier_stack::IdentifierStack; +pub use instruction::Instruction; pub use lexer::{lex, LexError, Lexer}; pub use parser::{parse, ParseError, Parser}; pub use r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict}; -pub use run::run; pub use token::{Token, TokenKind, TokenOwned}; pub use value::{Struct, Value, ValueError}; -pub use vm::{Instruction, Vm}; +pub use vm::{run, Vm}; use std::fmt::{self, Display, Formatter}; diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 8ae8063..188ab71 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -1,8 +1,15 @@ -use std::fmt::{self, Display, Formatter}; +use crate::{ + parse, Chunk, ChunkError, DustError, Identifier, Instruction, Span, Value, ValueError, +}; -use serde::{Deserialize, Serialize}; +pub fn run(source: &str) -> Result, DustError> { + let chunk = parse(source)?; -use crate::{Chunk, ChunkError, Identifier, Span, Value, ValueError}; + let mut vm = Vm::new(chunk); + + vm.run() + .map_err(|error| DustError::Runtime { error, source }) +} #[derive(Debug, Clone, Eq, PartialEq)] pub struct Vm { @@ -229,144 +236,6 @@ impl From for VmError { } } -#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)] -pub enum Instruction { - Constant = 0, - Return = 1, - Pop = 2, - - // Variables - DefineVariable = 3, - GetVariable = 4, - SetVariable = 5, - - // Unary - Negate = 6, - Not = 7, - - // Binary - Add = 8, - Subtract = 9, - Multiply = 10, - Divide = 11, - Greater = 12, - Less = 13, - GreaterEqual = 14, - LessEqual = 15, - Equal = 16, - NotEqual = 17, - And = 18, - Or = 19, -} - -impl Instruction { - pub fn from_byte(byte: u8) -> Option { - match byte { - 0 => Some(Instruction::Constant), - 1 => Some(Instruction::Return), - 2 => Some(Instruction::Pop), - 3 => Some(Instruction::DefineVariable), - 4 => Some(Instruction::GetVariable), - 5 => Some(Instruction::SetVariable), - 6 => Some(Instruction::Negate), - 7 => Some(Instruction::Not), - 8 => Some(Instruction::Add), - 9 => Some(Instruction::Subtract), - 10 => Some(Instruction::Multiply), - 11 => Some(Instruction::Divide), - 12 => Some(Instruction::Greater), - 13 => Some(Instruction::Less), - 14 => Some(Instruction::GreaterEqual), - 15 => Some(Instruction::LessEqual), - 16 => Some(Instruction::Equal), - 17 => Some(Instruction::NotEqual), - 18 => Some(Instruction::And), - 19 => Some(Instruction::Or), - _ => None, - } - } - - pub fn disassemble(&self, chunk: &Chunk, offset: usize) -> String { - match self { - Instruction::Constant => { - let (index_display, value_display) = - if let Ok((index, _)) = chunk.get_code(offset + 1) { - let index_string = index.to_string(); - let value_string = chunk - .get_constant(*index) - .map(|value| value.to_string()) - .unwrap_or_else(|error| format!("{:?}", error)); - - (index_string, value_string) - } 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::Pop => format!("{offset:04} POP"), - - // Variables - Instruction::DefineVariable => { - let (index, _) = chunk.get_code(offset + 1).unwrap(); - let identifier_display = match chunk.get_identifier(*index) { - Ok(identifier) => identifier.to_string(), - Err(error) => format!("{:?}", error), - }; - - format!("DEFINE_VARIABLE {identifier_display} {index}") - } - Instruction::GetVariable => { - let (index, _) = chunk.get_code(offset + 1).unwrap(); - let identifier_display = match chunk.get_identifier(*index) { - Ok(identifier) => identifier.to_string(), - Err(error) => format!("{:?}", error), - }; - - format!("GET_VARIABLE {identifier_display} {index}") - } - - Instruction::SetVariable => { - let (index, _) = chunk.get_code(offset + 1).unwrap(); - let identifier_display = match chunk.get_identifier(*index) { - Ok(identifier) => identifier.to_string(), - Err(error) => format!("{:?}", error), - }; - - format!("SET_VARIABLE {identifier_display} {index}") - } - - // Unary - Instruction::Negate => "NEGATE".to_string(), - Instruction::Not => "NOT".to_string(), - - // Binary - Instruction::Add => "ADD".to_string(), - Instruction::Subtract => "SUBTRACT".to_string(), - Instruction::Multiply => "MULTIPLY".to_string(), - Instruction::Divide => "DIVIDE".to_string(), - Instruction::Greater => "GREATER".to_string(), - Instruction::Less => "LESS".to_string(), - Instruction::GreaterEqual => "GREATER_EQUAL".to_string(), - Instruction::LessEqual => "LESS_EQUAL".to_string(), - Instruction::Equal => "EQUAL".to_string(), - Instruction::NotEqual => "NOT_EQUAL".to_string(), - Instruction::And => "AND".to_string(), - Instruction::Or => "OR".to_string(), - } - } -} - -impl Display for Instruction { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{self:?}") - } -} - #[cfg(test)] pub mod tests { use super::*; diff --git a/dust-lang/tests/math.rs b/dust-lang/tests/math.rs new file mode 100644 index 0000000..5b559be --- /dev/null +++ b/dust-lang/tests/math.rs @@ -0,0 +1,9 @@ +use dust_lang::*; + +#[test] +fn addition() { + let source = "21 + 21"; + let result = run(source); + + assert_eq!(result, Ok(Some(Value::integer(42)))); +} diff --git a/dust-lang/tests/values.rs b/dust-lang/tests/values.rs new file mode 100644 index 0000000..556e2e4 --- /dev/null +++ b/dust-lang/tests/values.rs @@ -0,0 +1,49 @@ +use dust_lang::*; + +#[test] +fn integer() { + let source = "42"; + let result = run(source); + + assert_eq!(result, Ok(Some(Value::integer(42)))); +} + +#[test] +fn float() { + let source = "42.0"; + let result = run(source); + + assert_eq!(result, Ok(Some(Value::float(42.0)))); +} + +#[test] +fn string() { + let source = "\"Hello, World!\""; + let result = run(source); + + assert_eq!(result, Ok(Some(Value::string("Hello, World!")))); +} + +#[test] +fn boolean() { + let source = "true"; + let result = run(source); + + assert_eq!(result, Ok(Some(Value::boolean(true)))); +} + +#[test] +fn byte() { + let source = "0x42"; + let result = run(source); + + assert_eq!(result, Ok(Some(Value::byte(0x42)))); +} + +#[test] +fn character() { + let source = "'a'"; + let result = run(source); + + assert_eq!(result, Ok(Some(Value::character('a')))); +} diff --git a/dust-lang/tests/variables.rs b/dust-lang/tests/variables.rs new file mode 100644 index 0000000..af6ad04 --- /dev/null +++ b/dust-lang/tests/variables.rs @@ -0,0 +1,17 @@ +use dust_lang::*; + +#[test] +fn add_variables() { + let source = "let foo = 21; let bar = 21; foo + bar"; + let result = run(source); + + assert_eq!(result, Ok(Some(Value::integer(42)))); +} + +#[test] +fn variable() { + let source = "let foo = 42; foo"; + let result = run(source); + + assert_eq!(result, Ok(Some(Value::integer(42)))); +} diff --git a/dust-shell/src/main.rs b/dust-shell/src/main.rs index 1090834..ffea2ad 100644 --- a/dust-shell/src/main.rs +++ b/dust-shell/src/main.rs @@ -38,7 +38,7 @@ fn main() { fn parse_and_display_errors(source: &str) { match parse(source) { - Ok(chunk) => println!("{chunk}"), + Ok(chunk) => println!("{}", chunk.disassemble("Dust CLI Input")), Err(error) => { eprintln!("{:?}", error); }