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("== ");
|
||||||
output.push_str(name);
|
output.push_str(name);
|
||||||
output.push_str(" ==\n--code--\n");
|
output.push_str(" ==\n--Code--\n");
|
||||||
output.push_str("OFFSET INSTRUCTION POSITION\n");
|
output.push_str("OFFSET INSTRUCTION POSITION\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(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(
|
if let Some(
|
||||||
Instruction::DefineVariable | Instruction::GetVariable | Instruction::SetVariable,
|
Instruction::Constant
|
||||||
|
| Instruction::DefineVariable
|
||||||
|
| Instruction::GetVariable
|
||||||
|
| Instruction::SetVariable,
|
||||||
) = previous
|
) = previous
|
||||||
{
|
{
|
||||||
let display = format!("{offset:04} IDENTIFIER_INDEX {byte}");
|
|
||||||
let display_with_postion = format!("{display:27} {position}\n");
|
|
||||||
|
|
||||||
previous = None;
|
previous = None;
|
||||||
|
|
||||||
output.push_str(&display_with_postion);
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +135,7 @@ impl Chunk {
|
|||||||
output.push_str(&display_with_postion);
|
output.push_str(&display_with_postion);
|
||||||
}
|
}
|
||||||
|
|
||||||
output.push_str("--constants--\n");
|
output.push_str("--Constants--\n");
|
||||||
output.push_str("INDEX KIND VALUE\n");
|
output.push_str("INDEX KIND VALUE\n");
|
||||||
|
|
||||||
for (index, value) in self.constants.iter().enumerate() {
|
for (index, value) in self.constants.iter().enumerate() {
|
||||||
@ -161,7 +149,7 @@ impl Chunk {
|
|||||||
output.push_str(&display);
|
output.push_str(&display);
|
||||||
}
|
}
|
||||||
|
|
||||||
output.push_str("--identifiers--\n");
|
output.push_str("--Identifiers--\n");
|
||||||
output.push_str("INDEX IDENTIFIER DEPTH\n");
|
output.push_str("INDEX IDENTIFIER DEPTH\n");
|
||||||
|
|
||||||
for (index, Local { identifier, depth }) in self.identifiers.iter().enumerate() {
|
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 dust_error;
|
||||||
pub mod identifier;
|
pub mod identifier;
|
||||||
pub mod identifier_stack;
|
pub mod identifier_stack;
|
||||||
|
pub mod instruction;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod run;
|
|
||||||
pub mod token;
|
pub mod token;
|
||||||
pub mod r#type;
|
pub mod r#type;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
@ -33,13 +33,13 @@ pub use constructor::{ConstructError, Constructor};
|
|||||||
pub use dust_error::DustError;
|
pub use dust_error::DustError;
|
||||||
pub use identifier::Identifier;
|
pub use identifier::Identifier;
|
||||||
pub use identifier_stack::IdentifierStack;
|
pub use identifier_stack::IdentifierStack;
|
||||||
|
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};
|
||||||
pub use r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict};
|
pub use r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict};
|
||||||
pub use run::run;
|
|
||||||
pub use token::{Token, TokenKind, TokenOwned};
|
pub use token::{Token, TokenKind, TokenOwned};
|
||||||
pub use value::{Struct, Value, ValueError};
|
pub use value::{Struct, Value, ValueError};
|
||||||
pub use vm::{Instruction, Vm};
|
pub use vm::{run, Vm};
|
||||||
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
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)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct Vm {
|
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)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
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) {
|
fn parse_and_display_errors(source: &str) {
|
||||||
match parse(source) {
|
match parse(source) {
|
||||||
Ok(chunk) => println!("{chunk}"),
|
Ok(chunk) => println!("{}", chunk.disassemble("Dust CLI Input")),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
eprintln!("{:?}", error);
|
eprintln!("{:?}", error);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user