From 78228ce8d65baeff531031cb6f0256ca3b3b9133 Mon Sep 17 00:00:00 2001 From: Jeff Date: Sun, 11 Aug 2024 22:02:17 -0400 Subject: [PATCH] Add new analyzer and vm tests --- dust-lang/src/analyzer.rs | 58 +++++++++++++++++++++++++++------------ dust-lang/src/value.rs | 8 ++++++ dust-lang/src/vm.rs | 29 ++++++++++++++++++++ 3 files changed, 78 insertions(+), 17 deletions(-) diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index e9485b1..d22104f 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -220,7 +220,6 @@ impl<'a> Analyzer<'a> { } else { return Err(AnalyzerError::ExpectedBoolean { actual: condition.as_ref().clone(), - position: condition.position, }); } @@ -238,7 +237,6 @@ impl<'a> Analyzer<'a> { } else { return Err(AnalyzerError::ExpectedBoolean { actual: condition.as_ref().clone(), - position: condition.position, }); } @@ -257,7 +255,6 @@ impl<'a> Analyzer<'a> { } else { return Err(AnalyzerError::ExpectedBoolean { actual: condition.as_ref().clone(), - position: condition.position, }); } @@ -271,7 +268,6 @@ impl<'a> Analyzer<'a> { } else { return Err(AnalyzerError::ExpectedBoolean { actual: condition.clone(), - position: condition.position, }); } @@ -291,7 +287,6 @@ impl<'a> Analyzer<'a> { } else { return Err(AnalyzerError::ExpectedBoolean { actual: condition.as_ref().clone(), - position: condition.position, }); } @@ -305,7 +300,6 @@ impl<'a> Analyzer<'a> { } else { return Err(AnalyzerError::ExpectedBoolean { actual: condition.clone(), - position: condition.position, }); } @@ -328,13 +322,20 @@ impl<'a> Analyzer<'a> { self.analyze_node(node)?; } Statement::PropertyAccess(left, right) => { + self.analyze_node(left)?; + + if let Statement::Identifier(_) = right.inner { + // Do not expect a value for property accessors + } else { + self.analyze_node(right)?; + } + if let Some(Type::List { .. }) = left.inner.expected_type(self.context) { if let Some(Type::Integer) = right.inner.expected_type(self.context) { // Allow indexing lists with integers } else { return Err(AnalyzerError::ExpectedInteger { actual: right.as_ref().clone(), - position: right.position, }); } } @@ -350,9 +351,6 @@ impl<'a> Analyzer<'a> { }); } } - - self.analyze_node(left)?; - self.analyze_node(right)?; } Statement::While { condition, body } => { self.analyze_node(condition)?; @@ -362,7 +360,6 @@ impl<'a> Analyzer<'a> { } else { return Err(AnalyzerError::ExpectedBoolean { actual: condition.as_ref().clone(), - position: condition.position, }); } } @@ -376,18 +373,15 @@ impl<'a> Analyzer<'a> { pub enum AnalyzerError { ExpectedBoolean { actual: Node, - position: Span, }, ExpectedIdentifier { actual: Node, - position: Span, }, ExpectedIdentifierOrString { actual: Node, }, ExpectedInteger { actual: Node, - position: Span, }, ExpectedValue { actual: Node, @@ -416,10 +410,10 @@ pub enum AnalyzerError { impl AnalyzerError { pub fn position(&self) -> Span { match self { - AnalyzerError::ExpectedBoolean { position, .. } => *position, - AnalyzerError::ExpectedIdentifier { position, .. } => *position, + AnalyzerError::ExpectedBoolean { actual, .. } => actual.position, + AnalyzerError::ExpectedIdentifier { actual, .. } => actual.position, AnalyzerError::ExpectedIdentifierOrString { actual } => actual.position, - AnalyzerError::ExpectedInteger { position, .. } => *position, + AnalyzerError::ExpectedInteger { actual, .. } => actual.position, AnalyzerError::ExpectedValue { actual } => actual.position, AnalyzerError::ExpectedValueArgumentCount { position, .. } => *position, AnalyzerError::TypeConflict { @@ -485,6 +479,36 @@ mod tests { use super::*; + #[test] + fn malformed_list_index() { + let source = "[1, 2, 3].foo"; + + assert_eq!( + analyze(source), + Err(DustError::AnalyzerError { + analyzer_error: AnalyzerError::ExpectedInteger { + actual: Node::new(Statement::Identifier(Identifier::new("foo")), (10, 13)), + }, + source + }) + ); + } + + #[test] + fn malformed_property_access() { + let source = "{ x = 1 }.0"; + + assert_eq!( + analyze(source), + Err(DustError::AnalyzerError { + analyzer_error: AnalyzerError::ExpectedIdentifierOrString { + actual: Node::new(Statement::Constant(Value::integer(0)), (10, 11)), + }, + source + }) + ); + } + #[test] fn length_no_arguments() { let source = "length()"; diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index fe5a607..15ba230 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -121,6 +121,14 @@ impl Value { } } + pub fn as_map(&self) -> Option<&BTreeMap> { + if let ValueInner::Map(map) = self.inner().as_ref() { + Some(map) + } else { + None + } + } + pub fn as_integer(&self) -> Option { if let ValueInner::Integer(integer) = self.inner().as_ref() { Some(*integer) diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index cc4bb5a..cf2ba11 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -463,6 +463,14 @@ impl Vm { } } + if let (Some(map), Statement::Identifier(identifier)) = + (left_value.as_map(), &right.inner) + { + let value = map.get(identifier).cloned(); + + return Ok(value); + } + Err(VmError::ExpectedIdentifierOrInteger { position: right_span, }) @@ -623,6 +631,27 @@ impl Display for VmError { mod tests { use super::*; + #[test] + fn list_index() { + let input = "[1, 42, 3].1"; + + assert_eq!(run(input), Ok(Some(Value::integer(42)))); + } + + #[test] + fn map_property_access() { + let input = "{ a = 42 }.a"; + + assert_eq!(run(input), Ok(Some(Value::integer(42)))); + } + + #[test] + fn built_in_function_dot_notation() { + let input = "42.to_string()"; + + assert_eq!(run(input), Ok(Some(Value::string("42")))); + } + #[test] fn to_string() { let input = "to_string(42)";