diff --git a/src/abstract_tree/expression.rs b/src/abstract_tree/expression.rs index a0062ea..4b01206 100644 --- a/src/abstract_tree/expression.rs +++ b/src/abstract_tree/expression.rs @@ -3,13 +3,17 @@ use crate::{ error::{RuntimeError, ValidationError}, }; -use super::{AbstractTree, Action, FunctionCall, Identifier, Index, Logic, Math, Type, ValueNode}; +use super::{ + AbstractTree, Action, FunctionCall, Identifier, ListIndex, Logic, MapIndex, Math, Type, + ValueNode, +}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Expression { FunctionCall(FunctionCall), Identifier(Identifier), - Index(Box), + MapIndex(Box), + ListIndex(Box), Logic(Box), Math(Box), Value(ValueNode), @@ -20,7 +24,8 @@ impl AbstractTree for Expression { match self { Expression::FunctionCall(function_call) => function_call.expected_type(_context), Expression::Identifier(identifier) => identifier.expected_type(_context), - Expression::Index(index) => index.expected_type(_context), + Expression::MapIndex(index) => index.expected_type(_context), + Expression::ListIndex(_) => todo!(), Expression::Logic(logic) => logic.expected_type(_context), Expression::Math(math) => math.expected_type(_context), Expression::Value(value_node) => value_node.expected_type(_context), @@ -31,7 +36,8 @@ impl AbstractTree for Expression { match self { Expression::FunctionCall(function_call) => function_call.validate(_context), Expression::Identifier(identifier) => identifier.validate(_context), - Expression::Index(index) => index.validate(_context), + Expression::MapIndex(index) => index.validate(_context), + Expression::ListIndex(_) => todo!(), Expression::Logic(logic) => logic.validate(_context), Expression::Math(math) => math.validate(_context), Expression::Value(value_node) => value_node.validate(_context), @@ -42,7 +48,8 @@ impl AbstractTree for Expression { match self { Expression::FunctionCall(function_call) => function_call.run(_context), Expression::Identifier(identifier) => identifier.run(_context), - Expression::Index(index) => index.run(_context), + Expression::MapIndex(index) => index.run(_context), + Expression::ListIndex(_) => todo!(), 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/list_index.rs similarity index 83% rename from src/abstract_tree/index.rs rename to src/abstract_tree/list_index.rs index e3390e4..19f21dc 100644 --- a/src/abstract_tree/index.rs +++ b/src/abstract_tree/list_index.rs @@ -6,18 +6,18 @@ use crate::{ use super::{AbstractTree, Action, Expression, Type, ValueNode, WithPosition}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct Index { +pub struct ListIndex { left: WithPosition, right: WithPosition, } -impl Index { +impl ListIndex { pub fn new(left: WithPosition, right: WithPosition) -> Self { Self { left, right } } } -impl AbstractTree for Index { +impl AbstractTree for ListIndex { fn expected_type(&self, _context: &Context) -> Result { let left_type = self.left.node.expected_type(_context)?; @@ -53,7 +53,11 @@ impl AbstractTree for Index { if let Type::Integer = right_type { Ok(()) } else { - Err(ValidationError::CannotIndexWith(left_type, right_type)) + Err(ValidationError::CannotIndexWith { + collection_type: todo!(), + index_type: todo!(), + position: todo!(), + }) } } _ => Err(ValidationError::CannotIndex { @@ -77,7 +81,11 @@ impl AbstractTree for Index { } } else { Err(RuntimeError::ValidationFailure( - ValidationError::CannotIndexWith(left_value.r#type(), right_value.r#type()), + ValidationError::CannotIndexWith { + collection_type: todo!(), + index_type: todo!(), + position: todo!(), + }, )) } } diff --git a/src/abstract_tree/map_index.rs b/src/abstract_tree/map_index.rs new file mode 100644 index 0000000..0d5ce51 --- /dev/null +++ b/src/abstract_tree/map_index.rs @@ -0,0 +1,122 @@ +use crate::{ + context::Context, + error::{RuntimeError, ValidationError}, + value::ValueInner, +}; + +use super::{AbstractTree, Action, Expression, Type, ValueNode, WithPosition}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct MapIndex { + left: WithPosition, + right: WithPosition, +} + +impl MapIndex { + pub fn new(left: WithPosition, right: WithPosition) -> Self { + Self { left, right } + } +} + +impl AbstractTree for MapIndex { + fn expected_type(&self, _context: &Context) -> Result { + let left_type = self.left.node.expected_type(_context)?; + + if let ( + Expression::Identifier(collection_identifier), + Expression::Identifier(index_identifier), + ) = (&self.left.node, &self.right.node) + { + let collection = if let Some(collection) = _context.get_value(collection_identifier)? { + collection + } else { + return Err(ValidationError::VariableNotFound( + collection_identifier.clone(), + )); + }; + + if let ValueInner::Map(map) = collection.inner().as_ref() { + return if let Some(value) = map.get(index_identifier) { + Ok(value.r#type()) + } else { + Err(ValidationError::PropertyNotFound( + collection_identifier.clone(), + )) + }; + }; + } + + if let (Expression::Value(ValueNode::Map(properties)), Expression::Identifier(identifier)) = + (&self.left.node, &self.right.node) + { + return if let Some(type_result) = + properties + .iter() + .find_map(|(property, type_option, expression)| { + if property == identifier { + if let Some(r#type) = type_option { + Some(r#type.node.expected_type(_context)) + } else { + Some(expression.node.expected_type(_context)) + } + } else { + None + } + }) + { + type_result + } else { + Ok(Type::None) + }; + } + + Err(ValidationError::CannotIndexWith { + collection_type: left_type, + index_type: self.right.node.expected_type(_context)?, + position: self.right.position, + }) + } + + fn validate(&self, context: &Context) -> Result<(), ValidationError> { + let left_type = self.left.node.expected_type(context)?; + + if let (Expression::Value(ValueNode::Map(_)), Expression::Identifier(_)) = + (&self.left.node, &self.right.node) + { + Ok(()) + } else if let (Expression::Identifier(_), Expression::Identifier(_)) = + (&self.left.node, &self.right.node) + { + Ok(()) + } else { + Err(ValidationError::CannotIndexWith { + collection_type: left_type, + index_type: self.right.node.expected_type(context)?, + position: self.right.position, + }) + } + } + + fn run(self, _context: &Context) -> Result { + let collection = self.left.node.run(_context)?.as_return_value()?; + + if let (ValueInner::Map(map), Expression::Identifier(identifier)) = + (collection.inner().as_ref(), &self.right.node) + { + let action = map + .get(identifier) + .map(|value| Action::Return(value.clone())) + .unwrap_or(Action::None); + + Ok(action) + } else { + Err(RuntimeError::ValidationFailure( + ValidationError::CannotIndexWith { + collection_type: collection.r#type(), + index_type: self.right.node.expected_type(_context)?, + position: self.right.position, + }, + )) + } + } +} diff --git a/src/abstract_tree/mod.rs b/src/abstract_tree/mod.rs index 428c4a9..7fb6a25 100644 --- a/src/abstract_tree/mod.rs +++ b/src/abstract_tree/mod.rs @@ -4,9 +4,10 @@ pub mod expression; pub mod function_call; pub mod identifier; pub mod if_else; -pub mod index; +pub mod list_index; pub mod logic; pub mod r#loop; +pub mod map_index; pub mod math; pub mod statement; pub mod r#type; @@ -22,8 +23,9 @@ pub use self::{ function_call::FunctionCall, identifier::Identifier, if_else::IfElse, - index::Index, + list_index::ListIndex, logic::Logic, + map_index::MapIndex, math::Math, r#loop::Loop, r#type::Type, diff --git a/src/error.rs b/src/error.rs index 750dd66..06f562e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -42,6 +42,8 @@ impl Error { expected => format!("Expected {expected}."), }; + builder = builder.with_note("Parsing error."); + builder.add_label(Label::new(span.0..span.1).with_message(message)); } Error::Lex { expected, span } => { @@ -50,6 +52,8 @@ impl Error { expected => format!("Expected {expected}."), }; + builder = builder.with_note("Lexing error."); + builder.add_label(Label::new(span.0..span.1).with_message(message)); } Error::Runtime { error, position } => match error { @@ -109,10 +113,15 @@ impl Error { Label::new(position.0..position.1) .with_message(format!("Cannot index into a {}.", r#type.fg(type_color))), ), - ValidationError::CannotIndexWith(_, _) => todo!(), + ValidationError::CannotIndexWith { + collection_type, + index_type, + position, + } => todo!(), ValidationError::InterpreterExpectedReturn => todo!(), ValidationError::ExpectedFunction { .. } => todo!(), ValidationError::ExpectedValue => todo!(), + ValidationError::PropertyNotFound(_) => todo!(), }, } @@ -182,7 +191,11 @@ pub enum ValidationError { r#type: Type, position: SourcePosition, }, - CannotIndexWith(Type, Type), + CannotIndexWith { + collection_type: Type, + index_type: Type, + position: SourcePosition, + }, ExpectedBoolean { actual: Type, position: SourcePosition, @@ -206,6 +219,7 @@ pub enum ValidationError { expected_position: SourcePosition, }, VariableNotFound(Identifier), + PropertyNotFound(Identifier), } impl From for ValidationError { diff --git a/src/parser.rs b/src/parser.rs index 1c0f489..e7d2ff3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -49,8 +49,8 @@ pub fn parser<'src>() -> DustParser<'src> { let basic_value = select! { Token::Boolean(boolean) => ValueNode::Boolean(boolean), - Token::Integer(integer) => ValueNode::Integer(integer), Token::Float(float) => ValueNode::Float(float), + Token::Integer(integer) => ValueNode::Integer(integer), Token::String(string) => ValueNode::String(string.to_string()), } .map_with(|value, state| Expression::Value(value).with_position(state.span())) @@ -126,20 +126,18 @@ pub fn parser<'src>() -> DustParser<'src> { Expression::Identifier(identifier).with_position(state.span()) }); - let range = { - let raw_integer = select! { - Token::Integer(integer) => integer - }; - - raw_integer - .clone() - .then_ignore(just(Token::Control(Control::DoubleDot))) - .then(raw_integer) - .map_with(|(start, end), state| { - Expression::Value(ValueNode::Range(start..end)).with_position(state.span()) - }) + let raw_integer = select! { + Token::Integer(integer) => integer }; + let range = raw_integer + .clone() + .then_ignore(just(Token::Control(Control::DoubleDot))) + .then(raw_integer) + .map_with(|(start, end), state| { + Expression::Value(ValueNode::Range(start..end)).with_position(state.span()) + }); + let list = positioned_expression .clone() .separated_by(just(Token::Control(Control::Comma))) @@ -216,6 +214,7 @@ pub fn parser<'src>() -> DustParser<'src> { identifier_expression.clone(), basic_value.clone(), list.clone(), + map.clone(), positioned_expression.clone().delimited_by( just(Token::Control(Control::ParenOpen)), just(Token::Control(Control::ParenClose)), @@ -224,15 +223,27 @@ pub fn parser<'src>() -> DustParser<'src> { use Operator::*; - let logic_math_and_index = atom.pratt(( + let logic_math_and_indexes = atom.pratt(( prefix(2, just(Token::Operator(Not)), |_, expression, span| { Expression::Logic(Box::new(Logic::Not(expression))).with_position(span) }), + postfix( + 2, + positioned_expression.clone().delimited_by( + just(Token::Control(Control::SquareOpen)), + just(Token::Control(Control::SquareClose)), + ), + |op, expression, span| { + Expression::ListIndex(Box::new(ListIndex::new(expression, op))) + .with_position(span) + }, + ), infix( left(3), just(Token::Control(Control::Dot)), |left, _, right, span| { - Expression::Index(Box::new(Index::new(left, right))).with_position(span) + Expression::MapIndex(Box::new(MapIndex::new(left, right))) + .with_position(span) }, ), infix( @@ -333,13 +344,13 @@ pub fn parser<'src>() -> DustParser<'src> { choice(( range, - logic_math_and_index, + logic_math_and_indexes, function, function_call, - identifier_expression, list, map, basic_value, + identifier_expression, )) }); @@ -434,6 +445,29 @@ mod tests { use super::*; + #[test] + fn map_index() { + assert_eq!( + parse(&lex("{ x = 42}.x").unwrap()).unwrap()[0].node, + Statement::Expression(Expression::MapIndex(Box::new(MapIndex::new( + Expression::Value(ValueNode::Map(vec![( + Identifier::new("x"), + None, + Expression::Value(ValueNode::Integer(42)).with_position((6, 8)) + )])) + .with_position((0, 9)), + Expression::Identifier(Identifier::new("x")).with_position((10, 11)) + )))) + ); + assert_eq!( + parse(&lex("foo.x").unwrap()).unwrap()[0].node, + Statement::Expression(Expression::MapIndex(Box::new(MapIndex::new( + Expression::Identifier(Identifier::new("foo")).with_position((0, 3)), + Expression::Identifier(Identifier::new("x")).with_position((4, 5)) + )))) + ); + } + #[test] fn r#while() { assert_eq!( diff --git a/tests/expressions.rs b/tests/expressions.rs index 0f16a98..3c87047 100644 --- a/tests/expressions.rs +++ b/tests/expressions.rs @@ -21,7 +21,16 @@ fn math() { #[test] fn list_index() { assert_eq!( - interpret("foo = [1, 2, 3]; foo.2").unwrap(), + interpret("foo = [1, 2, 3]; foo[2]").unwrap(), + Some(Value::integer(3)) + ); +} + +#[test] +fn map_index() { + assert_eq!(interpret("{ x = 3 }.x").unwrap(), Some(Value::integer(3))); + assert_eq!( + interpret("foo = { x = 3 }; foo.x").unwrap(), Some(Value::integer(3)) ); }