Add instantiation for tuple structs

This commit is contained in:
Jeff 2024-08-13 16:21:44 -04:00
parent 049790726a
commit 83aa53b998
7 changed files with 294 additions and 87 deletions

View File

@ -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<Node<Statement>>,
},
// Function calls
// Type definitions
StructDefinition(StructDefinition),
// Function calls and type instantiation
BuiltInFunctionCall {
function: BuiltInFunction,
type_arguments: Option<Vec<Node<Statement>>>,
value_arguments: Option<Vec<Node<Statement>>>,
},
FunctionCall {
function: Box<Node<Statement>>,
Invokation {
invokee: Box<Node<Statement>>,
type_arguments: Option<Vec<Node<Statement>>>,
value_arguments: Option<Vec<Node<Statement>>>,
},
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<Node<Statement>>),
// 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::<Option<Vec<Type>>>()?,
}))
}
},
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<Identifier>,
fields: Vec<Node<Statement>>,
},
}
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, ")")
}
}
}
}

View File

@ -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<Statement>,
},
UndefinedField {
identifier: Node<Statement>,
map: Node<Statement>,
},
UndefinedType {
identifier: Node<Identifier>,
},
UnexpectedIdentifier {
identifier: Node<Statement>,
},
UnexectedString {
actual: Node<Statement>,
},
UndefinedVariable {
identifier: Node<Statement>,
},
}
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)
}

View File

@ -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};

View File

@ -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,7 +762,38 @@ impl<'src> Parser<'src> {
fn parse_postfix(&mut self, left: Node<Statement>) -> Result<Node<Statement>, ParseError> {
let left_start = left.position.0;
let statement = if let Token::LeftSquareBrace = &self.current.0 {
let statement = match &self.current.0 {
Token::LeftParenthesis => {
self.next_token()?;
let mut arguments = Vec::new();
while self.current.0 != Token::RightParenthesis {
let argument = self.parse_statement(0)?;
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()?;
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()?;
@ -788,22 +819,28 @@ impl<'src> Parser<'src> {
Node::new(
Statement::BinaryOperation {
left: Box::new(left),
operator: Node::new(BinaryOperator::ListIndex, (operator_start, operator_end)),
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 {
}
Token::Semicolon => {
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)";

View File

@ -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
)
}
}

View File

@ -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<Type>,
fields: Vec<Type>,
},
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, ", ")?;
}
}

View File

@ -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::<Result<Vec<Value>, 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 = "