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