From 929468338dfa508cb9cbdb11e411cca284552de1 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 9 Aug 2024 06:09:59 -0400 Subject: [PATCH] Add map parsing --- dust-lang/src/abstract_tree.rs | 42 ++++++++++-- dust-lang/src/analyzer.rs | 5 ++ dust-lang/src/parse.rs | 120 ++++++++++++++++++++++++--------- dust-lang/src/type.rs | 47 ------------- dust-lang/src/vm.rs | 18 ++++- 5 files changed, 148 insertions(+), 84 deletions(-) diff --git a/dust-lang/src/abstract_tree.rs b/dust-lang/src/abstract_tree.rs index ef9b430..699dae4 100644 --- a/dust-lang/src/abstract_tree.rs +++ b/dust-lang/src/abstract_tree.rs @@ -1,6 +1,6 @@ //! In-memory representation of a Dust program. use std::{ - collections::{HashMap, VecDeque}, + collections::{BTreeMap, HashMap, VecDeque}, fmt::{self, Display, Formatter}, }; @@ -67,6 +67,7 @@ pub enum Statement { // Value collection expressions List(Vec>), + Map(Vec<(Node, Node)>), // Hard-coded values Constant(Value), @@ -83,9 +84,26 @@ impl Statement { Statement::Identifier(identifier) => variables .get(identifier) .map(|value| value.r#type(variables)), - Statement::List(nodes) => nodes - .first() - .and_then(|node| node.inner.expected_type(variables)), + Statement::List(nodes) => { + let item_type = nodes.first().unwrap().inner.expected_type(variables)?; + + Some(Type::List { + length: nodes.len(), + item_type: Box::new(item_type), + }) + } + Statement::Map(nodes) => { + let mut types = BTreeMap::new(); + + for (identifier, item) in nodes { + types.insert( + identifier.inner.clone(), + item.inner.expected_type(variables)?, + ); + } + + Some(Type::Map(types)) + } Statement::PropertyAccess(_, _) => None, } } @@ -181,14 +199,30 @@ impl Display for Statement { Statement::Identifier(identifier) => write!(f, "{identifier}"), Statement::List(nodes) => { write!(f, "[")?; + for (i, node) in nodes.iter().enumerate() { if i > 0 { write!(f, ", ")?; } + write!(f, "{node}")?; } + write!(f, "]") } + Statement::Map(nodes) => { + write!(f, "{{")?; + + for (i, (identifier, node)) in nodes.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + + write!(f, "{identifier} = {node}")?; + } + + write!(f, "}}") + } Statement::PropertyAccess(left, right) => write!(f, "{left}.{right}"), } } diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index 30a0d5b..9aacc7c 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -163,6 +163,11 @@ impl<'a> Analyzer<'a> { self.analyze_node(statement)?; } } + Statement::Map(properties) => { + for (_key, value_node) in properties { + self.analyze_node(value_node)?; + } + } Statement::PropertyAccess(left, right) => { if let Statement::Identifier(_) | Statement::Constant(_) | Statement::List(_) = &left.inner diff --git a/dust-lang/src/parse.rs b/dust-lang/src/parse.rs index 6d3d6da..33b2bf6 100644 --- a/dust-lang/src/parse.rs +++ b/dust-lang/src/parse.rs @@ -4,7 +4,7 @@ //! - `parse` convenience function //! - `Parser` struct, which parses the input a statement at a time use std::{ - collections::VecDeque, + collections::{BTreeMap, VecDeque}, error::Error, fmt::{self, Display, Formatter}, }; @@ -172,10 +172,7 @@ impl<'src> Parser<'src> { let identifier = if let Statement::Identifier(identifier) = left_node.inner { identifier } else { - return Err(ParseError::ExpectedIdentifier { - actual: left_node.inner, - position: left_node.position, - }); + todo!() }; let right_node = self.parse_node(self.current_precedence())?; let right_end = right_node.position.1; @@ -347,6 +344,51 @@ impl<'src> Parser<'src> { Ok(Node::new(Statement::Constant(Value::string(string)), span)) } + (Token::LeftCurlyBrace, left_span) => { + self.next_token()?; + + let mut nodes = Vec::new(); + + loop { + if let (Token::RightCurlyBrace, right_span) = self.current { + self.next_token()?; + + return Ok(Node::new( + Statement::Map(nodes), + (left_span.0, right_span.1), + )); + } + + let identifier = if let (Token::Identifier(text), right_span) = self.current { + self.next_token()?; + + Node::new(Identifier::new(text), right_span) + } else { + return Err(ParseError::ExpectedIdentifier { + actual: self.current.0.to_owned(), + position: self.current.1, + }); + }; + + if let Token::Equal = self.current.0 { + self.next_token()?; + } else { + return Err(ParseError::ExpectedToken { + expected: TokenOwned::Equal, + actual: self.current.0.to_owned(), + position: self.current.1, + }); + } + + let current_value_node = self.parse_node(0)?; + + nodes.push((identifier, current_value_node)); + + if let Token::Comma = self.current.0 { + self.next_token()?; + } + } + } (Token::LeftParenthesis, left_span) => { self.next_token()?; @@ -357,7 +399,8 @@ impl<'src> Parser<'src> { Ok(Node::new(node.inner, (left_span.0, right_span.1))) } else { - Err(ParseError::ExpectedClosingParenthesis { + Err(ParseError::ExpectedToken { + expected: TokenOwned::RightParenthesis, actual: self.current.0.to_owned(), position: self.current.1, }) @@ -387,7 +430,8 @@ impl<'src> Parser<'src> { if let Ok(instruction) = self.parse_node(0) { nodes.push(instruction); } else { - return Err(ParseError::ExpectedClosingSquareBrace { + return Err(ParseError::ExpectedToken { + expected: TokenOwned::RightSquareBrace, actual: self.current.0.to_owned(), position: self.current.1, }); @@ -412,7 +456,8 @@ impl<'src> Parser<'src> { if let (Token::LeftParenthesis, _) = self.current { self.next_token()?; } else { - return Err(ParseError::ExpectedOpeningParenthesis { + return Err(ParseError::ExpectedToken { + expected: TokenOwned::LeftParenthesis, actual: self.current.0.to_owned(), position: self.current.1, }); @@ -438,7 +483,8 @@ impl<'src> Parser<'src> { value_arguments = Some(vec![node]); } } else { - return Err(ParseError::ExpectedClosingParenthesis { + return Err(ParseError::ExpectedToken { + expected: TokenOwned::RightParenthesis, actual: self.current.0.to_owned(), position: self.current.1, }); @@ -480,20 +526,12 @@ pub enum ParseError { error: LexError, position: Span, }, - - ExpectedClosingParenthesis { - actual: TokenOwned, - position: Span, - }, - ExpectedClosingSquareBrace { - actual: TokenOwned, - position: Span, - }, ExpectedIdentifier { - actual: Statement, + actual: TokenOwned, position: (usize, usize), }, - ExpectedOpeningParenthesis { + ExpectedToken { + expected: TokenOwned, actual: TokenOwned, position: Span, }, @@ -507,10 +545,8 @@ impl ParseError { pub fn position(&self) -> Span { match self { Self::LexError { position, .. } => *position, - Self::ExpectedClosingParenthesis { position, .. } => *position, - Self::ExpectedClosingSquareBrace { position, .. } => *position, Self::ExpectedIdentifier { position, .. } => *position, - Self::ExpectedOpeningParenthesis { position, .. } => *position, + Self::ExpectedToken { position, .. } => *position, Self::UnexpectedToken { position, .. } => *position, } } @@ -529,18 +565,12 @@ impl Display for ParseError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Self::LexError { error, .. } => write!(f, "{}", error), - Self::ExpectedClosingParenthesis { actual, .. } => { - write!(f, "Expected closing parenthesis, found {actual}",) - } - Self::ExpectedClosingSquareBrace { actual, .. } => { - write!(f, "Expected closing square brace, found {actual}",) - } Self::ExpectedIdentifier { actual, .. } => { write!(f, "Expected identifier, found {actual}") } - Self::ExpectedOpeningParenthesis { actual, .. } => { - write!(f, "Expected opening parenthesis, found {actual}",) - } + Self::ExpectedToken { + expected, actual, .. + } => write!(f, "Expected token {expected}, found {actual}"), Self::UnexpectedToken { actual, .. } => write!(f, "Unexpected token {actual}"), } } @@ -548,10 +578,36 @@ impl Display for ParseError { #[cfg(test)] mod tests { + use crate::{abstract_tree::BinaryOperator, Identifier}; use super::*; + #[test] + fn map() { + let input = "{ x = 42, y = 'foobar' }"; + + assert_eq!( + parse(input), + Ok(AbstractSyntaxTree { + nodes: [Node::new( + Statement::Map(vec![ + ( + Node::new(Identifier::new("x"), (2, 3)), + Node::new(Statement::Constant(Value::integer(42)), (6, 8)) + ), + ( + Node::new(Identifier::new("y"), (10, 11)), + Node::new(Statement::Constant(Value::string("foobar")), (14, 22)) + ) + ]), + (0, 24) + )] + .into() + }) + ); + } + #[test] fn less_than() { let input = "1 < 2"; diff --git a/dust-lang/src/type.rs b/dust-lang/src/type.rs index b768b4b..3814d11 100644 --- a/dust-lang/src/type.rs +++ b/dust-lang/src/type.rs @@ -51,7 +51,6 @@ pub enum Type { length: usize, item_type: Box, }, - ListOf(Box), Map(BTreeMap), Range, String, @@ -111,11 +110,6 @@ impl Type { } } } - (Type::ListOf(left), Type::ListOf(right)) => { - if left.check(right).is_ok() { - return Ok(()); - } - } ( Type::Structure { name: left_name, @@ -160,29 +154,6 @@ impl Type { return Ok(()); } - ( - Type::ListOf(left_type), - Type::List { - item_type: right_type, - .. - }, - ) - | ( - Type::List { - item_type: right_type, - .. - }, - Type::ListOf(left_type), - ) => { - if right_type.check(left_type).is_err() { - return Err(TypeConflict { - actual: other.clone(), - expected: self.clone(), - }); - } else { - return Ok(()); - } - } ( Type::Function { type_parameters: left_type_parameters, @@ -274,7 +245,6 @@ impl Display for Type { } Type::Integer => write!(f, "int"), Type::List { length, item_type } => write!(f, "[{length}; {}]", item_type), - Type::ListOf(item_type) => write!(f, "[{}]", item_type), Type::Map(map) => { write!(f, "{{ ")?; @@ -349,10 +319,6 @@ mod tests { }), Ok(()) ); - assert_eq!( - Type::ListOf(Box::new(Type::Integer)).check(&Type::ListOf(Box::new(Type::Integer))), - Ok(()) - ); let mut map = BTreeMap::new(); @@ -393,7 +359,6 @@ mod tests { length: 10, item_type: Box::new(Type::Integer), }, - Type::ListOf(Box::new(Type::Boolean)), Type::Map(BTreeMap::new()), Type::Range, Type::String, @@ -415,16 +380,4 @@ mod tests { } } } - - #[test] - fn check_list_types() { - let list = Type::List { - length: 42, - item_type: Box::new(Type::Integer), - }; - let list_of = Type::ListOf(Box::new(Type::Integer)); - - assert_eq!(list.check(&list_of), Ok(())); - assert_eq!(list_of.check(&list), Ok(())); - } } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index c5934f0..05c6447 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -1,6 +1,6 @@ //! Virtual machine for running the abstract syntax tree. use std::{ - collections::HashMap, + collections::{BTreeMap, HashMap}, error::Error, fmt::{self, Display, Formatter}, }; @@ -216,6 +216,22 @@ impl Vm { Ok(Some(Value::list(values))) } + Statement::Map(nodes) => { + let mut values = BTreeMap::new(); + + for (identifier, value_node) in nodes { + let position = value_node.position; + let value = if let Some(value) = self.run_node(value_node, variables)? { + value + } else { + return Err(VmError::ExpectedValue { position }); + }; + + values.insert(identifier.inner, value); + } + + Ok(Some(Value::map(values))) + } Statement::PropertyAccess(left, right) => { let left_span = left.position; let left_value = if let Some(value) = self.run_node(*left, variables)? {