Restructure, clean up and add tests

This commit is contained in:
Jeff 2024-09-09 23:24:22 -04:00
parent 85f5f44946
commit fcfcb4a429
8 changed files with 239 additions and 164 deletions

View File

@ -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() {

View File

@ -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<Self> {
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:?}")
}
}

View File

@ -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};

View File

@ -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<Option<Value>, 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<ValueError> 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<Self> {
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::*;

9
dust-lang/tests/math.rs Normal file
View File

@ -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))));
}

49
dust-lang/tests/values.rs Normal file
View File

@ -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'))));
}

View File

@ -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))));
}

View File

@ -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);
}