diff --git a/dust-lang/src/abstract_tree.rs b/dust-lang/src/abstract_tree.rs index 2700e47..fcad545 100644 --- a/dust-lang/src/abstract_tree.rs +++ b/dust-lang/src/abstract_tree.rs @@ -491,7 +491,11 @@ pub enum StructDefinition { }, Tuple { name: Node, - fields: Vec>, + items: Vec>, + }, + Fields { + name: Node, + fields: Vec<(Node, Node)>, }, } @@ -499,7 +503,10 @@ 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 } => { + StructDefinition::Tuple { + name, + items: fields, + } => { write!(f, "struct {name} {{")?; for (i, field) in fields.iter().enumerate() { @@ -510,6 +517,19 @@ impl Display for StructDefinition { write!(f, "{field}")?; } + write!(f, "}}") + } + StructDefinition::Fields { name, fields } => { + write!(f, "struct {name} {{")?; + + for (i, (field_name, field_type)) in fields.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + + write!(f, "{field_name}: {field_type}")?; + } + write!(f, "}}") } } diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index 9049fd3..ff71613 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -307,7 +307,7 @@ impl<'a> Analyzer<'a> { if let Some(Type::Struct(struct_type)) = invokee_type { match struct_type { - StructType::Unit { name } => todo!(), + StructType::Unit { .. } => todo!(), StructType::Tuple { fields, .. } => { for (expected_type, argument) in fields.iter().zip(arguments.iter()) { @@ -328,7 +328,7 @@ impl<'a> Analyzer<'a> { } } } - StructType::Fields { name, fields } => todo!(), + StructType::Fields { .. } => todo!(), } } } @@ -459,7 +459,10 @@ impl<'a> Analyzer<'a> { name: name.inner.clone(), }), ), - StructDefinition::Tuple { name, fields } => ( + StructDefinition::Tuple { + name, + items: fields, + } => ( name.inner.clone(), Type::Struct(StructType::Tuple { name: name.inner.clone(), @@ -469,6 +472,18 @@ impl<'a> Analyzer<'a> { .collect(), }), ), + StructDefinition::Fields { name, fields } => ( + name.inner.clone(), + Type::Struct(StructType::Fields { + name: name.inner.clone(), + fields: fields + .iter() + .map(|(identifier, r#type)| { + (identifier.inner.clone(), r#type.inner.clone()) + }) + .collect(), + }), + ), }; self.context.set_type(name, r#type, node.position); diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 06b6b56..58f6ebd 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -580,7 +580,7 @@ impl<'src> Parser<'src> { return Ok(Node::new( Statement::StructDefinition(StructDefinition::Tuple { name, - fields: types, + items: types, }), (left_position.0, right_position.1), )); @@ -595,12 +595,53 @@ impl<'src> Parser<'src> { types.push(type_node); } - } else { - Ok(Node::new( - Statement::StructDefinition(StructDefinition::Unit { name }), - (left_position.0, name_end), - )) } + + if let Token::LeftCurlyBrace = self.current.0 { + self.next_token()?; + + let mut fields = Vec::new(); + + loop { + if let (Token::RightCurlyBrace, right_position) = self.current { + self.next_token()?; + + return Ok(Node::new( + Statement::StructDefinition(StructDefinition::Fields { + name, + fields, + }), + (left_position.0, right_position.1), + )); + } + + if let (Token::Comma, _) = self.current { + self.next_token()?; + continue; + } + + let field_name = self.parse_identifier()?; + + if let (Token::Colon, _) = self.current { + self.next_token()?; + } else { + return Err(ParseError::ExpectedToken { + expected: TokenKind::Colon, + actual: self.current.0.to_owned(), + position: self.current.1, + }); + } + + let field_type = self.parse_type()?; + + fields.push((field_name, field_type)); + } + } + + Ok(Node::new( + Statement::StructDefinition(StructDefinition::Unit { name }), + (left_position.0, name_end), + )) } (Token::While, left_position) => { self.next_token()?; @@ -1042,6 +1083,34 @@ mod tests { use super::*; + #[test] + fn fields_struct() { + let input = "struct Foo { a: int, b: float }"; + + assert_eq!( + parse(input), + Ok(AbstractSyntaxTree { + nodes: [Node::new( + Statement::StructDefinition(StructDefinition::Fields { + name: Node::new(Identifier::new("Foo"), (7, 10)), + fields: vec![ + ( + Node::new(Identifier::new("a"), (13, 14)), + Node::new(Type::Integer, (16, 19)) + ), + ( + Node::new(Identifier::new("b"), (21, 22)), + Node::new(Type::Float, (24, 29)) + ) + ] + }), + (0, 31) + )] + .into() + }) + ); + } + #[test] fn tuple_struct_instantiation() { let input = "struct Foo(int, float) Foo(1, 2.0)"; @@ -1053,7 +1122,7 @@ mod tests { Node::new( Statement::StructDefinition(StructDefinition::Tuple { name: Node::new(Identifier::new("Foo"), (7, 10)), - fields: vec![ + items: vec![ Node::new(Type::Integer, (11, 14)), Node::new(Type::Float, (16, 21)) ] @@ -1090,7 +1159,7 @@ mod tests { nodes: [Node::new( Statement::StructDefinition(StructDefinition::Tuple { name: Node::new(Identifier::new("Foo"), (7, 10)), - fields: vec![ + items: vec![ Node::new(Type::Integer, (11, 14)), Node::new(Type::Float, (16, 21)) ] diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 29481f9..f30e754 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -599,30 +599,7 @@ impl Vm { Ok(None) } - Statement::StructDefinition(struct_definition) => { - let (type_name, r#type) = match struct_definition { - StructDefinition::Unit { name } => ( - name.inner.clone(), - Type::Struct(StructType::Unit { - name: name.inner.clone(), - }), - ), - StructDefinition::Tuple { name, fields } => ( - name.inner.clone(), - Type::Struct(StructType::Tuple { - name: name.inner.clone(), - fields: fields - .into_iter() - .map(|type_node| type_node.inner) - .collect(), - }), - ), - }; - - self.context.set_type(type_name, r#type, node.position); - - Ok(None) - } + Statement::StructDefinition(_) => Ok(None), Statement::StructInstantiation(struct_instantiation) => match struct_instantiation { StructInstantiation::Tuple { name, fields } => { Ok(Some(Value::r#struct(Struct::Tuple {