Implement fields struct instantiation
This commit is contained in:
parent
b55a79d6bf
commit
5c8e72a6f7
@ -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, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)?;
|
||||
|
||||
|
@ -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};
|
||||
|
@ -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 }";
|
||||
|
@ -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 = "
|
||||
|
Loading…
Reference in New Issue
Block a user