From 83aa53b998ef29e062c0f72d095e7c073e7f0f55 Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 13 Aug 2024 16:21:44 -0400 Subject: [PATCH] Add instantiation for tuple structs --- dust-lang/src/abstract_tree.rs | 66 +++++++++++--- dust-lang/src/analyzer.rs | 43 +++++++-- dust-lang/src/lib.rs | 3 +- dust-lang/src/parser.rs | 153 +++++++++++++++++++++++++-------- dust-lang/src/token.rs | 7 +- dust-lang/src/type.rs | 19 ++-- dust-lang/src/vm.rs | 90 ++++++++++++++++--- 7 files changed, 294 insertions(+), 87 deletions(-) diff --git a/dust-lang/src/abstract_tree.rs b/dust-lang/src/abstract_tree.rs index 1888160..0b465ac 100644 --- a/dust-lang/src/abstract_tree.rs +++ b/dust-lang/src/abstract_tree.rs @@ -6,7 +6,7 @@ use std::{ use serde::{Deserialize, Serialize}; -use crate::{BuiltInFunction, Context, Identifier, Span, Type, Value}; +use crate::{BuiltInFunction, Context, Identifier, Span, StructType, Type, Value}; /// In-memory representation of a Dust program. #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] @@ -50,17 +50,21 @@ pub enum Statement { operand: Box>, }, - // Function calls + // Type definitions + StructDefinition(StructDefinition), + + // Function calls and type instantiation BuiltInFunctionCall { function: BuiltInFunction, type_arguments: Option>>, value_arguments: Option>>, }, - FunctionCall { - function: Box>, + Invokation { + invokee: Box>, type_arguments: Option>>, value_arguments: Option>>, }, + StructInstantiation(StructInstantiation), // Loops While { @@ -103,9 +107,6 @@ pub enum Statement { // A statement that always returns None. Created with a semicolon, it causes the preceding // statement to return None. This is analagous to the semicolon or unit type in Rust. Nil(Box>), - - // Type definitions - StructDefinition(StructDefinition), } impl Statement { @@ -159,7 +160,9 @@ impl Statement { }, Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(), Statement::Constant(value) => Some(value.r#type()), - Statement::FunctionCall { function, .. } => function.inner.expected_type(context), + Statement::Invokation { + invokee: function, .. + } => function.inner.expected_type(context), Statement::Identifier(identifier) => context.get_type(identifier), Statement::If { .. } => None, Statement::IfElse { if_body, .. } => if_body.inner.expected_type(context), @@ -190,6 +193,17 @@ impl Statement { UnaryOperator::Not => Some(Type::Boolean), }, Statement::StructDefinition(_) => None, + Statement::StructInstantiation(struct_instantiation) => match struct_instantiation { + StructInstantiation::Tuple { name, fields } => { + Some(Type::Struct(StructType::Tuple { + name: name.inner.clone(), + fields: fields + .iter() + .map(|field| field.inner.expected_type(context)) + .collect::>>()?, + })) + } + }, Statement::While { .. } => None, } } @@ -287,8 +301,8 @@ impl Display for Statement { write!(f, ")") } Statement::Constant(value) => write!(f, "{value}"), - Statement::FunctionCall { - function, + Statement::Invokation { + invokee: function, type_arguments: type_parameters, value_arguments: value_parameters, } => { @@ -398,6 +412,9 @@ impl Display for Statement { Statement::StructDefinition(struct_definition) => { write!(f, "{struct_definition}") } + Statement::StructInstantiation(struct_instantiation) => { + write!(f, "{struct_instantiation}") + } Statement::While { condition, body } => { write!(f, "while {condition} {body}") } @@ -471,3 +488,32 @@ impl Display for StructDefinition { } } } + +#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub enum StructInstantiation { + // The Unit variant is absent because unit structs are instantiated without any fields + Tuple { + name: Node, + fields: Vec>, + }, +} + +impl Display for StructInstantiation { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + StructInstantiation::Tuple { name, fields } => { + write!(f, "{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 1dc2385..c256feb 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -10,7 +10,7 @@ use std::{ }; use crate::{ - abstract_tree::{BinaryOperator, UnaryOperator}, + abstract_tree::{BinaryOperator, StructInstantiation, UnaryOperator}, parse, AbstractSyntaxTree, Context, DustError, Identifier, Node, Span, Statement, StructDefinition, StructType, Type, }; @@ -290,8 +290,8 @@ impl<'a> Analyzer<'a> { } } Statement::Constant(_) => {} - Statement::FunctionCall { - function, + Statement::Invokation { + invokee: function, value_arguments, .. } => { @@ -429,13 +429,31 @@ impl<'a> Analyzer<'a> { name: name.inner.clone(), }), ), - StructDefinition::Tuple { name, fields } => { - todo!() - } + StructDefinition::Tuple { name, fields } => ( + name.inner.clone(), + Type::Struct(StructType::Tuple { + name: name.inner.clone(), + fields: fields + .iter() + .map(|type_node| type_node.inner.clone()) + .collect(), + }), + ), }; self.context.set_type(name, r#type, node.position); } + Statement::StructInstantiation(struct_instantiation) => { + let name = match struct_instantiation { + StructInstantiation::Tuple { name, .. } => name, + }; + + if self.context.get_type(&name.inner).is_none() { + return Err(AnalyzerError::UndefinedType { + identifier: name.clone(), + }); + } + } Statement::UnaryOperation { operator, operand } => { self.analyze_statement(operand)?; @@ -517,19 +535,22 @@ pub enum AnalyzerError { actual_type: Type, expected: Type, }, - UndefinedVariable { - identifier: Node, - }, UndefinedField { identifier: Node, map: Node, }, + UndefinedType { + identifier: Node, + }, UnexpectedIdentifier { identifier: Node, }, UnexectedString { actual: Node, }, + UndefinedVariable { + identifier: Node, + }, } impl AnalyzerError { @@ -550,6 +571,7 @@ impl AnalyzerError { actual_statement, .. } => actual_statement.position, AnalyzerError::UndefinedField { identifier, .. } => identifier.position, + AnalyzerError::UndefinedType { identifier } => identifier.position, AnalyzerError::UndefinedVariable { identifier } => identifier.position, AnalyzerError::UnexpectedIdentifier { identifier } => identifier.position, AnalyzerError::UnexectedString { actual } => actual.position, @@ -606,6 +628,9 @@ impl Display for AnalyzerError { AnalyzerError::UndefinedField { identifier, map } => { write!(f, "Undefined field {} in map {}", identifier, map) } + AnalyzerError::UndefinedType { identifier } => { + write!(f, "Undefined type {}", identifier) + } AnalyzerError::UndefinedVariable { identifier } => { write!(f, "Undefined variable {}", identifier) } diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index cc5f3d7..af55715 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -15,7 +15,8 @@ pub mod value; pub mod vm; pub use abstract_tree::{ - AbstractSyntaxTree, BinaryOperator, Node, Statement, StructDefinition, UnaryOperator, + AbstractSyntaxTree, BinaryOperator, Node, Statement, StructDefinition, StructInstantiation, + UnaryOperator, }; pub use analyzer::{analyze, Analyzer, AnalyzerError}; pub use built_in_function::{BuiltInFunction, BuiltInFunctionError}; diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 61a657c..ed8dcab 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -686,8 +686,8 @@ impl<'src> Parser<'src> { )); } - if let Statement::FunctionCall { - function, + if let Statement::Invokation { + invokee: function, type_arguments, value_arguments, } = right.inner @@ -701,8 +701,8 @@ impl<'src> Parser<'src> { }; return Ok(Node::new( - Statement::FunctionCall { - function, + Statement::Invokation { + invokee: function, type_arguments, value_arguments, }, @@ -762,48 +762,85 @@ impl<'src> Parser<'src> { fn parse_postfix(&mut self, left: Node) -> Result, ParseError> { let left_start = left.position.0; - let statement = if let Token::LeftSquareBrace = &self.current.0 { - let operator_start = self.current.1 .0; + let statement = match &self.current.0 { + Token::LeftParenthesis => { + self.next_token()?; - self.next_token()?; + let mut arguments = Vec::new(); - let index = self.parse_statement(0)?; + while self.current.0 != Token::RightParenthesis { + let argument = self.parse_statement(0)?; - let operator_end = if let Token::RightSquareBrace = self.current.0 { - let end = self.current.1 .1; + arguments.push(argument); + + if let Token::Comma = self.current.0 { + self.next_token()?; + } else { + break; + } + } + + let right_end = self.current.1 .1; self.next_token()?; - end - } else { - return Err(ParseError::ExpectedToken { - expected: TokenKind::RightSquareBrace, + Node::new( + Statement::Invokation { + invokee: Box::new(left), + type_arguments: None, + value_arguments: Some(arguments), + }, + (left_start, right_end), + ) + } + Token::LeftSquareBrace => { + let operator_start = self.current.1 .0; + + self.next_token()?; + + let index = self.parse_statement(0)?; + + let operator_end = if let Token::RightSquareBrace = self.current.0 { + let end = self.current.1 .1; + + self.next_token()?; + + end + } else { + return Err(ParseError::ExpectedToken { + expected: TokenKind::RightSquareBrace, + actual: self.current.0.to_owned(), + position: self.current.1, + }); + }; + + let right_end = self.current.1 .1; + + Node::new( + Statement::BinaryOperation { + left: Box::new(left), + operator: Node::new( + BinaryOperator::ListIndex, + (operator_start, operator_end), + ), + right: Box::new(index), + }, + (left_start, right_end), + ) + } + Token::Semicolon => { + let operator_end = self.current.1 .1; + + self.next_token()?; + + Node::new(Statement::Nil(Box::new(left)), (left_start, operator_end)) + } + _ => { + return Err(ParseError::UnexpectedToken { actual: self.current.0.to_owned(), position: self.current.1, }); - }; - - let right_end = self.current.1 .1; - - Node::new( - Statement::BinaryOperation { - left: Box::new(left), - operator: Node::new(BinaryOperator::ListIndex, (operator_start, operator_end)), - right: Box::new(index), - }, - (left_start, right_end), - ) - } else if let Token::Semicolon = &self.current.0 { - let operator_end = self.current.1 .1; - - self.next_token()?; - - Node::new(Statement::Nil(Box::new(left)), (left_start, operator_end)) - } else { - return Err(ParseError::UnexpectedToken { - actual: self.current.0.to_owned(), - position: self.current.1, - }); + } }; if self.current.0.is_postfix() { @@ -990,10 +1027,50 @@ impl Display for ParseError { #[cfg(test)] mod tests { - use crate::{BinaryOperator, Identifier, StructDefinition, Type, UnaryOperator}; + use crate::{ + BinaryOperator, Identifier, StructDefinition, StructInstantiation, Type, UnaryOperator, + }; use super::*; + #[test] + fn tuple_struct_instantiation() { + let input = "struct Foo(int, float) Foo(1, 2.0)"; + + 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) + ), + Node::new( + Statement::Invokation { + invokee: Box::new(Node::new( + Statement::Identifier(Identifier::new("Foo")), + (23, 26) + )), + type_arguments: None, + value_arguments: Some(vec![ + Node::new(Statement::Constant(Value::integer(1)), (27, 28)), + Node::new(Statement::Constant(Value::float(2.0)), (30, 33)) + ]) + }, + (23, 34) + ) + ] + .into() + }) + ); + } + #[test] fn tuple_struct() { let input = "struct Foo(int, float)"; diff --git a/dust-lang/src/token.rs b/dust-lang/src/token.rs index ceabe91..ecd1c7a 100644 --- a/dust-lang/src/token.rs +++ b/dust-lang/src/token.rs @@ -223,7 +223,7 @@ impl<'src> Token<'src> { pub fn precedence(&self) -> u8 { match self { Token::Dot => 10, - Token::LeftSquareBrace => 9, + Token::LeftParenthesis | Token::LeftSquareBrace => 9, Token::Star | Token::Slash | Token::Percent => 8, Token::Minus | Token::Plus => 6, Token::DoubleEqual @@ -261,7 +261,10 @@ impl<'src> Token<'src> { } pub fn is_postfix(&self) -> bool { - matches!(self, Token::LeftSquareBrace | Token::Semicolon) + matches!( + self, + Token::LeftParenthesis | Token::LeftSquareBrace | Token::Semicolon + ) } } diff --git a/dust-lang/src/type.rs b/dust-lang/src/type.rs index 6a6b6e0..1fdb90e 100644 --- a/dust-lang/src/type.rs +++ b/dust-lang/src/type.rs @@ -16,7 +16,7 @@ use std::{ use serde::{Deserialize, Serialize}; -use crate::{Identifier, Struct}; +use crate::Identifier; #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct TypeConflict { @@ -344,7 +344,7 @@ pub enum StructType { }, Tuple { name: Identifier, - types: Vec, + fields: Vec, }, Fields { name: Identifier, @@ -352,26 +352,17 @@ pub enum StructType { }, } -impl StructType { - pub fn instantiate(&self) -> Struct { - match self { - StructType::Unit { name } => Struct::Unit { name: name.clone() }, - _ => todo!(), - } - } -} - impl Display for StructType { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { StructType::Unit { name } => write!(f, "struct {name}"), - StructType::Tuple { name, types } => { + StructType::Tuple { name, fields } => { write!(f, "struct {name}(")?; - for (index, r#type) in types.iter().enumerate() { + for (index, r#type) in fields.iter().enumerate() { write!(f, "{type}")?; - if index != types.len() - 1 { + if index != fields.len() - 1 { write!(f, ", ")?; } } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index ded222d..662d1ad 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -11,8 +11,8 @@ use std::{ use crate::{ parse, value::ValueInner, AbstractSyntaxTree, Analyzer, BinaryOperator, BuiltInFunctionError, - Context, DustError, Identifier, Node, ParseError, Span, Statement, StructDefinition, - StructType, Type, UnaryOperator, Value, ValueError, + Context, DustError, Identifier, Node, ParseError, Span, Statement, Struct, StructDefinition, + StructInstantiation, StructType, Type, UnaryOperator, Value, ValueError, }; /// Run the source code and return the result. @@ -329,17 +329,43 @@ impl Vm { Ok(function_call_return) } Statement::Constant(value) => Ok(Some(value.clone())), - Statement::FunctionCall { - function: function_node, + Statement::Invokation { + invokee, type_arguments: _, value_arguments: value_parameter_nodes, } => { - let function_position = function_node.position; - let function_value = if let Some(value) = self.run_statement(*function_node)? { + let invokee_position = invokee.position; + let invokee_type = invokee.inner.expected_type(&self.context); + + if let Some(Type::Struct(StructType::Tuple { name, .. })) = invokee_type { + let mut fields = Vec::new(); + + if let Some(value_parameter_nodes) = value_parameter_nodes { + for statement in value_parameter_nodes { + let position = statement.position; + let value = if let Some(value) = self.run_statement(statement)? { + value + } else { + return Err(VmError::ExpectedValue { position }); + }; + + fields.push(value); + } + } + + let struct_value = Value::r#struct(Struct::Tuple { + name: name.clone(), + fields, + }); + + return Ok(Some(struct_value)); + } + + let function_value = if let Some(value) = self.run_statement(*invokee)? { value } else { return Err(VmError::ExpectedValue { - position: function_position, + position: invokee_position, }); }; let function = if let Some(function) = function_value.as_function() { @@ -347,7 +373,7 @@ impl Vm { } else { return Err(VmError::ExpectedFunction { actual: function_value, - position: function_position, + position: invokee_position, }); }; @@ -385,8 +411,8 @@ impl Vm { println!("{type_option:?}"); - if let Some(Type::Struct(struct_type)) = type_option { - return Ok(Some(Value::r#struct(struct_type.instantiate()))); + if let Some(Type::Struct(StructType::Unit { name })) = type_option { + return Ok(Some(Value::r#struct(Struct::Unit { name }))); } Err(VmError::UndefinedVariable { @@ -592,15 +618,40 @@ impl Vm { name: name.inner.clone(), }), ), - StructDefinition::Tuple { name, fields } => { - todo!() - } + 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::StructInstantiation(struct_instantiation) => match struct_instantiation { + StructInstantiation::Tuple { name, fields } => { + Ok(Some(Value::r#struct(Struct::Tuple { + name: name.inner, + fields: fields + .into_iter() + .map(|node| { + let position = node.position; + if let Some(value) = self.run_statement(node)? { + Ok(value) + } else { + Err(VmError::ExpectedValue { position }) + } + }) + .collect::, VmError>>()?, + }))) + } + }, Statement::UnaryOperation { operator, operand } => { let position = operand.position; let value = if let Some(value) = self.run_statement(*operand)? { @@ -815,6 +866,19 @@ mod tests { use super::*; + #[test] + fn define_and_instantiate_tuple_struct() { + let input = "struct Foo(int) Foo(42)"; + + assert_eq!( + run(input), + Ok(Some(Value::r#struct(Struct::Tuple { + name: Identifier::new("Foo"), + fields: vec![Value::integer(42)] + }))) + ); + } + #[test] fn assign_unit_struct_variable() { let input = "