Restructure, clean up and add tests
This commit is contained in:
parent
85f5f44946
commit
fcfcb4a429
@ -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() {
|
||||
|
143
dust-lang/src/instruction.rs
Normal file
143
dust-lang/src/instruction.rs
Normal 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:?}")
|
||||
}
|
||||
}
|
@ -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};
|
||||
|
||||
|
@ -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
9
dust-lang/tests/math.rs
Normal 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
49
dust-lang/tests/values.rs
Normal 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'))));
|
||||
}
|
17
dust-lang/tests/variables.rs
Normal file
17
dust-lang/tests/variables.rs
Normal 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))));
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user