From d99e3cb861f7935735cb6938dae6523af5b29f22 Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 7 Mar 2024 12:29:07 -0500 Subject: [PATCH] Add indexes --- src/abstract_tree/expression.rs | 6 ++- src/abstract_tree/index.rs | 76 +++++++++++++++++++++++++++++++++ src/abstract_tree/mod.rs | 3 +- src/abstract_tree/value_node.rs | 10 ++++- src/error.rs | 4 ++ src/parser.rs | 6 ++- src/value.rs | 16 +++++++ tests/expressions.rs | 10 ++++- 8 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 src/abstract_tree/index.rs diff --git a/src/abstract_tree/expression.rs b/src/abstract_tree/expression.rs index 3fa742a..4d181fe 100644 --- a/src/abstract_tree/expression.rs +++ b/src/abstract_tree/expression.rs @@ -4,11 +4,12 @@ use crate::{ Value, }; -use super::{AbstractTree, Identifier, Logic, Math, Type, ValueNode}; +use super::{AbstractTree, Identifier, Index, Logic, Math, Type, ValueNode}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Expression<'src> { Identifier(Identifier), + Index(Box>), Logic(Box>), Math(Box>), Value(ValueNode<'src>), @@ -18,6 +19,7 @@ impl<'src> AbstractTree for Expression<'src> { fn expected_type(&self, _context: &Context) -> Result { match self { Expression::Identifier(identifier) => identifier.expected_type(_context), + Expression::Index(index) => index.expected_type(_context), Expression::Logic(logic) => logic.expected_type(_context), Expression::Math(math) => math.expected_type(_context), Expression::Value(value_node) => value_node.expected_type(_context), @@ -27,6 +29,7 @@ impl<'src> AbstractTree for Expression<'src> { fn validate(&self, _context: &Context) -> Result<(), ValidationError> { match self { Expression::Identifier(identifier) => identifier.validate(_context), + Expression::Index(index) => index.validate(_context), Expression::Logic(logic) => logic.validate(_context), Expression::Math(math) => math.validate(_context), Expression::Value(value_node) => value_node.validate(_context), @@ -36,6 +39,7 @@ impl<'src> AbstractTree for Expression<'src> { fn run(self, _context: &Context) -> Result { match self { Expression::Identifier(identifier) => identifier.run(_context), + Expression::Index(index) => index.run(_context), Expression::Logic(logic) => logic.run(_context), Expression::Math(math) => math.run(_context), Expression::Value(value_node) => value_node.run(_context), diff --git a/src/abstract_tree/index.rs b/src/abstract_tree/index.rs new file mode 100644 index 0000000..9d3ab07 --- /dev/null +++ b/src/abstract_tree/index.rs @@ -0,0 +1,76 @@ +use crate::{ + context::Context, + error::{RuntimeError, ValidationError}, + Value, +}; + +use super::{AbstractTree, Expression, Type, ValueNode}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct Index<'src> { + left: Expression<'src>, + right: Expression<'src>, +} + +impl<'src> Index<'src> { + pub fn new(left: Expression<'src>, right: Expression<'src>) -> Self { + Self { left, right } + } +} + +impl<'src> AbstractTree for Index<'src> { + fn expected_type(&self, context: &Context) -> Result { + let left_type = self.left.expected_type(context)?; + + if let ( + Expression::Value(ValueNode::List(expression_list)), + Expression::Value(ValueNode::Integer(index)), + ) = (&self.left, &self.right) + { + let expression = if let Some(expression) = expression_list.get(*index as usize) { + expression + } else { + return Ok(Type::None); + }; + + expression.expected_type(context) + } else { + Err(ValidationError::CannotIndex(left_type)) + } + } + + fn validate(&self, context: &Context) -> Result<(), ValidationError> { + let left_type = self.left.expected_type(context)?; + + match left_type { + Type::List => todo!(), + Type::ListOf(_) => todo!(), + Type::ListExact(_) => { + let right_type = self.right.expected_type(context)?; + + if let Type::Integer = right_type { + Ok(()) + } else { + Err(ValidationError::CannotIndexWith(left_type, right_type)) + } + } + _ => Err(ValidationError::CannotIndex(left_type)), + } + } + + fn run(self, _context: &Context) -> Result { + let left_value = self.left.run(_context)?; + let right_value = self.right.run(_context)?; + + if let (Some(list), Some(index)) = (left_value.as_list(), right_value.as_integer()) { + Ok(list + .get(index as usize) + .cloned() + .unwrap_or_else(Value::none)) + } else { + Err(RuntimeError::ValidationFailure( + ValidationError::CannotIndexWith(left_value.r#type(), right_value.r#type()), + )) + } + } +} diff --git a/src/abstract_tree/mod.rs b/src/abstract_tree/mod.rs index c38fefa..f598672 100644 --- a/src/abstract_tree/mod.rs +++ b/src/abstract_tree/mod.rs @@ -2,6 +2,7 @@ pub mod assignment; pub mod block; pub mod expression; pub mod identifier; +pub mod index; pub mod logic; pub mod r#loop; pub mod math; @@ -11,7 +12,7 @@ pub mod value_node; pub use self::{ assignment::Assignment, block::Block, expression::Expression, identifier::Identifier, - logic::Logic, math::Math, r#loop::Loop, r#type::Type, statement::Statement, + index::Index, logic::Logic, math::Math, r#loop::Loop, r#type::Type, statement::Statement, value_node::ValueNode, }; diff --git a/src/abstract_tree/value_node.rs b/src/abstract_tree/value_node.rs index c16a276..478a34d 100644 --- a/src/abstract_tree/value_node.rs +++ b/src/abstract_tree/value_node.rs @@ -26,7 +26,15 @@ impl<'src> AbstractTree for ValueNode<'src> { ValueNode::Boolean(_) => Type::Boolean, ValueNode::Float(_) => Type::Float, ValueNode::Integer(_) => Type::Integer, - ValueNode::List(_) => Type::List, + ValueNode::List(items) => { + let mut item_types = Vec::with_capacity(items.len()); + + for expression in items { + item_types.push(expression.expected_type(_context)?); + } + + Type::ListExact(item_types) + } ValueNode::Map(_) => Type::Map, ValueNode::Range(_) => Type::Range, ValueNode::String(_) => Type::String, diff --git a/src/error.rs b/src/error.rs index f653b22..bad8a92 100644 --- a/src/error.rs +++ b/src/error.rs @@ -85,6 +85,8 @@ impl Error { "The variable {identifier} does not exist." ))); } + ValidationError::CannotIndex(_) => todo!(), + ValidationError::CannotIndexWith(_, _) => todo!(), } report.finish() @@ -137,6 +139,8 @@ impl From for RuntimeError { #[derive(Debug, PartialEq)] pub enum ValidationError { + CannotIndex(Type), + CannotIndexWith(Type, Type), ExpectedBoolean, ExpectedIntegerOrFloat, RwLockPoison(RwLockPoisonError), diff --git a/src/parser.rs b/src/parser.rs index b195ccd..33d4412 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -95,7 +95,6 @@ pub fn parser<'src>() -> DustParser<'src> { use Operator::*; let logic_and_math = atom - .clone() .pratt(( prefix(2, just(Token::Operator(Not)), |expression| { Expression::Logic(Box::new(Logic::Not(expression))) @@ -143,6 +142,11 @@ pub fn parser<'src>() -> DustParser<'src> { infix(left(1), just(Token::Operator(Modulo)), |left, right| { Expression::Math(Box::new(Math::Modulo(left, right))) }), + infix( + left(3), + just(Token::Control(Control::Dot)), + |left, right| Expression::Index(Box::new(Index::new(left, right))), + ), )) .boxed(); diff --git a/src/value.rs b/src/value.rs index 782e458..a29cddf 100644 --- a/src/value.rs +++ b/src/value.rs @@ -103,6 +103,22 @@ impl Value { Err(ValidationError::ExpectedBoolean) } + pub fn as_list(&self) -> Option<&Vec> { + if let ValueInner::List(list) = self.inner().as_ref() { + Some(list) + } else { + None + } + } + + pub fn as_integer(&self) -> Option { + if let ValueInner::Integer(integer) = self.inner().as_ref() { + Some(*integer) + } else { + None + } + } + pub fn is_none(&self) -> bool { self == get_none() } diff --git a/tests/expressions.rs b/tests/expressions.rs index b26dad5..63d9935 100644 --- a/tests/expressions.rs +++ b/tests/expressions.rs @@ -12,5 +12,13 @@ fn logic() { #[test] fn math() { assert_eq!(interpret("1 + 1"), Ok(Value::integer(2))); - assert_eq!(interpret("21 + 19 + 1 * 2"), Ok(Value::integer(42))); + assert_eq!( + interpret("2 * (21 + 19 + 1 * 2) / 2"), + Ok(Value::integer(42)) + ); +} + +#[test] +fn list_index() { + assert_eq!(interpret("foo = [1, 2, 3]; foo.2"), Ok(Value::integer(3))); }