diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index d92880b..ba06653 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -596,11 +596,17 @@ impl<'src> Parser<'src> { fn parse_infix(&mut self, left: Node) -> Result, ParseError> { let left_start = left.position.0; + let operator_precedence = self.current.0.precedence() + - if self.current.0.is_right_associative() { + 1 + } else { + 0 + }; if let Token::Dot = &self.current.0 { self.next_token()?; - let right = self.parse_statement(0)?; + let right = self.parse_statement(operator_precedence)?; let right_end = right.position.1; if let Statement::BuiltInFunctionCall { @@ -679,12 +685,6 @@ impl<'src> Parser<'src> { }); } }; - let operator_precedence = self.current.0.precedence() - - if self.current.0.is_right_associative() { - 1 - } else { - 0 - }; self.next_token()?; @@ -845,6 +845,59 @@ mod tests { use super::*; + #[test] + fn map_property_nested() { + let input = "{ x = { y = 42 } }.x.y"; + + assert_eq!( + parse(input), + Ok(AbstractSyntaxTree { + nodes: [Node { + inner: Statement::PropertyAccess( + Box::new(Node { + inner: Statement::Map(vec![( + Node { + inner: Statement::Identifier(Identifier::new("x")), + position: (2, 3) + }, + Node { + inner: Statement::Map(vec![( + Node { + inner: Statement::Identifier(Identifier::new("y")), + position: (8, 9) + }, + Node { + inner: Statement::Constant(Value::integer(42)), + position: (12, 14) + } + )]), + position: (6, 16) + } + )]), + position: (0, 18) + }), + Box::new(Node { + inner: Statement::PropertyAccess( + Box::new(Node { + inner: Statement::Identifier(Identifier::new("x")), + position: (19, 20) + }), + Box::new(Node { + inner: Statement::Identifier(Identifier::new("y")), + + position: (21, 22) + }) + ), + position: (19, 22) + }) + ), + position: (0, 22) + }] + .into() + }) + ) + } + #[test] fn range() { let input = "0..42"; diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 1d37a08..4a86733 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -153,6 +153,14 @@ impl Value { } } + pub fn as_string(&self) -> Option<&String> { + if let ValueInner::String(string) = self.inner().as_ref() { + Some(string) + } else { + 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)), diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index d8ac15a..a61143d 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -495,12 +495,22 @@ impl Vm { } } - if let (Some(map), Statement::Identifier(identifier)) = - (left_value.as_map(), &right.inner) - { - let value = map.get(identifier).cloned(); + if let Some(map) = left_value.as_map() { + if let Statement::Identifier(identifier) = right.inner { + let value = map.get(&identifier).cloned(); - return Ok(value); + return Ok(value); + } + + if let Some(value) = self.run_statement(*right)? { + if let Some(string) = value.as_string() { + let identifier = Identifier::new(string); + + let value = map.get(&identifier).cloned(); + + return Ok(value); + } + } } Err(VmError::ExpectedIdentifierIntegerOrRange { @@ -701,6 +711,27 @@ impl Display for VmError { mod tests { use super::*; + #[test] + fn map_property() { + let input = "{ x = 42 }.x"; + + assert_eq!(run(input), Ok(Some(Value::integer(42)))); + } + + #[test] + fn map_property_nested() { + let input = "{ x = { y = 42 } }.x.y"; + + assert_eq!(run(input), Ok(Some(Value::integer(42)))); + } + + #[test] + fn map_property_access_expression() { + let input = "{ foobar = 42 }.('foo' + 'bar')"; + + assert_eq!(run(input), Ok(Some(Value::integer(42)))); + } + #[test] fn list_index_range() { let input = "[1, 2, 3, 4, 5].1..3";