From f64babc546addb1b1d10edc068aed0a54cde4dc1 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 7 Aug 2024 11:57:15 -0400 Subject: [PATCH] Add more analysis to the analyzer --- dust-lang/src/analyzer.rs | 166 +++++++++++++++++++++++++++++--------- dust-lang/src/vm.rs | 2 +- 2 files changed, 129 insertions(+), 39 deletions(-) diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index 9662f9f..cf05232 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -1,18 +1,43 @@ -use crate::{AbstractSyntaxTree, Node, Statement}; +use std::collections::HashMap; -pub fn analyze(abstract_tree: &AbstractSyntaxTree) -> Result<(), AnalyzerError> { - let analyzer = Analyzer::new(abstract_tree); +use crate::{AbstractSyntaxTree, Identifier, Node, Statement, Type, Value}; + +/// Analyzes the abstract syntax tree for errors. +/// +/// # Examples +/// ``` +/// # use std::collections::HashMap; +/// # use dust_lang::*; +/// let input = "x = 1 + false"; +/// let abstract_tree = parse(input).unwrap(); +/// let variables = HashMap::new(); +/// let result = analyze(&abstract_tree, &variables); +/// +/// assert!(result.is_err()); +/// ``` +pub fn analyze( + abstract_tree: &AbstractSyntaxTree, + variables: &HashMap, +) -> Result<(), AnalyzerError> { + let analyzer = Analyzer::new(abstract_tree, variables); analyzer.analyze() } pub struct Analyzer<'a> { abstract_tree: &'a AbstractSyntaxTree, + variables: &'a HashMap, } impl<'a> Analyzer<'a> { - pub fn new(abstract_tree: &'a AbstractSyntaxTree) -> Self { - Analyzer { abstract_tree } + pub fn new( + abstract_tree: &'a AbstractSyntaxTree, + variables: &'a HashMap, + ) -> Self { + Self { + abstract_tree, + variables, + } } pub fn analyze(&self) -> Result<(), AnalyzerError> { @@ -26,6 +51,24 @@ impl<'a> Analyzer<'a> { fn analyze_node(&self, node: &Node) -> Result<(), AnalyzerError> { match &node.statement { Statement::Add(left, right) => { + if let Some(Type::Integer) | Some(Type::Float) = + left.statement.expected_type(self.variables) + { + } else { + return Err(AnalyzerError::ExpectedIntegerOrFloat { + actual: left.as_ref().clone(), + }); + } + + if let Some(Type::Integer) | Some(Type::Float) = + right.statement.expected_type(self.variables) + { + } else { + return Err(AnalyzerError::ExpectedIntegerOrFloat { + actual: right.as_ref().clone(), + }); + } + self.analyze_node(left)?; self.analyze_node(right)?; } @@ -55,12 +98,32 @@ impl<'a> Analyzer<'a> { } } Statement::Multiply(left, right) => { + if let Some(Type::Integer) | Some(Type::Float) = + left.statement.expected_type(self.variables) + { + } else { + return Err(AnalyzerError::ExpectedIntegerOrFloat { + actual: left.as_ref().clone(), + }); + } + + if let Some(Type::Integer) | Some(Type::Float) = + right.statement.expected_type(self.variables) + { + } else { + return Err(AnalyzerError::ExpectedIntegerOrFloat { + actual: right.as_ref().clone(), + }); + } + self.analyze_node(left)?; self.analyze_node(right)?; } Statement::PropertyAccess(left, right) => { - if let Statement::Identifier(_) = &left.statement { - // Identifier is in the correct position + if let Statement::Identifier(_) | Statement::Constant(_) | Statement::List(_) = + &left.statement + { + // Left side is valid } else { return Err(AnalyzerError::ExpectedIdentifier { actual: left.as_ref().clone(), @@ -79,6 +142,7 @@ impl<'a> Analyzer<'a> { #[derive(Clone, Debug, PartialEq)] pub enum AnalyzerError { ExpectedIdentifier { actual: Node }, + ExpectedIntegerOrFloat { actual: Node }, UnexpectedIdentifier { identifier: Node }, } @@ -88,6 +152,58 @@ mod tests { use super::*; + #[test] + fn multiply_expect_integer_or_float() { + let abstract_tree = AbstractSyntaxTree { + nodes: [Node::new( + Statement::Multiply( + Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))), + Box::new(Node::new( + Statement::Constant(Value::boolean(false)), + (1, 2), + )), + ), + (0, 2), + )] + .into(), + }; + let variables = HashMap::new(); + let analyzer = Analyzer::new(&abstract_tree, &variables); + + assert_eq!( + analyzer.analyze(), + Err(AnalyzerError::ExpectedIntegerOrFloat { + actual: Node::new(Statement::Constant(Value::boolean(false)), (1, 2)) + }) + ) + } + + #[test] + fn add_expect_integer_or_float() { + let abstract_tree = AbstractSyntaxTree { + nodes: [Node::new( + Statement::Add( + Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))), + Box::new(Node::new( + Statement::Constant(Value::boolean(false)), + (1, 2), + )), + ), + (0, 2), + )] + .into(), + }; + let variables = HashMap::new(); + let analyzer = Analyzer::new(&abstract_tree, &variables); + + assert_eq!( + analyzer.analyze(), + Err(AnalyzerError::ExpectedIntegerOrFloat { + actual: Node::new(Statement::Constant(Value::boolean(false)), (1, 2)) + }) + ) + } + #[test] fn assignment_expect_identifier() { let abstract_tree = AbstractSyntaxTree { @@ -100,8 +216,8 @@ mod tests { )] .into(), }; - - let analyzer = Analyzer::new(&abstract_tree); + let variables = HashMap::new(); + let analyzer = Analyzer::new(&abstract_tree, &variables); assert_eq!( analyzer.analyze(), @@ -112,7 +228,7 @@ mod tests { } #[test] - fn unexpected_identifier_simple() { + fn unexpected_identifier() { let abstract_tree = AbstractSyntaxTree { nodes: [Node::new( Statement::Identifier(Identifier::new("x")), @@ -120,8 +236,8 @@ mod tests { )] .into(), }; - - let analyzer = Analyzer::new(&abstract_tree); + let variables = HashMap::new(); + let analyzer = Analyzer::new(&abstract_tree, &variables); assert_eq!( analyzer.analyze(), @@ -130,30 +246,4 @@ mod tests { }) ) } - - #[test] - fn unexpected_identifier_nested() { - let abstract_tree = AbstractSyntaxTree { - nodes: [Node::new( - Statement::Add( - Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))), - Box::new(Node::new( - Statement::Identifier(Identifier::new("x")), - (1, 2), - )), - ), - (0, 1), - )] - .into(), - }; - - let analyzer = Analyzer::new(&abstract_tree); - - assert_eq!( - analyzer.analyze(), - Err(AnalyzerError::UnexpectedIdentifier { - identifier: Node::new(Statement::Identifier(Identifier::new("x")), (1, 2)) - }) - ) - } } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index eabd933..fea2c8d 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -10,7 +10,7 @@ pub fn run( variables: &mut HashMap, ) -> Result, VmError> { let abstract_syntax_tree = parse(input)?; - let analyzer = Analyzer::new(&abstract_syntax_tree); + let analyzer = Analyzer::new(&abstract_syntax_tree, variables); analyzer.analyze()?;