Improve constructors
This commit is contained in:
parent
de3c83a6f5
commit
6a488c2245
@ -82,9 +82,11 @@ impl<'a> Analyzer<'a> {
|
|||||||
|
|
||||||
fn analyze_statement(&mut self, statement: &Statement) {
|
fn analyze_statement(&mut self, statement: &Statement) {
|
||||||
match statement {
|
match statement {
|
||||||
Statement::Expression(expression) => self.analyze_expression(expression),
|
Statement::Expression(expression) => {
|
||||||
|
self.analyze_expression(expression, statement.position())
|
||||||
|
}
|
||||||
Statement::ExpressionNullified(expression_node) => {
|
Statement::ExpressionNullified(expression_node) => {
|
||||||
self.analyze_expression(&expression_node.inner);
|
self.analyze_expression(&expression_node.inner, statement.position());
|
||||||
}
|
}
|
||||||
Statement::Let(let_statement) => match &let_statement.inner {
|
Statement::Let(let_statement) => match &let_statement.inner {
|
||||||
LetStatement::Let { identifier, value }
|
LetStatement::Let { identifier, value }
|
||||||
@ -118,7 +120,7 @@ impl<'a> Analyzer<'a> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.analyze_expression(value);
|
self.analyze_expression(value, statement.position());
|
||||||
}
|
}
|
||||||
LetStatement::LetType { .. } => todo!(),
|
LetStatement::LetType { .. } => todo!(),
|
||||||
LetStatement::LetMutType { .. } => todo!(),
|
LetStatement::LetMutType { .. } => todo!(),
|
||||||
@ -130,7 +132,7 @@ impl<'a> Analyzer<'a> {
|
|||||||
StructType::Unit {
|
StructType::Unit {
|
||||||
name: name.inner.clone(),
|
name: name.inner.clone(),
|
||||||
},
|
},
|
||||||
name.position,
|
statement.position(),
|
||||||
),
|
),
|
||||||
StructDefinition::Tuple { name, items } => {
|
StructDefinition::Tuple { name, items } => {
|
||||||
let fields = items.iter().map(|item| item.inner.clone()).collect();
|
let fields = items.iter().map(|item| item.inner.clone()).collect();
|
||||||
@ -141,7 +143,7 @@ impl<'a> Analyzer<'a> {
|
|||||||
name: name.inner.clone(),
|
name: name.inner.clone(),
|
||||||
fields,
|
fields,
|
||||||
},
|
},
|
||||||
name.position,
|
statement.position(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
StructDefinition::Fields { name, fields } => {
|
StructDefinition::Fields { name, fields } => {
|
||||||
@ -158,7 +160,7 @@ impl<'a> Analyzer<'a> {
|
|||||||
name: name.inner.clone(),
|
name: name.inner.clone(),
|
||||||
fields,
|
fields,
|
||||||
},
|
},
|
||||||
name.position,
|
statement.position(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -173,69 +175,96 @@ impl<'a> Analyzer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_expression(&mut self, expression: &Expression) {
|
fn analyze_expression(&mut self, expression: &Expression, statement_position: Span) {
|
||||||
match expression {
|
match expression {
|
||||||
Expression::Block(block_expression) => self.analyze_block(&block_expression.inner),
|
Expression::Block(block_expression) => self.analyze_block(&block_expression.inner),
|
||||||
Expression::Break(break_node) => {
|
Expression::Break(break_node) => {
|
||||||
if let Some(expression) = &break_node.inner {
|
if let Some(expression) = &break_node.inner {
|
||||||
self.analyze_expression(expression);
|
self.analyze_expression(expression, statement_position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::Call(call_expression) => {
|
Expression::Call(call_expression) => {
|
||||||
let CallExpression { invoker, arguments } = call_expression.inner.as_ref();
|
let CallExpression { invoker, arguments } = call_expression.inner.as_ref();
|
||||||
|
|
||||||
self.analyze_expression(invoker);
|
self.analyze_expression(invoker, statement_position);
|
||||||
|
|
||||||
for argument in arguments {
|
for argument in arguments {
|
||||||
self.analyze_expression(argument);
|
self.analyze_expression(argument, statement_position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::FieldAccess(field_access_expression) => {
|
Expression::FieldAccess(field_access_expression) => {
|
||||||
let FieldAccessExpression { container, .. } =
|
let FieldAccessExpression { container, field } =
|
||||||
field_access_expression.inner.as_ref();
|
field_access_expression.inner.as_ref();
|
||||||
|
|
||||||
self.analyze_expression(container);
|
let container_type = match container.return_type(&self.context) {
|
||||||
}
|
Ok(Some(r#type)) => r#type,
|
||||||
Expression::Grouped(expression) => {
|
Ok(None) => {
|
||||||
self.analyze_expression(expression.inner.as_ref());
|
self.errors
|
||||||
}
|
.push(AnalysisError::ExpectedValueFromExpression {
|
||||||
Expression::Identifier(identifier) => {
|
expression: container.clone(),
|
||||||
let found = self
|
|
||||||
.context
|
|
||||||
.update_last_position(&identifier.inner, identifier.position)
|
|
||||||
.map_err(|error| {
|
|
||||||
self.errors.push(AnalysisError::ContextError {
|
|
||||||
error,
|
|
||||||
position: identifier.position,
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Ok(false) = found {
|
return;
|
||||||
|
}
|
||||||
|
Err(ast_error) => {
|
||||||
|
self.errors.push(AnalysisError::AstError(ast_error));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !container_type.has_field(&field.inner) {
|
||||||
|
self.errors.push(AnalysisError::UndefinedFieldIdentifier {
|
||||||
|
identifier: field.clone(),
|
||||||
|
container: container.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.analyze_expression(container, statement_position);
|
||||||
|
}
|
||||||
|
Expression::Grouped(expression) => {
|
||||||
|
self.analyze_expression(expression.inner.as_ref(), statement_position);
|
||||||
|
}
|
||||||
|
Expression::Identifier(identifier) => {
|
||||||
|
let find_identifier = self
|
||||||
|
.context
|
||||||
|
.update_last_position(&identifier.inner, statement_position);
|
||||||
|
|
||||||
|
if let Ok(false) = find_identifier {
|
||||||
self.errors.push(AnalysisError::UndefinedVariable {
|
self.errors.push(AnalysisError::UndefinedVariable {
|
||||||
identifier: identifier.clone(),
|
identifier: identifier.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Err(context_error) = find_identifier {
|
||||||
|
self.errors.push(AnalysisError::ContextError {
|
||||||
|
error: context_error,
|
||||||
|
position: identifier.position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::If(if_expression) => {
|
||||||
|
self.analyze_if(&if_expression.inner, statement_position)
|
||||||
}
|
}
|
||||||
Expression::If(if_expression) => self.analyze_if(&if_expression.inner),
|
|
||||||
Expression::List(list_expression) => match list_expression.inner.as_ref() {
|
Expression::List(list_expression) => match list_expression.inner.as_ref() {
|
||||||
ListExpression::AutoFill {
|
ListExpression::AutoFill {
|
||||||
repeat_operand,
|
repeat_operand,
|
||||||
length_operand,
|
length_operand,
|
||||||
} => {
|
} => {
|
||||||
self.analyze_expression(repeat_operand);
|
self.analyze_expression(repeat_operand, statement_position);
|
||||||
self.analyze_expression(length_operand);
|
self.analyze_expression(length_operand, statement_position);
|
||||||
}
|
}
|
||||||
ListExpression::Ordered(expressions) => {
|
ListExpression::Ordered(expressions) => {
|
||||||
for expression in expressions {
|
for expression in expressions {
|
||||||
self.analyze_expression(expression);
|
self.analyze_expression(expression, statement_position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Expression::ListIndex(list_index_expression) => {
|
Expression::ListIndex(list_index_expression) => {
|
||||||
let ListIndexExpression { list, index } = list_index_expression.inner.as_ref();
|
let ListIndexExpression { list, index } = list_index_expression.inner.as_ref();
|
||||||
|
|
||||||
self.analyze_expression(list);
|
self.analyze_expression(list, statement_position);
|
||||||
self.analyze_expression(index);
|
self.analyze_expression(index, statement_position);
|
||||||
|
|
||||||
let list_type = match list.return_type(&self.context) {
|
let list_type = match list.return_type(&self.context) {
|
||||||
Ok(Some(r#type)) => r#type,
|
Ok(Some(r#type)) => r#type,
|
||||||
@ -253,7 +282,7 @@ impl<'a> Analyzer<'a> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let index_type = match list.return_type(&self.context) {
|
let index_type = match index.return_type(&self.context) {
|
||||||
Ok(Some(r#type)) => r#type,
|
Ok(Some(r#type)) => r#type,
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
self.errors
|
self.errors
|
||||||
@ -301,6 +330,22 @@ impl<'a> Analyzer<'a> {
|
|||||||
index_value: integer,
|
index_value: integer,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if let Type::Integer
|
||||||
|
| Type::Range {
|
||||||
|
r#type: RangeableType::Integer,
|
||||||
|
} = index_type
|
||||||
|
{
|
||||||
|
} else {
|
||||||
|
self.errors.push(AnalysisError::ExpectedTypeMultiple {
|
||||||
|
expected: vec![
|
||||||
|
Type::Integer,
|
||||||
|
Type::Range {
|
||||||
|
r#type: RangeableType::Integer,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
actual: index_type.clone(),
|
||||||
|
actual_expression: index.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,13 +374,13 @@ impl<'a> Analyzer<'a> {
|
|||||||
Expression::Loop(loop_expression) => match loop_expression.inner.as_ref() {
|
Expression::Loop(loop_expression) => match loop_expression.inner.as_ref() {
|
||||||
LoopExpression::Infinite { block } => self.analyze_block(&block.inner),
|
LoopExpression::Infinite { block } => self.analyze_block(&block.inner),
|
||||||
LoopExpression::While { condition, block } => {
|
LoopExpression::While { condition, block } => {
|
||||||
self.analyze_expression(condition);
|
self.analyze_expression(condition, statement_position);
|
||||||
self.analyze_block(&block.inner);
|
self.analyze_block(&block.inner);
|
||||||
}
|
}
|
||||||
LoopExpression::For {
|
LoopExpression::For {
|
||||||
iterator, block, ..
|
iterator, block, ..
|
||||||
} => {
|
} => {
|
||||||
self.analyze_expression(iterator);
|
self.analyze_expression(iterator, statement_position);
|
||||||
self.analyze_block(&block.inner);
|
self.analyze_block(&block.inner);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -343,23 +388,23 @@ impl<'a> Analyzer<'a> {
|
|||||||
let MapExpression { pairs } = map_expression.inner.as_ref();
|
let MapExpression { pairs } = map_expression.inner.as_ref();
|
||||||
|
|
||||||
for (_, expression) in pairs {
|
for (_, expression) in pairs {
|
||||||
self.analyze_expression(expression);
|
self.analyze_expression(expression, statement_position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::Operator(operator_expression) => match operator_expression.inner.as_ref() {
|
Expression::Operator(operator_expression) => match operator_expression.inner.as_ref() {
|
||||||
OperatorExpression::Assignment { assignee, value } => {
|
OperatorExpression::Assignment { assignee, value } => {
|
||||||
self.analyze_expression(assignee);
|
self.analyze_expression(assignee, statement_position);
|
||||||
self.analyze_expression(value);
|
self.analyze_expression(value, statement_position);
|
||||||
}
|
}
|
||||||
OperatorExpression::Comparison { left, right, .. } => {
|
OperatorExpression::Comparison { left, right, .. } => {
|
||||||
self.analyze_expression(left);
|
self.analyze_expression(left, statement_position);
|
||||||
self.analyze_expression(right);
|
self.analyze_expression(right, statement_position);
|
||||||
}
|
}
|
||||||
OperatorExpression::CompoundAssignment {
|
OperatorExpression::CompoundAssignment {
|
||||||
assignee, modifier, ..
|
assignee, modifier, ..
|
||||||
} => {
|
} => {
|
||||||
self.analyze_expression(assignee);
|
self.analyze_expression(assignee, statement_position);
|
||||||
self.analyze_expression(modifier);
|
self.analyze_expression(modifier, statement_position);
|
||||||
|
|
||||||
let (expected_type, actual_type) = match (
|
let (expected_type, actual_type) = match (
|
||||||
assignee.return_type(&self.context),
|
assignee.return_type(&self.context),
|
||||||
@ -413,14 +458,14 @@ impl<'a> Analyzer<'a> {
|
|||||||
}
|
}
|
||||||
OperatorExpression::ErrorPropagation(_) => todo!(),
|
OperatorExpression::ErrorPropagation(_) => todo!(),
|
||||||
OperatorExpression::Negation(expression) => {
|
OperatorExpression::Negation(expression) => {
|
||||||
self.analyze_expression(expression);
|
self.analyze_expression(expression, statement_position);
|
||||||
}
|
}
|
||||||
OperatorExpression::Not(expression) => {
|
OperatorExpression::Not(expression) => {
|
||||||
self.analyze_expression(expression);
|
self.analyze_expression(expression, statement_position);
|
||||||
}
|
}
|
||||||
OperatorExpression::Math { left, right, .. } => {
|
OperatorExpression::Math { left, right, .. } => {
|
||||||
self.analyze_expression(left);
|
self.analyze_expression(left, statement_position);
|
||||||
self.analyze_expression(right);
|
self.analyze_expression(right, statement_position);
|
||||||
|
|
||||||
let (left_type, right_type) = match (
|
let (left_type, right_type) = match (
|
||||||
left.return_type(&self.context),
|
left.return_type(&self.context),
|
||||||
@ -506,8 +551,8 @@ impl<'a> Analyzer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
OperatorExpression::Logic { left, right, .. } => {
|
OperatorExpression::Logic { left, right, .. } => {
|
||||||
self.analyze_expression(left);
|
self.analyze_expression(left, statement_position);
|
||||||
self.analyze_expression(right);
|
self.analyze_expression(right, statement_position);
|
||||||
|
|
||||||
let (left_type, right_type) = match (
|
let (left_type, right_type) = match (
|
||||||
left.return_type(&self.context),
|
left.return_type(&self.context),
|
||||||
@ -560,19 +605,19 @@ impl<'a> Analyzer<'a> {
|
|||||||
},
|
},
|
||||||
Expression::Range(range_expression) => match range_expression.inner.as_ref() {
|
Expression::Range(range_expression) => match range_expression.inner.as_ref() {
|
||||||
RangeExpression::Exclusive { start, end } => {
|
RangeExpression::Exclusive { start, end } => {
|
||||||
self.analyze_expression(start);
|
self.analyze_expression(start, statement_position);
|
||||||
self.analyze_expression(end);
|
self.analyze_expression(end, statement_position);
|
||||||
}
|
}
|
||||||
RangeExpression::Inclusive { start, end } => {
|
RangeExpression::Inclusive { start, end } => {
|
||||||
self.analyze_expression(start);
|
self.analyze_expression(start, statement_position);
|
||||||
self.analyze_expression(end);
|
self.analyze_expression(end, statement_position);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Expression::Struct(struct_expression) => match struct_expression.inner.as_ref() {
|
Expression::Struct(struct_expression) => match struct_expression.inner.as_ref() {
|
||||||
StructExpression::Fields { name, fields } => {
|
StructExpression::Fields { name, fields } => {
|
||||||
let update_position = self
|
let update_position = self
|
||||||
.context
|
.context
|
||||||
.update_last_position(&name.inner, name.position);
|
.update_last_position(&name.inner, statement_position);
|
||||||
|
|
||||||
if let Err(error) = update_position {
|
if let Err(error) = update_position {
|
||||||
self.errors.push(AnalysisError::ContextError {
|
self.errors.push(AnalysisError::ContextError {
|
||||||
@ -584,14 +629,14 @@ impl<'a> Analyzer<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (_, expression) in fields {
|
for (_, expression) in fields {
|
||||||
self.analyze_expression(expression);
|
self.analyze_expression(expression, statement_position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Expression::TupleAccess(tuple_access) => {
|
Expression::TupleAccess(tuple_access) => {
|
||||||
let TupleAccessExpression { tuple, .. } = tuple_access.inner.as_ref();
|
let TupleAccessExpression { tuple, .. } = tuple_access.inner.as_ref();
|
||||||
|
|
||||||
self.analyze_expression(tuple);
|
self.analyze_expression(tuple, statement_position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -611,13 +656,13 @@ impl<'a> Analyzer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_if(&mut self, if_expression: &IfExpression) {
|
fn analyze_if(&mut self, if_expression: &IfExpression, statement_position: Span) {
|
||||||
match if_expression {
|
match if_expression {
|
||||||
IfExpression::If {
|
IfExpression::If {
|
||||||
condition,
|
condition,
|
||||||
if_block,
|
if_block,
|
||||||
} => {
|
} => {
|
||||||
self.analyze_expression(condition);
|
self.analyze_expression(condition, statement_position);
|
||||||
self.analyze_block(&if_block.inner);
|
self.analyze_block(&if_block.inner);
|
||||||
}
|
}
|
||||||
IfExpression::IfElse {
|
IfExpression::IfElse {
|
||||||
@ -625,7 +670,7 @@ impl<'a> Analyzer<'a> {
|
|||||||
if_block,
|
if_block,
|
||||||
r#else,
|
r#else,
|
||||||
} => {
|
} => {
|
||||||
self.analyze_expression(condition);
|
self.analyze_expression(condition, statement_position);
|
||||||
self.analyze_block(&if_block.inner);
|
self.analyze_block(&if_block.inner);
|
||||||
|
|
||||||
match r#else {
|
match r#else {
|
||||||
@ -633,7 +678,7 @@ impl<'a> Analyzer<'a> {
|
|||||||
self.analyze_block(&block_expression.inner);
|
self.analyze_block(&block_expression.inner);
|
||||||
}
|
}
|
||||||
ElseExpression::If(if_expression) => {
|
ElseExpression::If(if_expression) => {
|
||||||
self.analyze_if(&if_expression.inner);
|
self.analyze_if(&if_expression.inner, statement_position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -691,9 +736,9 @@ pub enum AnalysisError {
|
|||||||
actual_type: Type,
|
actual_type: Type,
|
||||||
expected: Type,
|
expected: Type,
|
||||||
},
|
},
|
||||||
UndefinedField {
|
UndefinedFieldIdentifier {
|
||||||
identifier: Expression,
|
identifier: Node<Identifier>,
|
||||||
expression: Expression,
|
container: Expression,
|
||||||
},
|
},
|
||||||
UndefinedType {
|
UndefinedType {
|
||||||
identifier: Node<Identifier>,
|
identifier: Node<Identifier>,
|
||||||
@ -736,7 +781,7 @@ impl AnalysisError {
|
|||||||
AnalysisError::TypeConflict {
|
AnalysisError::TypeConflict {
|
||||||
actual_expression, ..
|
actual_expression, ..
|
||||||
} => actual_expression.position(),
|
} => actual_expression.position(),
|
||||||
AnalysisError::UndefinedField { identifier, .. } => identifier.position(),
|
AnalysisError::UndefinedFieldIdentifier { identifier, .. } => identifier.position,
|
||||||
AnalysisError::UndefinedType { identifier } => identifier.position,
|
AnalysisError::UndefinedType { identifier } => identifier.position,
|
||||||
AnalysisError::UndefinedVariable { identifier } => identifier.position,
|
AnalysisError::UndefinedVariable { identifier } => identifier.position,
|
||||||
AnalysisError::UnexpectedIdentifier { identifier } => identifier.position,
|
AnalysisError::UnexpectedIdentifier { identifier } => identifier.position,
|
||||||
@ -826,11 +871,15 @@ impl Display for AnalysisError {
|
|||||||
expected, actual_statement, actual_type
|
expected, actual_statement, actual_type
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
AnalysisError::UndefinedField {
|
AnalysisError::UndefinedFieldIdentifier {
|
||||||
identifier,
|
identifier,
|
||||||
expression: map,
|
container,
|
||||||
} => {
|
} => {
|
||||||
write!(f, "Undefined field {} in map {}", identifier, map)
|
write!(
|
||||||
|
f,
|
||||||
|
"Undefined field {} in container {}",
|
||||||
|
identifier, container
|
||||||
|
)
|
||||||
}
|
}
|
||||||
AnalysisError::UndefinedType { identifier } => {
|
AnalysisError::UndefinedType { identifier } => {
|
||||||
write!(f, "Undefined type {}", identifier)
|
write!(f, "Undefined type {}", identifier)
|
||||||
@ -850,6 +899,7 @@ impl Display for AnalysisError {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::RangeableType;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -963,43 +1013,21 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nonexistant_field_identifier() {
|
fn nonexistant_map_field_identifier() {
|
||||||
let source = "{ x = 1 }.y";
|
let source = "map { x = 1 }.y";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
analyze(source),
|
analyze(source),
|
||||||
Err(DustError::Analysis {
|
Err(DustError::Analysis {
|
||||||
analysis_errors: vec![AnalysisError::UndefinedField {
|
analysis_errors: vec![AnalysisError::UndefinedFieldIdentifier {
|
||||||
identifier: Expression::identifier(Identifier::new("y"), (11, 12)),
|
container: Expression::map(
|
||||||
expression: Expression::map(
|
|
||||||
[(
|
[(
|
||||||
Node::new(Identifier::new("x"), (2, 3)),
|
Node::new(Identifier::new("x"), (6, 7)),
|
||||||
Expression::literal(1, (6, 7))
|
Expression::literal(1, (10, 11))
|
||||||
)],
|
)],
|
||||||
(0, 11)
|
(0, 13)
|
||||||
),
|
|
||||||
}],
|
|
||||||
source,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn nonexistant_field_string() {
|
|
||||||
let source = "{ x = 1 }.'y'";
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
analyze(source),
|
|
||||||
Err(DustError::Analysis {
|
|
||||||
analysis_errors: vec![AnalysisError::UndefinedField {
|
|
||||||
identifier: Expression::literal("y", (11, 14)),
|
|
||||||
expression: Expression::map(
|
|
||||||
[(
|
|
||||||
Node::new(Identifier::new("x"), (2, 3)),
|
|
||||||
Expression::literal(1, (6, 7))
|
|
||||||
)],
|
|
||||||
(0, 11)
|
|
||||||
),
|
),
|
||||||
|
identifier: Node::new(Identifier::new("y"), (14, 15)),
|
||||||
}],
|
}],
|
||||||
source,
|
source,
|
||||||
})
|
})
|
||||||
@ -1030,7 +1058,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn malformed_field_access() {
|
fn malformed_field_access() {
|
||||||
let source = "{ x = 1 }.0";
|
let source = "struct Foo { x: int } Foo { x: 1 }.0";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
analyze(source),
|
analyze(source),
|
||||||
@ -1079,14 +1107,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nonexistant_field() {
|
fn nonexistant_field() {
|
||||||
let source = "'hello'.foo";
|
let source = "\"hello\".foo";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
analyze(source),
|
analyze(source),
|
||||||
Err(DustError::Analysis {
|
Err(DustError::Analysis {
|
||||||
analysis_errors: vec![AnalysisError::UndefinedField {
|
analysis_errors: vec![AnalysisError::UndefinedFieldIdentifier {
|
||||||
expression: Expression::literal("hello", (0, 7)),
|
container: Expression::literal("hello", (0, 7)),
|
||||||
identifier: Expression::identifier(Identifier::new("foo"), (8, 11)),
|
identifier: Node::new(Identifier::new("foo"), (8, 11)),
|
||||||
}],
|
}],
|
||||||
source,
|
source,
|
||||||
})
|
})
|
||||||
|
@ -5,60 +5,103 @@ use std::{
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{Identifier, Struct, Value};
|
use crate::{Identifier, Struct, StructType, TypeConflict, Value};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum Constructor {
|
pub struct Constructor {
|
||||||
Unit(UnitConstructor),
|
pub struct_type: StructType,
|
||||||
Tuple(TupleConstructor),
|
|
||||||
Fields(FieldsConstructor),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Constructor {
|
impl Constructor {
|
||||||
|
pub fn construct_unit(&self) -> Result<Value, ConstructError> {
|
||||||
|
if let StructType::Unit { name } = &self.struct_type {
|
||||||
|
Ok(Value::r#struct(Struct::Unit { name: name.clone() }))
|
||||||
|
} else {
|
||||||
|
Err(ConstructError::ExpectedUnit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn construct_tuple(&self, fields: Vec<Value>) -> Result<Value, ConstructError> {
|
||||||
|
if let StructType::Tuple {
|
||||||
|
name: expected_name,
|
||||||
|
fields: expected_fields,
|
||||||
|
} = &self.struct_type
|
||||||
|
{
|
||||||
|
if fields.len() != expected_fields.len() {
|
||||||
|
return Err(ConstructError::FieldCountMismatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, value) in fields.iter().enumerate() {
|
||||||
|
let expected_type = expected_fields.get(i).unwrap();
|
||||||
|
let actual_type = value.r#type();
|
||||||
|
|
||||||
|
expected_type.check(&actual_type)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::r#struct(Struct::Tuple {
|
||||||
|
name: expected_name.clone(),
|
||||||
|
fields,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Err(ConstructError::ExpectedTuple)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn construct_fields(
|
||||||
|
&self,
|
||||||
|
fields: HashMap<Identifier, Value>,
|
||||||
|
) -> Result<Value, ConstructError> {
|
||||||
|
if let StructType::Fields {
|
||||||
|
name: expected_name,
|
||||||
|
fields: expected_fields,
|
||||||
|
} = &self.struct_type
|
||||||
|
{
|
||||||
|
if fields.len() != expected_fields.len() {
|
||||||
|
return Err(ConstructError::FieldCountMismatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (field_name, field_value) in fields.iter() {
|
||||||
|
let expected_type = expected_fields.get(field_name).unwrap();
|
||||||
|
let actual_type = field_value.r#type();
|
||||||
|
|
||||||
|
expected_type.check(&actual_type)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::r#struct(Struct::Fields {
|
||||||
|
name: expected_name.clone(),
|
||||||
|
fields,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Err(ConstructError::ExpectedFields)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum ConstructError {
|
||||||
|
FieldCountMismatch,
|
||||||
|
ExpectedUnit,
|
||||||
|
ExpectedTuple,
|
||||||
|
ExpectedFields,
|
||||||
|
TypeConflict(TypeConflict),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TypeConflict> for ConstructError {
|
||||||
|
fn from(conflict: TypeConflict) -> Self {
|
||||||
|
Self::TypeConflict(conflict)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ConstructError {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Constructor::Unit(unit) => write!(f, "{}", unit.name),
|
ConstructError::FieldCountMismatch => write!(f, "Field count mismatch"),
|
||||||
Constructor::Tuple(tuple) => write!(f, "{}", tuple.name),
|
ConstructError::ExpectedUnit => write!(f, "Expected unit struct"),
|
||||||
Constructor::Fields(fields) => write!(f, "{}", fields.name),
|
ConstructError::ExpectedTuple => write!(f, "Expected tuple struct"),
|
||||||
|
ConstructError::ExpectedFields => write!(f, "Expected fields struct"),
|
||||||
|
ConstructError::TypeConflict(TypeConflict { expected, actual }) => {
|
||||||
|
write!(f, "Type conflict: expected {}, got {}", expected, actual)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct UnitConstructor {
|
|
||||||
pub name: Identifier,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnitConstructor {
|
|
||||||
pub fn construct(self) -> Value {
|
|
||||||
Value::r#struct(Struct::Unit { name: self.name })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct TupleConstructor {
|
|
||||||
pub name: Identifier,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TupleConstructor {
|
|
||||||
pub fn construct(self, fields: Vec<Value>) -> Value {
|
|
||||||
Value::r#struct(Struct::Tuple {
|
|
||||||
name: self.name,
|
|
||||||
fields,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct FieldsConstructor {
|
|
||||||
pub name: Identifier,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FieldsConstructor {
|
|
||||||
pub fn construct(self, fields: HashMap<Identifier, Value>) -> Value {
|
|
||||||
Value::r#struct(Struct::Fields {
|
|
||||||
name: self.name,
|
|
||||||
fields,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -172,7 +172,7 @@ impl Context {
|
|||||||
identifier: Identifier,
|
identifier: Identifier,
|
||||||
constructor: Constructor,
|
constructor: Constructor,
|
||||||
) -> Result<(), ContextError> {
|
) -> Result<(), ContextError> {
|
||||||
log::trace!("Setting {identifier} to constructor {constructor}");
|
log::trace!("Setting {identifier} to constructor {constructor:?}");
|
||||||
|
|
||||||
let mut associations = self.associations.write()?;
|
let mut associations = self.associations.write()?;
|
||||||
|
|
||||||
|
@ -204,14 +204,10 @@ impl<'src> Parser<'src> {
|
|||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
if let Token::Semicolon = self.current_token {
|
if let Token::Semicolon = self.current_token {
|
||||||
break;
|
self.next_token()?;
|
||||||
} else {
|
|
||||||
return Err(ParseError::ExpectedToken {
|
|
||||||
expected: TokenKind::Semicolon,
|
|
||||||
actual: self.current_token.to_owned(),
|
|
||||||
position: self.current_position,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let type_node = self.parse_type()?;
|
let type_node = self.parse_type()?;
|
||||||
@ -227,8 +223,6 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
let position = (start_position.0, self.current_position.1);
|
let position = (start_position.0, self.current_position.1);
|
||||||
|
|
||||||
self.next_token()?;
|
|
||||||
|
|
||||||
return if types.is_empty() {
|
return if types.is_empty() {
|
||||||
Ok(Statement::struct_definition(
|
Ok(Statement::struct_definition(
|
||||||
StructDefinition::Unit { name },
|
StructDefinition::Unit { name },
|
||||||
@ -249,6 +243,8 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Token::RightCurlyBrace = self.current_token {
|
if let Token::RightCurlyBrace = self.current_token {
|
||||||
|
self.next_token()?;
|
||||||
|
|
||||||
if let Token::Semicolon = self.current_token {
|
if let Token::Semicolon = self.current_token {
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
}
|
}
|
||||||
@ -281,8 +277,6 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
let position = (start_position.0, self.current_position.1);
|
let position = (start_position.0, self.current_position.1);
|
||||||
|
|
||||||
self.next_token()?;
|
|
||||||
|
|
||||||
return if fields.is_empty() {
|
return if fields.is_empty() {
|
||||||
Ok(Statement::struct_definition(
|
Ok(Statement::struct_definition(
|
||||||
StructDefinition::Unit { name },
|
StructDefinition::Unit { name },
|
||||||
|
@ -17,10 +17,7 @@ use std::{
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{constructor::Constructor, Identifier};
|
||||||
constructor::{FieldsConstructor, TupleConstructor, UnitConstructor},
|
|
||||||
Constructor, Identifier,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Description of a kind of value.
|
/// Description of a kind of value.
|
||||||
///
|
///
|
||||||
@ -234,6 +231,28 @@ impl Type {
|
|||||||
expected: self.clone(),
|
expected: self.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_field(&self, field: &Identifier) -> bool {
|
||||||
|
match field.as_str() {
|
||||||
|
"to_string" => true,
|
||||||
|
"length" => {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Type::List { .. }
|
||||||
|
| Type::ListOf { .. }
|
||||||
|
| Type::ListEmpty
|
||||||
|
| Type::Map { .. }
|
||||||
|
| Type::String { .. }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"is_even" | "is_odd" => matches!(self, Type::Integer | Type::Float),
|
||||||
|
_ => match self {
|
||||||
|
Type::Struct(StructType::Fields { fields, .. }) => fields.contains_key(field),
|
||||||
|
Type::Map { pairs } => pairs.contains_key(field),
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Type {
|
impl Display for Type {
|
||||||
@ -445,14 +464,8 @@ impl StructType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn constructor(&self) -> Constructor {
|
pub fn constructor(&self) -> Constructor {
|
||||||
match self {
|
Constructor {
|
||||||
StructType::Unit { name } => Constructor::Unit(UnitConstructor { name: name.clone() }),
|
struct_type: self.clone(),
|
||||||
StructType::Tuple { name, .. } => {
|
|
||||||
Constructor::Tuple(TupleConstructor { name: name.clone() })
|
|
||||||
}
|
|
||||||
StructType::Fields { name, .. } => {
|
|
||||||
Constructor::Fields(FieldsConstructor { name: name.clone() })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ use crate::{
|
|||||||
OperatorExpression, PrimitiveValueExpression, RangeExpression, Span, Statement,
|
OperatorExpression, PrimitiveValueExpression, RangeExpression, Span, Statement,
|
||||||
StructDefinition, StructExpression,
|
StructDefinition, StructExpression,
|
||||||
},
|
},
|
||||||
|
constructor::ConstructError,
|
||||||
core_library, parse, Analyzer, BuiltInFunctionError, Constructor, Context, ContextData,
|
core_library, parse, Analyzer, BuiltInFunctionError, Constructor, Context, ContextData,
|
||||||
ContextError, DustError, Expression, Function, FunctionCallError, Identifier, ParseError,
|
ContextError, DustError, Expression, Function, FunctionCallError, Identifier, ParseError,
|
||||||
StructType, Type, Value, ValueData, ValueError,
|
StructType, Type, Value, ValueData, ValueError,
|
||||||
@ -313,18 +314,23 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ContextData::Constructor(constructor)) = get_data {
|
if let Some(ContextData::Constructor(constructor)) = get_data {
|
||||||
if let Constructor::Unit(unit_constructor) = constructor {
|
let construct_result = constructor.construct_unit();
|
||||||
return Ok(Evaluation::Return(Some(unit_constructor.construct())));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(Evaluation::Constructor(constructor));
|
match construct_result {
|
||||||
|
Ok(value) => Ok(Evaluation::Return(Some(value))),
|
||||||
|
Err(ConstructError::ExpectedUnit) => Ok(Evaluation::Constructor(constructor)),
|
||||||
|
Err(error) => Err(RuntimeError::ConstructError {
|
||||||
|
error,
|
||||||
|
position: identifier.position,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
Err(RuntimeError::UnassociatedIdentifier {
|
Err(RuntimeError::UnassociatedIdentifier {
|
||||||
identifier: identifier.inner,
|
identifier: identifier.inner,
|
||||||
position: identifier.position,
|
position: identifier.position,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn run_struct(
|
fn run_struct(
|
||||||
&self,
|
&self,
|
||||||
@ -340,7 +346,6 @@ impl Vm {
|
|||||||
.map_err(|error| RuntimeError::ContextError { error, position })?;
|
.map_err(|error| RuntimeError::ContextError { error, position })?;
|
||||||
|
|
||||||
if let Some(constructor) = constructor {
|
if let Some(constructor) = constructor {
|
||||||
if let Constructor::Fields(fields_constructor) = constructor {
|
|
||||||
let mut arguments = HashMap::with_capacity(fields.len());
|
let mut arguments = HashMap::with_capacity(fields.len());
|
||||||
|
|
||||||
for (identifier, expression) in fields {
|
for (identifier, expression) in fields {
|
||||||
@ -352,12 +357,11 @@ impl Vm {
|
|||||||
arguments.insert(identifier.inner, value);
|
arguments.insert(identifier.inner, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Evaluation::Return(Some(
|
let value = constructor
|
||||||
fields_constructor.construct(arguments),
|
.construct_fields(arguments.clone())
|
||||||
)))
|
.map_err(|error| RuntimeError::ConstructError { error, position })?;
|
||||||
} else {
|
|
||||||
Err(RuntimeError::ExpectedFieldsConstructor { position })
|
Ok(Evaluation::Return(Some(value)))
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Err(RuntimeError::ExpectedConstructor { position })
|
Err(RuntimeError::ExpectedConstructor { position })
|
||||||
}
|
}
|
||||||
@ -803,32 +807,28 @@ impl Vm {
|
|||||||
let run_invoker = self.run_expression(invoker, collect_garbage)?;
|
let run_invoker = self.run_expression(invoker, collect_garbage)?;
|
||||||
|
|
||||||
match run_invoker {
|
match run_invoker {
|
||||||
Evaluation::Constructor(constructor) => match constructor {
|
Evaluation::Constructor(constructor) => {
|
||||||
Constructor::Unit(unit_constructor) => {
|
|
||||||
Ok(Evaluation::Return(Some(unit_constructor.construct())))
|
|
||||||
}
|
|
||||||
Constructor::Tuple(tuple_constructor) => {
|
|
||||||
let mut fields = Vec::new();
|
let mut fields = Vec::new();
|
||||||
|
|
||||||
for argument in arguments {
|
for argument in arguments {
|
||||||
let position = argument.position();
|
let position = argument.position();
|
||||||
|
|
||||||
if let Some(value) = self.run_expression(argument, collect_garbage)?.value()
|
if let Some(value) = self.run_expression(argument, collect_garbage)?.value() {
|
||||||
{
|
|
||||||
fields.push(value);
|
fields.push(value);
|
||||||
} else {
|
} else {
|
||||||
return Err(RuntimeError::ExpectedValue { position });
|
return Err(RuntimeError::ExpectedValue { position });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let tuple = tuple_constructor.construct(fields);
|
let value = constructor.construct_tuple(fields).map_err(|error| {
|
||||||
|
RuntimeError::ConstructError {
|
||||||
|
error,
|
||||||
|
position: invoker_position,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(Evaluation::Return(Some(tuple)))
|
Ok(Evaluation::Return(Some(value)))
|
||||||
}
|
}
|
||||||
Constructor::Fields(_) => {
|
|
||||||
todo!("Return an error")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Evaluation::Return(Some(value)) => {
|
Evaluation::Return(Some(value)) => {
|
||||||
let function = match value {
|
let function = match value {
|
||||||
Value::Raw(ValueData::Function(function)) => function,
|
Value::Raw(ValueData::Function(function)) => function,
|
||||||
@ -1091,6 +1091,10 @@ impl Evaluation {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum RuntimeError {
|
pub enum RuntimeError {
|
||||||
|
ConstructError {
|
||||||
|
error: ConstructError,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
ContextError {
|
ContextError {
|
||||||
error: ContextError,
|
error: ContextError,
|
||||||
position: Span,
|
position: Span,
|
||||||
@ -1113,9 +1117,6 @@ pub enum RuntimeError {
|
|||||||
left_position: Span,
|
left_position: Span,
|
||||||
right_position: Span,
|
right_position: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Anaylsis Failures
|
|
||||||
// These should be prevented by running the analyzer before the VM
|
|
||||||
BuiltInFunctionError {
|
BuiltInFunctionError {
|
||||||
error: BuiltInFunctionError,
|
error: BuiltInFunctionError,
|
||||||
position: Span,
|
position: Span,
|
||||||
@ -1192,6 +1193,7 @@ pub enum RuntimeError {
|
|||||||
impl RuntimeError {
|
impl RuntimeError {
|
||||||
pub fn position(&self) -> Span {
|
pub fn position(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
|
Self::ConstructError { position, .. } => *position,
|
||||||
Self::ContextError { position, .. } => *position,
|
Self::ContextError { position, .. } => *position,
|
||||||
Self::BuiltInFunctionError { position, .. } => *position,
|
Self::BuiltInFunctionError { position, .. } => *position,
|
||||||
Self::FunctionCall { position, .. } => *position,
|
Self::FunctionCall { position, .. } => *position,
|
||||||
@ -1242,6 +1244,9 @@ impl From<ParseError> for RuntimeError {
|
|||||||
impl Display for RuntimeError {
|
impl Display for RuntimeError {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
Self::ConstructError { error, position } => {
|
||||||
|
write!(f, "Constructor error at {:?}: {}", position, error)
|
||||||
|
}
|
||||||
Self::ContextError { error, position } => {
|
Self::ContextError { error, position } => {
|
||||||
write!(f, "Context error at {:?}: {}", position, error)
|
write!(f, "Context error at {:?}: {}", position, error)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user