diff --git a/dust-lang/src/abstract_tree.rs b/dust-lang/src/abstract_tree.rs index 9bce093..8658b0b 100644 --- a/dust-lang/src/abstract_tree.rs +++ b/dust-lang/src/abstract_tree.rs @@ -1,6 +1,6 @@ //! In-memory representation of a Dust program. use std::{ - collections::{BTreeMap, HashMap, VecDeque}, + collections::{BTreeMap, VecDeque}, fmt::{self, Display, Formatter}, }; diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index 56b56e3..83b49cc 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -6,14 +6,13 @@ //! - `analyze` convenience function //! - `Analyzer` struct use std::{ - collections::HashMap, error::Error, fmt::{self, Display, Formatter}, }; use crate::{ - abstract_tree::BinaryOperator, AbstractSyntaxTree, BuiltInFunction, Identifier, Node, Span, - Statement, Type, Value, + abstract_tree::BinaryOperator, AbstractSyntaxTree, BuiltInFunction, Context, Node, Span, + Statement, Type, }; /// Analyzes the abstract syntax tree for errors. @@ -31,9 +30,9 @@ use crate::{ /// ``` pub fn analyze( abstract_tree: &AbstractSyntaxTree, - variables: &HashMap, + context: &mut Context, ) -> Result<(), AnalyzerError> { - let analyzer = Analyzer::new(abstract_tree, variables); + let mut analyzer = Analyzer::new(abstract_tree, context); analyzer.analyze() } @@ -53,21 +52,18 @@ pub fn analyze( /// assert!(result.is_err()); pub struct Analyzer<'a> { abstract_tree: &'a AbstractSyntaxTree, - variables: &'a HashMap, + context: &'a mut Context, } impl<'a> Analyzer<'a> { - pub fn new( - abstract_tree: &'a AbstractSyntaxTree, - variables: &'a HashMap, - ) -> Self { + pub fn new(abstract_tree: &'a AbstractSyntaxTree, context: &'a mut Context) -> Self { Self { abstract_tree, - variables, + context, } } - pub fn analyze(&self) -> Result<(), AnalyzerError> { + pub fn analyze(&mut self) -> Result<(), AnalyzerError> { for node in &self.abstract_tree.nodes { self.analyze_node(node)?; } @@ -75,17 +71,27 @@ impl<'a> Analyzer<'a> { Ok(()) } - fn analyze_node(&self, node: &Node) -> Result<(), AnalyzerError> { + fn analyze_node(&mut self, node: &Node) -> Result<(), AnalyzerError> { match &node.inner { Statement::BinaryOperation { left, operator, right, } => { - if let BinaryOperator::AddAssign | BinaryOperator::Assign = operator.inner { - if let Statement::Identifier(_) = left.inner { + if let BinaryOperator::Assign = operator.inner { + if let Statement::Identifier(identifier) = &left.inner { self.analyze_node(right)?; + let right_type = right.inner.expected_type(self.context); + + self.context.set_type( + identifier.clone(), + right_type.ok_or(AnalyzerError::ExpectedValue { + actual: right.as_ref().clone(), + position: right.position, + })?, + ); + return Ok(()); } } @@ -93,8 +99,8 @@ impl<'a> Analyzer<'a> { self.analyze_node(left)?; self.analyze_node(right)?; - let left_type = left.inner.expected_type(self.variables); - let right_type = right.inner.expected_type(self.variables); + let left_type = left.inner.expected_type(self.context); + let right_type = right.inner.expected_type(self.context); if let BinaryOperator::Add | BinaryOperator::Subtract @@ -154,7 +160,7 @@ impl<'a> Analyzer<'a> { } } Statement::Identifier(identifier) => { - if !self.variables.contains_key(identifier) { + if !self.context.contains(identifier) { return Err(AnalyzerError::UndefinedVariable { identifier: node.clone(), }); @@ -184,7 +190,7 @@ impl<'a> Analyzer<'a> { if let Statement::BuiltInFunctionCall { function, .. } = &right.inner { if function == &BuiltInFunction::IsEven || function == &BuiltInFunction::IsOdd { - if let Some(Type::Integer) = left.inner.expected_type(self.variables) { + if let Some(Type::Integer) = left.inner.expected_type(self.context) { } else { return Err(AnalyzerError::ExpectedIntegerOrFloat { actual: left.as_ref().clone(), @@ -348,8 +354,8 @@ mod tests { )] .into(), }; - let variables = HashMap::new(); - let analyzer = Analyzer::new(&abstract_tree, &variables); + let mut context = Context::new(); + let mut analyzer = Analyzer::new(&abstract_tree, &mut context); assert_eq!( analyzer.analyze(), @@ -373,8 +379,8 @@ mod tests { )] .into(), }; - let variables = HashMap::new(); - let analyzer = Analyzer::new(&abstract_tree, &variables); + let mut context = Context::new(); + let mut analyzer = Analyzer::new(&abstract_tree, &mut context); assert_eq!( analyzer.analyze(), @@ -404,8 +410,8 @@ mod tests { )] .into(), }; - let variables = HashMap::new(); - let analyzer = Analyzer::new(&abstract_tree, &variables); + let mut context = Context::new(); + let mut analyzer = Analyzer::new(&abstract_tree, &mut context); assert_eq!( analyzer.analyze(), @@ -434,8 +440,8 @@ mod tests { )] .into(), }; - let variables = HashMap::new(); - let analyzer = Analyzer::new(&abstract_tree, &variables); + let mut context = Context::new(); + let mut analyzer = Analyzer::new(&abstract_tree, &mut context); assert_eq!( analyzer.analyze(), @@ -455,8 +461,8 @@ mod tests { )] .into(), }; - let variables = HashMap::new(); - let analyzer = Analyzer::new(&abstract_tree, &variables); + let mut context = Context::new(); + let mut analyzer = Analyzer::new(&abstract_tree, &mut context); assert_eq!( analyzer.analyze(), diff --git a/dust-lang/src/context.rs b/dust-lang/src/context.rs index ea0f0a1..cd19f3a 100644 --- a/dust-lang/src/context.rs +++ b/dust-lang/src/context.rs @@ -1,7 +1,10 @@ +//! Garbage-collecting context for variables. use std::collections::HashMap; use crate::{Identifier, Type, Value}; +/// Garbage-collecting context for variables. +#[derive(Debug, Clone)] pub struct Context { pub variables: HashMap, } @@ -68,12 +71,13 @@ impl Default for Context { } } +#[derive(Debug, Clone)] pub enum VariableData { Value(Value), Type(Type), } -#[derive(Default)] +#[derive(Default, Debug, Clone)] pub struct UsageData { pub allowed_uses: u16, pub used: u16, diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index b3cbdc8..b16f2f0 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -14,7 +14,7 @@ pub mod r#type; pub mod value; pub mod vm; -pub use abstract_tree::{AbstractSyntaxTree, Node, Statement}; +pub use abstract_tree::{AbstractSyntaxTree, BinaryOperator, Node, Statement}; pub use analyzer::{analyze, Analyzer, AnalyzerError}; pub use built_in_function::{BuiltInFunction, BuiltInFunctionError}; pub use context::{Context, UsageData, VariableData}; diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 3f8679f..b940d81 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -1,7 +1,7 @@ //! Dust value representation use std::{ cmp::Ordering, - collections::{BTreeMap, HashMap}, + collections::BTreeMap, error::Error, fmt::{self, Display, Formatter}, ops::Range, @@ -715,21 +715,21 @@ impl Function { self, _type_arguments: Option>, value_arguments: Option>, - variables: &HashMap, + context: &mut Context, ) -> Result, VmError> { - let mut new_variables = variables.clone(); + let mut new_context = context.clone(); if let (Some(value_parameters), Some(value_arguments)) = (self.value_parameters, value_arguments) { for ((identifier, _), value) in value_parameters.into_iter().zip(value_arguments) { - new_variables.insert(identifier, value); + new_context.set_value(identifier, value); } } let mut vm = Vm::new(self.body); - vm.run(&mut new_variables) + vm.run(&mut new_context) } pub fn return_type(&self, variables: &Context) -> Option { diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index dc4a5b9..65577f5 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -1,27 +1,24 @@ //! Virtual machine for running the abstract syntax tree. use std::{ - collections::{BTreeMap, HashMap}, + collections::BTreeMap, error::Error, fmt::{self, Display, Formatter}, }; use crate::{ abstract_tree::BinaryOperator, parse, AbstractSyntaxTree, Analyzer, AnalyzerError, - BuiltInFunctionError, Identifier, Node, ParseError, Span, Statement, Value, ValueError, + BuiltInFunctionError, Context, Node, ParseError, Span, Statement, Value, ValueError, }; -pub fn run( - input: &str, - variables: &mut HashMap, -) -> Result, VmError> { +pub fn run(input: &str, context: &mut Context) -> Result, VmError> { let abstract_syntax_tree = parse(input)?; - let analyzer = Analyzer::new(&abstract_syntax_tree, variables); + let mut analyzer = Analyzer::new(&abstract_syntax_tree, context); analyzer.analyze()?; let mut vm = Vm::new(abstract_syntax_tree); - vm.run(variables) + vm.run(context) } pub struct Vm { @@ -33,14 +30,11 @@ impl Vm { Self { abstract_tree } } - pub fn run( - &mut self, - variables: &mut HashMap, - ) -> Result, VmError> { + pub fn run(&mut self, context: &mut Context) -> Result, VmError> { let mut previous_value = None; while let Some(node) = self.abstract_tree.nodes.pop_front() { - previous_value = self.run_node(node, variables)?; + previous_value = self.run_node(node, context)?; } Ok(previous_value) @@ -49,7 +43,7 @@ impl Vm { fn run_node( &self, node: Node, - variables: &mut HashMap, + context: &mut Context, ) -> Result, VmError> { match node.inner { Statement::BinaryOperation { @@ -68,7 +62,7 @@ impl Vm { }); }; - let value = if let Some(value) = self.run_node(*right, variables)? { + let value = if let Some(value) = self.run_node(*right, context)? { value } else { return Err(VmError::ExpectedValue { @@ -76,9 +70,9 @@ impl Vm { }); }; - variables.insert(identifier, value.clone()); + context.set_value(identifier, value); - return Ok(Some(value)); + return Ok(None); } if let BinaryOperator::AddAssign = operator.inner { @@ -89,25 +83,21 @@ impl Vm { position: left.position, }); }; - - let right_value = if let Some(value) = self.run_node(*right, variables)? { + let right_value = if let Some(value) = self.run_node(*right, context)? { value } else { return Err(VmError::ExpectedValue { position: right_position, }); }; - - let left_value = - variables - .get(&identifier) - .ok_or_else(|| VmError::UndefinedVariable { - identifier: Node::new( - Statement::Identifier(identifier.clone()), - left.position, - ), - })?; - + let left_value = context.get_value(&identifier).ok_or_else(|| { + VmError::UndefinedVariable { + identifier: Node::new( + Statement::Identifier(identifier.clone()), + left.position, + ), + } + })?; let new_value = left_value.add(&right_value).map_err(|value_error| { VmError::ValueError { error: value_error, @@ -115,21 +105,20 @@ impl Vm { } })?; - variables.insert(identifier, new_value.clone()); + context.set_value(identifier, new_value); - return Ok(Some(new_value)); + return Ok(None); } let left_position = left.position; - let left_value = if let Some(value) = self.run_node(*left, variables)? { + let left_value = if let Some(value) = self.run_node(*left, context)? { value } else { return Err(VmError::ExpectedValue { position: left_position, }); }; - - let right_value = if let Some(value) = self.run_node(*right, variables)? { + let right_value = if let Some(value) = self.run_node(*right, context)? { value } else { return Err(VmError::ExpectedValue { @@ -164,7 +153,7 @@ impl Vm { let mut previous_value = None; for statement in statements { - previous_value = self.run_node(statement, variables)?; + previous_value = self.run_node(statement, context)?; } Ok(previous_value) @@ -179,7 +168,7 @@ impl Vm { for node in nodes { let position = node.position; - let value = if let Some(value) = self.run_node(node, variables)? { + let value = if let Some(value) = self.run_node(node, context)? { value } else { return Err(VmError::ExpectedValue { position }); @@ -209,14 +198,13 @@ impl Vm { value_arguments: value_parameter_nodes, } => { let function_position = function_node.position; - let function_value = - if let Some(value) = self.run_node(*function_node, variables)? { - value - } else { - return Err(VmError::ExpectedValue { - position: function_position, - }); - }; + let function_value = if let Some(value) = self.run_node(*function_node, context)? { + value + } else { + return Err(VmError::ExpectedValue { + position: function_position, + }); + }; let function = if let Some(function) = function_value.as_function() { function } else { @@ -231,7 +219,7 @@ impl Vm { for node in value_nodes { let position = node.position; - let value = if let Some(value) = self.run_node(node, variables)? { + let value = if let Some(value) = self.run_node(node, context)? { value } else { return Err(VmError::ExpectedValue { position }); @@ -245,10 +233,10 @@ impl Vm { None }; - Ok(function.clone().call(None, value_parameters, variables)?) + Ok(function.clone().call(None, value_parameters, context)?) } Statement::Identifier(identifier) => { - if let Some(value) = variables.get(&identifier) { + if let Some(value) = context.get_value(&identifier) { Ok(Some(value.clone())) } else { Err(VmError::UndefinedVariable { @@ -261,7 +249,7 @@ impl Vm { .into_iter() .map(|node| { let span = node.position; - if let Some(value) = self.run_node(node, variables)? { + if let Some(value) = self.run_node(node, context)? { Ok(value) } else { Err(VmError::ExpectedValue { position: span }) @@ -283,7 +271,7 @@ impl Vm { }); }; let position = value_node.position; - let value = if let Some(value) = self.run_node(value_node, variables)? { + let value = if let Some(value) = self.run_node(value_node, context)? { value } else { return Err(VmError::ExpectedValue { position }); @@ -295,13 +283,13 @@ impl Vm { Ok(Some(Value::map(values))) } Statement::Nil(node) => { - let _return = self.run_node(*node, variables)?; + let _return = self.run_node(*node, context)?; Ok(None) } Statement::PropertyAccess(left, right) => { let left_span = left.position; - let left_value = if let Some(value) = self.run_node(*left, variables)? { + let left_value = if let Some(value) = self.run_node(*left, context)? { value } else { return Err(VmError::ExpectedValue { @@ -336,7 +324,7 @@ impl Vm { if let Some(value_nodes) = value_argument_nodes { for node in value_nodes { let position = node.position; - let value = if let Some(value) = self.run_node(node, variables)? { + let value = if let Some(value) = self.run_node(node, context)? { value } else { return Err(VmError::ExpectedValue { position }); @@ -495,7 +483,7 @@ mod tests { fn add_assign() { let input = "x = 1; x += 1; x"; - assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(2)))); + assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(2)))); } #[test] @@ -503,7 +491,7 @@ mod tests { let input = "true || false"; assert_eq!( - run(input, &mut HashMap::new()), + run(input, &mut Context::new()), Ok(Some(Value::boolean(true))) ); } @@ -513,7 +501,7 @@ mod tests { let input = "{ y = 'foo', } == { y = 'foo', }"; assert_eq!( - run(input, &mut HashMap::new()), + run(input, &mut Context::new()), Ok(Some(Value::boolean(true))) ); } @@ -523,7 +511,7 @@ mod tests { let input = "42 == 42"; assert_eq!( - run(input, &mut HashMap::new()), + run(input, &mut Context::new()), Ok(Some(Value::boolean(true))) ); } @@ -532,7 +520,7 @@ mod tests { fn modulo() { let input = "42 % 2"; - assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(0)))); + assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(0)))); } #[test] @@ -540,7 +528,7 @@ mod tests { let input = "42 / 2"; assert_eq!( - run(input, &mut HashMap::new()), + run(input, &mut Context::new()), Ok(Some(Value::integer(21))) ); } @@ -550,7 +538,7 @@ mod tests { let input = "2 < 3"; assert_eq!( - run(input, &mut HashMap::new()), + run(input, &mut Context::new()), Ok(Some(Value::boolean(true))) ); } @@ -560,7 +548,7 @@ mod tests { let input = "42 <= 42"; assert_eq!( - run(input, &mut HashMap::new()), + run(input, &mut Context::new()), Ok(Some(Value::boolean(true))) ); } @@ -570,7 +558,7 @@ mod tests { let input = "2 > 3"; assert_eq!( - run(input, &mut HashMap::new()), + run(input, &mut Context::new()), Ok(Some(Value::boolean(false))) ); } @@ -580,7 +568,7 @@ mod tests { let input = "42 >= 42"; assert_eq!( - run(input, &mut HashMap::new()), + run(input, &mut Context::new()), Ok(Some(Value::boolean(true))) ); } @@ -590,7 +578,7 @@ mod tests { let input = "9223372036854775807 + 1"; assert_eq!( - run(input, &mut HashMap::new()), + run(input, &mut Context::new()), Ok(Some(Value::integer(i64::MAX))) ); } @@ -600,7 +588,7 @@ mod tests { let input = "-9223372036854775808 - 1"; assert_eq!( - run(input, &mut HashMap::new()), + run(input, &mut Context::new()), Ok(Some(Value::integer(i64::MIN))) ); } @@ -609,7 +597,7 @@ mod tests { fn multiply() { let input = "2 * 3"; - assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(6)))); + assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(6)))); } #[test] @@ -617,7 +605,7 @@ mod tests { let input = "true"; assert_eq!( - run(input, &mut HashMap::new()), + run(input, &mut Context::new()), Ok(Some(Value::boolean(true))) ); } @@ -627,7 +615,7 @@ mod tests { let input = "42.is_even()"; assert_eq!( - run(input, &mut HashMap::new()), + run(input, &mut Context::new()), Ok(Some(Value::boolean(true))) ); } @@ -637,7 +625,7 @@ mod tests { let input = "42.is_odd()"; assert_eq!( - run(input, &mut HashMap::new()), + run(input, &mut Context::new()), Ok(Some(Value::boolean(false))) ); } @@ -646,27 +634,27 @@ mod tests { fn length() { let input = "[1, 2, 3].length()"; - assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(3)))); + assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(3)))); } #[test] fn list_access() { let input = "[1, 2, 3].1"; - assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(2)))); + assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(2)))); } #[test] fn add() { let input = "1 + 2"; - assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(3)))); + assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(3)))); } #[test] fn add_multiple() { let input = "1 + 2 + 3"; - assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(6)))); + assert_eq!(run(input, &mut Context::new()), Ok(Some(Value::integer(6)))); } } diff --git a/dust-shell/src/main.rs b/dust-shell/src/main.rs index 80ecf4e..c7fff89 100644 --- a/dust-shell/src/main.rs +++ b/dust-shell/src/main.rs @@ -1,7 +1,7 @@ -use std::{collections::HashMap, fs::read_to_string}; +use std::fs::read_to_string; use clap::Parser; -use dust_lang::{run, DustError, Identifier, Value}; +use dust_lang::{run, Context, DustError}; #[derive(Parser)] struct Cli { @@ -13,20 +13,20 @@ struct Cli { fn main() { let args = Cli::parse(); - let mut variables = HashMap::new(); + let mut context = Context::new(); if let Some(command) = &args.command { - run_and_display_errors(command, &mut variables); + run_and_display_errors(command, &mut context); } else if let Some(path) = &args.path { let source = read_to_string(path).expect("Failed to read file"); - run_and_display_errors(&source, &mut variables) + run_and_display_errors(&source, &mut context) } else { panic!("No command or path provided"); }; } -fn run_and_display_errors(source: &str, variables: &mut HashMap) { +fn run_and_display_errors(source: &str, variables: &mut Context) { match run(source, variables) { Ok(return_value) => { if let Some(value) = return_value {