1
0

Implement fields struct instantiation

This commit is contained in:
Jeff 2024-08-13 19:41:36 -04:00
parent b55a79d6bf
commit 5c8e72a6f7
5 changed files with 188 additions and 88 deletions

View File

@ -6,7 +6,7 @@ use std::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{BuiltInFunction, Context, Identifier, Span, StructType, Type, Value}; use crate::{BuiltInFunction, Context, Identifier, Span, Type, Value};
/// In-memory representation of a Dust program. /// In-memory representation of a Dust program.
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
@ -71,7 +71,10 @@ pub enum Statement {
type_arguments: Option<Vec<Node<Statement>>>, type_arguments: Option<Vec<Node<Statement>>>,
value_arguments: Option<Vec<Node<Statement>>>, value_arguments: Option<Vec<Node<Statement>>>,
}, },
StructInstantiation(StructInstantiation), FieldsStructInstantiation {
name: Node<Identifier>,
fields: Vec<(Node<Identifier>, Node<Statement>)>,
},
// Loops // Loops
While { While {
@ -169,6 +172,7 @@ impl Statement {
}, },
Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(), Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(),
Statement::Constant(value) => Some(value.r#type()), Statement::Constant(value) => Some(value.r#type()),
Statement::FieldsStructInstantiation { name, .. } => context.get_type(&name.inner),
Statement::Invokation { Statement::Invokation {
invokee: function, .. invokee: function, ..
} => function.inner.expected_type(context), } => function.inner.expected_type(context),
@ -200,17 +204,6 @@ impl Statement {
UnaryOperator::Not => Some(Type::Boolean), UnaryOperator::Not => Some(Type::Boolean),
}, },
Statement::StructDefinition(_) => None, 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, Statement::While { .. } => None,
} }
} }
@ -313,6 +306,19 @@ impl Display for Statement {
write!(f, ")") write!(f, ")")
} }
Statement::Constant(value) => write!(f, "{value}"), Statement::Constant(value) => write!(f, "{value}"),
Statement::FieldsStructInstantiation { name, fields } => {
write!(f, "{name} {{ ")?;
for (i, (identifier, value)) in fields.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{identifier}: {value}")?;
}
write!(f, " }}")
}
Statement::Invokation { Statement::Invokation {
invokee: function, invokee: function,
type_arguments: type_parameters, type_arguments: type_parameters,
@ -424,9 +430,6 @@ impl Display for Statement {
Statement::StructDefinition(struct_definition) => { Statement::StructDefinition(struct_definition) => {
write!(f, "{struct_definition}") write!(f, "{struct_definition}")
} }
Statement::StructInstantiation(struct_instantiation) => {
write!(f, "{struct_instantiation}")
}
Statement::While { condition, body } => { Statement::While { condition, body } => {
write!(f, "while {condition} {body}") write!(f, "while {condition} {body}")
} }
@ -535,32 +538,3 @@ 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::{ use crate::{
abstract_tree::{BinaryOperator, StructInstantiation, UnaryOperator}, abstract_tree::{BinaryOperator, UnaryOperator},
parse, AbstractSyntaxTree, Context, DustError, Identifier, Node, Span, Statement, parse, AbstractSyntaxTree, Context, DustError, Identifier, Node, Span, Statement,
StructDefinition, StructType, Type, StructDefinition, StructType, Type,
}; };
@ -291,6 +291,34 @@ impl<'a> Analyzer<'a> {
} }
} }
Statement::Constant(_) => {} Statement::Constant(_) => {}
Statement::FieldsStructInstantiation {
name,
fields: field_arguments,
} => {
let expected_type = self.context.get_type(&name.inner);
if let Some(Type::Struct(StructType::Fields { fields, .. })) = expected_type {
for ((_, expected_type), (_, argument)) in
fields.iter().zip(field_arguments.iter())
{
let actual_type = argument.inner.expected_type(self.context);
if let Some(actual_type) = actual_type {
expected_type.check(&actual_type).map_err(|conflict| {
AnalyzerError::TypeConflict {
actual_statement: argument.clone(),
actual_type: conflict.actual,
expected: conflict.expected,
}
})?;
} else {
return Err(AnalyzerError::ExpectedValue {
actual: argument.clone(),
});
}
}
}
}
Statement::Invokation { Statement::Invokation {
invokee, invokee,
value_arguments, value_arguments,
@ -488,17 +516,6 @@ impl<'a> Analyzer<'a> {
self.context.set_type(name, r#type, node.position); 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 } => { Statement::UnaryOperation { operator, operand } => {
self.analyze_statement(operand)?; self.analyze_statement(operand)?;

View File

@ -30,7 +30,7 @@ pub mod vm;
pub use abstract_tree::{ pub use abstract_tree::{
AbstractSyntaxTree, AssignmentOperator, BinaryOperator, Node, Statement, StructDefinition, AbstractSyntaxTree, AssignmentOperator, BinaryOperator, Node, Statement, StructDefinition,
StructInstantiation, UnaryOperator, UnaryOperator,
}; };
pub use analyzer::{analyze, Analyzer, AnalyzerError}; pub use analyzer::{analyze, Analyzer, AnalyzerError};
pub use built_in_function::{BuiltInFunction, BuiltInFunctionError}; pub use built_in_function::{BuiltInFunction, BuiltInFunctionError};

View File

@ -100,6 +100,7 @@ pub struct Parser<'src> {
source: &'src str, source: &'src str,
lexer: Lexer, lexer: Lexer,
current: (Token<'src>, Span), current: (Token<'src>, Span),
context: ParserContext,
} }
impl<'src> Parser<'src> { impl<'src> Parser<'src> {
@ -111,6 +112,7 @@ impl<'src> Parser<'src> {
source, source,
lexer, lexer,
current, current,
context: ParserContext::None,
} }
} }
@ -218,6 +220,63 @@ impl<'src> Parser<'src> {
position, position,
)) ))
} }
(Token::Identifier(text), position) => {
self.next_token()?;
if let ParserContext::IfElseStatement = self.context {
return Ok(Node::new(
Statement::Identifier(Identifier::new(text)),
position,
));
}
if let Token::LeftCurlyBrace = self.current.0 {
self.next_token()?;
let mut fields = Vec::new();
loop {
if let Token::RightCurlyBrace = self.current.0 {
let right_end = self.current.1 .1;
self.next_token()?;
return Ok(Node::new(
Statement::FieldsStructInstantiation {
name: Node::new(Identifier::new(text), position),
fields,
},
(position.0, right_end),
));
}
let field_name = self.parse_identifier()?;
if let Token::Equal = self.current.0 {
self.next_token()?;
} else {
return Err(ParseError::ExpectedToken {
expected: TokenKind::Equal,
actual: self.current.0.to_owned(),
position: self.current.1,
});
}
let field_value = self.parse_statement(0)?;
fields.push((field_name, field_value));
if let Token::Comma = self.current.0 {
self.next_token()?;
}
}
}
Ok(Node::new(
Statement::Identifier(Identifier::new(text)),
position,
))
}
(Token::Integer(text), position) => { (Token::Integer(text), position) => {
self.next_token()?; self.next_token()?;
@ -253,17 +312,11 @@ impl<'src> Parser<'src> {
)) ))
} }
} }
(Token::Identifier(text), position) => {
self.next_token()?;
Ok(Node::new(
Statement::Identifier(Identifier::new(text)),
position,
))
}
(Token::If, position) => { (Token::If, position) => {
self.next_token()?; self.next_token()?;
self.context = ParserContext::IfElseStatement;
let condition = Box::new(self.parse_statement(0)?); let condition = Box::new(self.parse_statement(0)?);
let if_body = Box::new(self.parse_block()?); let if_body = Box::new(self.parse_block()?);
@ -300,6 +353,8 @@ impl<'src> Parser<'src> {
let else_body = Box::new(self.parse_block()?); let else_body = Box::new(self.parse_block()?);
let else_end = else_body.position.1; let else_end = else_body.position.1;
self.context = ParserContext::None;
return Ok(Node::new( return Ok(Node::new(
Statement::IfElseIfElse { Statement::IfElseIfElse {
condition, condition,
@ -315,6 +370,8 @@ impl<'src> Parser<'src> {
let else_body = Box::new(self.parse_block()?); let else_body = Box::new(self.parse_block()?);
let else_end = else_body.position.1; let else_end = else_body.position.1;
self.context = ParserContext::None;
Ok(Node::new( Ok(Node::new(
Statement::IfElse { Statement::IfElse {
condition, condition,
@ -327,6 +384,8 @@ impl<'src> Parser<'src> {
} else { } else {
let if_end = if_body.position.1; let if_end = if_body.position.1;
self.context = ParserContext::None;
Ok(Node::new( Ok(Node::new(
Statement::If { Statement::If {
condition, condition,
@ -972,6 +1031,11 @@ impl<'src> Parser<'src> {
} }
} }
enum ParserContext {
IfElseStatement,
None,
}
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum ParseError { pub enum ParseError {
BooleanError { BooleanError {
@ -1083,6 +1147,34 @@ mod tests {
use super::*; use super::*;
#[test]
fn fields_struct_instantiation() {
let input = "Foo { a = 42, b = 4.0 }";
assert_eq!(
parse(input),
Ok(AbstractSyntaxTree {
nodes: [Node::new(
Statement::FieldsStructInstantiation {
name: Node::new(Identifier::new("Foo"), (0, 3)),
fields: vec![
(
Node::new(Identifier::new("a"), (6, 7)),
Node::new(Statement::Constant(Value::integer(42)), (10, 12))
),
(
Node::new(Identifier::new("b"), (14, 15)),
Node::new(Statement::Constant(Value::float(4.0)), (18, 21))
)
]
},
(0, 23)
)]
.into()
})
);
}
#[test] #[test]
fn fields_struct() { fn fields_struct() {
let input = "struct Foo { a: int, b: float }"; let input = "struct Foo { a: int, b: float }";

View File

@ -12,8 +12,7 @@ use std::{
use crate::{ use crate::{
parse, value::ValueInner, AbstractSyntaxTree, Analyzer, AssignmentOperator, BinaryOperator, parse, value::ValueInner, AbstractSyntaxTree, Analyzer, AssignmentOperator, BinaryOperator,
BuiltInFunctionError, Context, DustError, Identifier, Node, ParseError, Span, Statement, BuiltInFunctionError, Context, DustError, Identifier, Node, ParseError, Span, Statement,
Struct, StructDefinition, StructInstantiation, StructType, Type, UnaryOperator, Value, Struct, StructType, Type, UnaryOperator, Value, ValueError,
ValueError,
}; };
/// Run the source code and return the result. /// Run the source code and return the result.
@ -65,8 +64,9 @@ pub fn run_with_context(source: &str, context: Context) -> Result<Option<Value>,
/// Dust virtual machine. /// Dust virtual machine.
/// ///
/// **Warning**: Do not run an AbstractSyntaxTree that has not been analyzed. Use the `run` or /// **Warning**: Do not run an AbstractSyntaxTree that has not been analyzed *with the same
/// `run_with_context` functions to make sure the program is analyzed before running it. /// context*. Use the `run` or `run_with_context` functions to make sure the program is analyzed
/// before running it.
/// ///
/// See the `run_with_context` function for an example of how to use the Analyzer and the VM. /// See the `run_with_context` function for an example of how to use the Analyzer and the VM.
pub struct Vm { pub struct Vm {
@ -325,6 +325,25 @@ impl Vm {
Ok(function_call_return) Ok(function_call_return)
} }
Statement::Constant(value) => Ok(Some(value.clone())), Statement::Constant(value) => Ok(Some(value.clone())),
Statement::FieldsStructInstantiation { name, fields } => {
let mut values = Vec::new();
for (identifier, value_node) in fields {
let position = value_node.position;
let value = if let Some(value) = self.run_statement(value_node)? {
value
} else {
return Err(VmError::ExpectedValue { position });
};
values.push((identifier.inner, value));
}
Ok(Some(Value::r#struct(Struct::Fields {
name: name.inner,
fields: values,
})))
}
Statement::Invokation { Statement::Invokation {
invokee, invokee,
type_arguments: _, type_arguments: _,
@ -600,24 +619,6 @@ impl Vm {
Ok(None) Ok(None)
} }
Statement::StructDefinition(_) => Ok(None), Statement::StructDefinition(_) => 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 } => { Statement::UnaryOperation { operator, operand } => {
let position = operand.position; let position = operand.position;
let value = if let Some(value) = self.run_statement(*operand)? { let value = if let Some(value) = self.run_statement(*operand)? {
@ -832,6 +833,22 @@ mod tests {
use super::*; use super::*;
#[test]
fn define_and_instantiate_fields_struct() {
let input = "struct Foo { bar: int, baz: float } Foo { bar = 42, baz = 4.0 }";
assert_eq!(
run(input),
Ok(Some(Value::r#struct(Struct::Fields {
name: Identifier::new("Foo"),
fields: vec![
(Identifier::new("bar"), Value::integer(42)),
(Identifier::new("baz"), Value::float(4.0))
]
})))
);
}
#[test] #[test]
fn assign_tuple_struct_variable() { fn assign_tuple_struct_variable() {
let input = " let input = "