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 crate::{BuiltInFunction, Context, Identifier, Span, StructType, Type, Value};
use crate::{BuiltInFunction, Context, Identifier, Span, Type, Value};
/// In-memory representation of a Dust program.
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
@ -71,7 +71,10 @@ pub enum Statement {
type_arguments: Option<Vec<Node<Statement>>>,
value_arguments: Option<Vec<Node<Statement>>>,
},
StructInstantiation(StructInstantiation),
FieldsStructInstantiation {
name: Node<Identifier>,
fields: Vec<(Node<Identifier>, Node<Statement>)>,
},
// Loops
While {
@ -169,6 +172,7 @@ impl Statement {
},
Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(),
Statement::Constant(value) => Some(value.r#type()),
Statement::FieldsStructInstantiation { name, .. } => context.get_type(&name.inner),
Statement::Invokation {
invokee: function, ..
} => function.inner.expected_type(context),
@ -200,17 +204,6 @@ 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,
}
}
@ -313,6 +306,19 @@ impl Display for Statement {
write!(f, ")")
}
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 {
invokee: function,
type_arguments: type_parameters,
@ -424,9 +430,6 @@ 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}")
}
@ -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::{
abstract_tree::{BinaryOperator, StructInstantiation, UnaryOperator},
abstract_tree::{BinaryOperator, UnaryOperator},
parse, AbstractSyntaxTree, Context, DustError, Identifier, Node, Span, Statement,
StructDefinition, StructType, Type,
};
@ -291,6 +291,34 @@ impl<'a> Analyzer<'a> {
}
}
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 {
invokee,
value_arguments,
@ -488,17 +516,6 @@ impl<'a> Analyzer<'a> {
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)?;

View File

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

View File

@ -100,6 +100,7 @@ pub struct Parser<'src> {
source: &'src str,
lexer: Lexer,
current: (Token<'src>, Span),
context: ParserContext,
}
impl<'src> Parser<'src> {
@ -111,6 +112,7 @@ impl<'src> Parser<'src> {
source,
lexer,
current,
context: ParserContext::None,
}
}
@ -218,6 +220,63 @@ impl<'src> Parser<'src> {
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) => {
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) => {
self.next_token()?;
self.context = ParserContext::IfElseStatement;
let condition = Box::new(self.parse_statement(0)?);
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_end = else_body.position.1;
self.context = ParserContext::None;
return Ok(Node::new(
Statement::IfElseIfElse {
condition,
@ -315,6 +370,8 @@ impl<'src> Parser<'src> {
let else_body = Box::new(self.parse_block()?);
let else_end = else_body.position.1;
self.context = ParserContext::None;
Ok(Node::new(
Statement::IfElse {
condition,
@ -327,6 +384,8 @@ impl<'src> Parser<'src> {
} else {
let if_end = if_body.position.1;
self.context = ParserContext::None;
Ok(Node::new(
Statement::If {
condition,
@ -972,6 +1031,11 @@ impl<'src> Parser<'src> {
}
}
enum ParserContext {
IfElseStatement,
None,
}
#[derive(Debug, PartialEq, Clone)]
pub enum ParseError {
BooleanError {
@ -1083,6 +1147,34 @@ mod tests {
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]
fn fields_struct() {
let input = "struct Foo { a: int, b: float }";

View File

@ -12,8 +12,7 @@ use std::{
use crate::{
parse, value::ValueInner, AbstractSyntaxTree, Analyzer, AssignmentOperator, BinaryOperator,
BuiltInFunctionError, Context, DustError, Identifier, Node, ParseError, Span, Statement,
Struct, StructDefinition, StructInstantiation, StructType, Type, UnaryOperator, Value,
ValueError,
Struct, StructType, Type, UnaryOperator, Value, ValueError,
};
/// 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.
///
/// **Warning**: Do not run an AbstractSyntaxTree that has not been analyzed. Use the `run` or
/// `run_with_context` functions to make sure the program is analyzed before running it.
/// **Warning**: Do not run an AbstractSyntaxTree that has not been analyzed *with the same
/// 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.
pub struct Vm {
@ -325,6 +325,25 @@ impl Vm {
Ok(function_call_return)
}
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 {
invokee,
type_arguments: _,
@ -600,24 +619,6 @@ impl Vm {
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 } => {
let position = operand.position;
let value = if let Some(value) = self.run_statement(*operand)? {
@ -832,6 +833,22 @@ mod tests {
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]
fn assign_tuple_struct_variable() {
let input = "