From f2bfe2ed065f2f99471881e8570f38c4006870c4 Mon Sep 17 00:00:00 2001 From: Jeff Date: Sun, 4 Aug 2024 22:15:31 -0400 Subject: [PATCH] Add basic VM --- dust-lang/src/instruction.rs | 23 ++++++++++ dust-lang/src/lib.rs | 8 +++- dust-lang/src/parse.rs | 63 +++++++++++--------------- dust-lang/src/value.rs | 15 +++++++ dust-lang/src/vm.rs | 85 ++++++++++++++++++++++++++++++++++++ 5 files changed, 156 insertions(+), 38 deletions(-) create mode 100644 dust-lang/src/instruction.rs create mode 100644 dust-lang/src/vm.rs diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs new file mode 100644 index 0000000..bb683f6 --- /dev/null +++ b/dust-lang/src/instruction.rs @@ -0,0 +1,23 @@ +use crate::{Identifier, Span, Value}; + +#[derive(Debug, PartialEq, Clone)] +pub struct Instruction { + pub operation: Operation, + pub span: Span, +} + +impl Instruction { + pub fn new(operation: Operation, span: Span) -> Self { + Self { operation, span } + } +} + +#[derive(Debug, PartialEq, Clone)] +pub enum Operation { + Add(Box<(Instruction, Instruction)>), + Assign(Box<(Instruction, Instruction)>), + Constant(Value), + Identifier(Identifier), + List(Vec), + Multiply(Box<(Instruction, Instruction)>), +} diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index da06fc3..06a69a3 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -8,15 +8,21 @@ interpret Dust code. The `interpret` function is a convenience function that cre `Interpreter` and runs the given source code. */ pub mod identifier; +pub mod instruction; pub mod lex; pub mod parse; pub mod token; pub mod r#type; pub mod value; +pub mod vm; pub use identifier::Identifier; +pub use instruction::{Instruction, Operation}; +pub use lex::{lex, LexError, Lexer}; +pub use parse::{parse, ParseError, Parser}; pub use r#type::Type; pub use token::Token; -pub use value::Value; +pub use value::{Value, ValueError}; +pub use vm::Vm; pub type Span = (usize, usize); diff --git a/dust-lang/src/parse.rs b/dust-lang/src/parse.rs index 08b6d13..37ec5a1 100644 --- a/dust-lang/src/parse.rs +++ b/dust-lang/src/parse.rs @@ -1,36 +1,24 @@ use crate::{ - identifier::Identifier, lex::{LexError, Lexer}, - Span, Token, Value, + Instruction, Operation, Span, Token, Value, }; -pub fn parse(input: &str) -> Result { +pub fn parse(input: &str) -> Result, ParseError> { let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); + let mut instructions = Vec::new(); - parser.parse() -} + loop { + let instruction = parser.parse()?; -#[derive(Debug, PartialEq, Clone)] -pub struct Instruction { - pub operation: Operation, - pub span: Span, -} + instructions.push(instruction); -impl Instruction { - pub fn new(operation: Operation, span: Span) -> Self { - Self { operation, span } + if let Token::Eof = parser.current.0 { + break; + } } -} -#[derive(Debug, PartialEq, Clone)] -pub enum Operation { - Add(Box<(Instruction, Instruction)>), - Assign(Box<(Instruction, Instruction)>), - Constant(Value), - Identifier(Identifier), - List(Vec), - Multiply(Box<(Instruction, Instruction)>), + Ok(instructions) } pub struct Parser<'src> { @@ -205,6 +193,7 @@ impl From for ParseError { #[cfg(test)] mod tests { + use crate::Identifier; use super::*; @@ -214,7 +203,7 @@ mod tests { assert_eq!( parse(input), - Ok(Instruction::new( + Ok(vec![Instruction::new( Operation::List(vec![ Instruction::new(Operation::Constant(Value::integer(1)), (1, 2)), Instruction::new( @@ -245,7 +234,7 @@ mod tests { ) ]), (0, 24) - )) + )]) ); } @@ -255,13 +244,13 @@ mod tests { assert_eq!( parse(input), - Ok(Instruction::new( + Ok(vec![Instruction::new( Operation::List(vec![ Instruction::new(Operation::Constant(Value::integer(1)), (1, 2)), Instruction::new(Operation::Constant(Value::integer(2)), (4, 5)), ]), (0, 6) - )) + )]) ); } @@ -271,7 +260,7 @@ mod tests { assert_eq!( parse(input), - Ok(Instruction::new(Operation::List(vec![]), (0, 2))) + Ok(vec![Instruction::new(Operation::List(vec![]), (0, 2))]) ); } @@ -281,10 +270,10 @@ mod tests { assert_eq!( parse(input), - Ok(Instruction::new( + Ok(vec![Instruction::new( Operation::Constant(Value::float(42.0)), (0, 4) - )) + )]) ); } @@ -294,13 +283,13 @@ mod tests { assert_eq!( parse(input), - Ok(Instruction::new( + Ok(vec![Instruction::new( Operation::Add(Box::new(( Instruction::new(Operation::Constant(Value::integer(1)), (0, 1)), Instruction::new(Operation::Constant(Value::integer(2)), (4, 5)), ))), (0, 5) - )) + )]) ); } @@ -310,13 +299,13 @@ mod tests { assert_eq!( parse(input), - Ok(Instruction::new( + Ok(vec![Instruction::new( Operation::Multiply(Box::new(( Instruction::new(Operation::Constant(Value::integer(1)), (0, 1)), Instruction::new(Operation::Constant(Value::integer(2)), (4, 5)), ))), (0, 5) - )) + )]) ); } @@ -326,7 +315,7 @@ mod tests { assert_eq!( parse(input), - Ok(Instruction::new( + Ok(vec![Instruction::new( Operation::Add(Box::new(( Instruction::new(Operation::Constant(Value::integer(1)), (0, 1)), Instruction::new( @@ -338,7 +327,7 @@ mod tests { ), ))), (0, 9) - )) + )]) ); } @@ -348,7 +337,7 @@ mod tests { assert_eq!( parse(input), - Ok(Instruction::new( + Ok(vec![Instruction::new( Operation::Assign(Box::new(( Instruction::new(Operation::Identifier(Identifier::new("a")), (0, 1)), Instruction::new( @@ -372,7 +361,7 @@ mod tests { ), ))), (0, 13) - )) + )]) ); } } diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 645fa86..c6f76f8 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -78,6 +78,16 @@ impl Value { None } } + + pub fn add(&self, other: &Value) -> Result { + match (self.inner().as_ref(), other.inner().as_ref()) { + (ValueInner::Float(left), ValueInner::Float(right)) => Ok(Value::float(left + right)), + (ValueInner::Integer(left), ValueInner::Integer(right)) => { + Ok(Value::integer(left + right)) + } + _ => Err(ValueError::CannotAdd(self.clone(), other.clone())), + } + } } impl Display for Value { @@ -502,3 +512,8 @@ impl Ord for ValueInner { } } } + +#[derive(Clone, Debug, PartialEq)] +pub enum ValueError { + CannotAdd(Value, Value), +} diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs new file mode 100644 index 0000000..b0c9acc --- /dev/null +++ b/dust-lang/src/vm.rs @@ -0,0 +1,85 @@ +use crate::{parse, Instruction, Operation, ParseError, Parser, Value, ValueError}; + +pub fn run(input: &str) -> Result, VmError> { + let instructions = parse(input)?; + let vm = Vm::new(instructions); + + vm.run() +} + +pub struct Vm { + instructions: Vec, +} + +impl Vm { + pub fn new(instructions: Vec) -> Self { + Vm { instructions } + } + + pub fn run(&self) -> Result, VmError> { + let mut previous_value = None; + + for instruction in &self.instructions { + previous_value = self.run_instruction(instruction)?; + } + + Ok(previous_value) + } + + fn run_instruction(&self, instruction: &Instruction) -> Result, VmError> { + match &instruction.operation { + Operation::Add(instructions) => { + let left = if let Some(value) = self.run_instruction(&instructions.0)? { + value + } else { + return Err(VmError::ExpectedValue(instructions.0.operation.clone())); + }; + let right = if let Some(value) = self.run_instruction(&instructions.1)? { + value + } else { + return Err(VmError::ExpectedValue(instructions.1.operation.clone())); + }; + let sum = left.add(&right)?; + + Ok(Some(sum)) + } + Operation::Assign(_) => todo!(), + Operation::Constant(value) => Ok(Some(value.clone())), + Operation::Identifier(_) => todo!(), + Operation::List(_) => todo!(), + Operation::Multiply(_) => todo!(), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum VmError { + ExpectedValue(Operation), + InvalidOperation(Operation), + ParseError(ParseError), + ValueError(ValueError), +} + +impl From for VmError { + fn from(v: ParseError) -> Self { + Self::ParseError(v) + } +} + +impl From for VmError { + fn from(v: ValueError) -> Self { + Self::ValueError(v) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn add() { + let input = "1 + 2"; + + assert_eq!(run(input), Ok(Some(Value::integer(3)))); + } +}