diff --git a/dust-lang/src/parse.rs b/dust-lang/src/parse.rs index 785f54a..bd194db 100644 --- a/dust-lang/src/parse.rs +++ b/dust-lang/src/parse.rs @@ -205,6 +205,30 @@ mod tests { use super::*; + #[test] + fn list_access() { + let input = "[1, 2, 3].0"; + + assert_eq!( + parse(input), + Ok([Node::new( + Statement::PropertyAccess( + Box::new(Node::new( + Statement::List(vec![ + Node::new(Statement::Constant(Value::integer(1)), (1, 2)), + Node::new(Statement::Constant(Value::integer(2)), (4, 5)), + Node::new(Statement::Constant(Value::integer(3)), (7, 8)), + ]), + (0, 9) + )), + Box::new(Node::new(Statement::Constant(Value::integer(0)), (10, 11))), + ), + (0, 11), + )] + .into()) + ); + } + #[test] fn property_access() { let input = "a.b"; diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index e978b02..9bfa14e 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -119,6 +119,29 @@ impl Value { _ => todo!(), } } + + pub fn list_access(&self, index: i64) -> Result { + match self.inner().as_ref() { + ValueInner::List(list) => { + if index < 0 { + return Err(ValueError::IndexOutOfBounds { + value: self.clone(), + index, + }); + } + + if let Some(value) = list.get(index as usize) { + Ok(value.clone()) + } else { + Err(ValueError::IndexOutOfBounds { + value: self.clone(), + index, + }) + } + } + _ => Err(ValueError::ExpectedList(self.clone())), + } + } } impl Display for Value { @@ -560,4 +583,6 @@ impl Ord for ValueInner { pub enum ValueError { CannotAdd(Value, Value), PropertyNotFound { value: Value, property: Identifier }, + IndexOutOfBounds { value: Value, index: i64 }, + ExpectedList(Value), } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 0cfe87c..1ad4315 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -110,17 +110,24 @@ impl Vm { }); }; let right_span = right.span; - let right = if let Statement::Identifier(identifier) = &right.statement { - identifier - } else { - return Err(VmError::ExpectedValue { - position: right_span, - }); - }; - let value = left.property_access(right)?; + if let Statement::Identifier(identifier) = &right.statement { + let value = left.property_access(identifier)?; - Ok(Some(value)) + return Ok(Some(value)); + } + + if let Statement::Constant(value) = &right.statement { + if let Some(index) = value.as_integer() { + let value = left.list_access(index)?; + + return Ok(Some(value)); + } + } + + Err(VmError::ExpectedIdentifierOrInteger { + position: right_span, + }) } } } @@ -134,6 +141,7 @@ pub enum VmError { // Anaylsis Failures // These should be prevented by running the analyzer before the VM ExpectedValue { position: Span }, + ExpectedIdentifierOrInteger { position: (usize, usize) }, } impl From for VmError { @@ -152,6 +160,13 @@ impl From for VmError { mod tests { use super::*; + #[test] + fn list_access() { + let input = "[1, 2, 3][1]"; + + assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(2)))); + } + #[test] fn property_access() { let input = "[1, 2, 3].length";