From 049790726aeb4baf5e7bf38d0fb39222f6ac785c Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 13 Aug 2024 15:12:32 -0400 Subject: [PATCH] Add parsing for tuple structs --- dust-lang/src/abstract_tree.rs | 21 +++++- dust-lang/src/analyzer.rs | 3 + dust-lang/src/parser.rs | 114 +++++++++++++++++++++++++++++++-- dust-lang/src/value.rs | 2 +- dust-lang/src/vm.rs | 19 ++++++ 5 files changed, 150 insertions(+), 9 deletions(-) diff --git a/dust-lang/src/abstract_tree.rs b/dust-lang/src/abstract_tree.rs index 055a66f..1888160 100644 --- a/dust-lang/src/abstract_tree.rs +++ b/dust-lang/src/abstract_tree.rs @@ -442,13 +442,32 @@ pub enum UnaryOperator { #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub enum StructDefinition { - Unit { name: Node }, + Unit { + name: Node, + }, + Tuple { + name: Node, + fields: Vec>, + }, } impl Display for StructDefinition { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { StructDefinition::Unit { name } => write!(f, "struct {name}"), + StructDefinition::Tuple { name, fields } => { + write!(f, "struct {name} {{")?; + + for (i, field) in fields.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + + write!(f, "{field}")?; + } + + write!(f, "}}") + } } } } diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index ca3cbea..1dc2385 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -429,6 +429,9 @@ impl<'a> Analyzer<'a> { name: name.inner.clone(), }), ), + StructDefinition::Tuple { name, fields } => { + todo!() + } }; self.context.set_type(name, r#type, node.position); diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index f5b2667..61a657c 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -13,7 +13,8 @@ use std::{ use crate::{ AbstractSyntaxTree, BinaryOperator, BuiltInFunction, DustError, Identifier, LexError, Lexer, - Node, Span, Statement, StructDefinition, Token, TokenKind, TokenOwned, UnaryOperator, Value, + Node, Span, Statement, StructDefinition, Token, TokenKind, TokenOwned, Type, UnaryOperator, + Value, }; /// Parses the input into an abstract syntax tree. @@ -575,16 +576,50 @@ impl<'src> Parser<'src> { (Token::Struct, left_position) => { self.next_token()?; - if let Token::Identifier(_) = self.current.0 { - let name = self.parse_identifier()?; - let name_end = name.position.1; + let (name, name_end) = if let Token::Identifier(_) = self.current.0 { + let position = self.current.1 .1; + (self.parse_identifier()?, position) + } else { + return Err(ParseError::ExpectedToken { + expected: TokenKind::Identifier, + actual: self.current.0.to_owned(), + position: self.current.1, + }); + }; + + if let Token::LeftParenthesis = self.current.0 { + self.next_token()?; + + let mut types = Vec::new(); + + loop { + if let (Token::RightParenthesis, right_position) = self.current { + self.next_token()?; + + return Ok(Node::new( + Statement::StructDefinition(StructDefinition::Tuple { + name, + fields: types, + }), + (left_position.0, right_position.1), + )); + } + + if let (Token::Comma, _) = self.current { + self.next_token()?; + continue; + } + + let type_node = self.parse_type()?; + + types.push(type_node); + } + } else { Ok(Node::new( Statement::StructDefinition(StructDefinition::Unit { name }), (left_position.0, name_end), )) - } else { - todo!() } } (Token::While, left_position) => { @@ -826,6 +861,26 @@ impl<'src> Parser<'src> { statements.push(statement); } } + + fn parse_type(&mut self) -> Result, ParseError> { + let r#type = match self.current.0 { + Token::Bool => Type::Boolean, + Token::FloatKeyword => Type::Float, + Token::Int => Type::Integer, + _ => { + return Err(ParseError::ExpectedTokenMultiple { + expected: vec![TokenKind::Bool, TokenKind::FloatKeyword, TokenKind::Int], + actual: self.current.0.to_owned(), + position: self.current.1, + }); + } + }; + let position = self.current.1; + + self.next_token()?; + + Ok(Node::new(r#type, position)) + } } #[derive(Debug, PartialEq, Clone)] @@ -847,6 +902,11 @@ pub enum ParseError { actual: TokenOwned, position: Span, }, + ExpectedTokenMultiple { + expected: Vec, + actual: TokenOwned, + position: Span, + }, UnexpectedToken { actual: TokenOwned, position: Span, @@ -874,6 +934,7 @@ impl ParseError { ParseError::ExpectedAssignment { actual } => actual.position, ParseError::ExpectedIdentifier { position, .. } => *position, ParseError::ExpectedToken { position, .. } => *position, + ParseError::ExpectedTokenMultiple { position, .. } => *position, ParseError::FloatError { position, .. } => *position, ParseError::IntegerError { position, .. } => *position, ParseError::LexError(error) => error.position(), @@ -902,6 +963,23 @@ impl Display for ParseError { Self::ExpectedToken { expected, actual, .. } => write!(f, "Expected token {expected}, found {actual}"), + Self::ExpectedTokenMultiple { + expected, actual, .. + } => { + write!(f, "Expected one of")?; + + for (i, token_kind) in expected.iter().enumerate() { + if i == 0 { + write!(f, " {token_kind}")?; + } else if i == expected.len() - 1 { + write!(f, " or {token_kind}")?; + } else { + write!(f, ", {token_kind}")?; + } + } + + write!(f, ", found {actual}") + } Self::FloatError { error, .. } => write!(f, "{}", error), Self::IntegerError { error, .. } => write!(f, "{}", error), Self::LexError(error) => write!(f, "{}", error), @@ -912,10 +990,32 @@ impl Display for ParseError { #[cfg(test)] mod tests { - use crate::{BinaryOperator, Identifier, StructDefinition, UnaryOperator}; + use crate::{BinaryOperator, Identifier, StructDefinition, Type, UnaryOperator}; use super::*; + #[test] + fn tuple_struct() { + let input = "struct Foo(int, float)"; + + assert_eq!( + parse(input), + Ok(AbstractSyntaxTree { + nodes: [Node::new( + Statement::StructDefinition(StructDefinition::Tuple { + name: Node::new(Identifier::new("Foo"), (7, 10)), + fields: vec![ + Node::new(Type::Integer, (11, 14)), + Node::new(Type::Float, (16, 21)) + ] + }), + (0, 22) + )] + .into() + }) + ); + } + #[test] fn unit_struct() { let input = "struct Foo"; diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 0de3d8a..590deaa 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -14,7 +14,7 @@ use serde::{ Deserialize, Deserializer, Serialize, }; -use crate::{identifier::Identifier, AbstractSyntaxTree, Context, StructType, Type, Vm, VmError}; +use crate::{AbstractSyntaxTree, Context, Identifier, StructType, Type, Vm, VmError}; /// Dust value representation /// diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index a12b537..ded222d 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -592,6 +592,9 @@ impl Vm { name: name.inner.clone(), }), ), + StructDefinition::Tuple { name, fields } => { + todo!() + } }; self.context.set_type(type_name, r#type, node.position); @@ -812,6 +815,22 @@ mod tests { use super::*; + #[test] + fn assign_unit_struct_variable() { + let input = " + struct Foo + x = Foo + x + "; + + assert_eq!( + run(input), + Ok(Some(Value::r#struct(Struct::Unit { + name: Identifier::new("Foo") + }))) + ) + } + #[test] fn define_and_instantiate_unit_struct() { let input = "struct Foo Foo";