Pass analyzer tests

This commit is contained in:
Jeff 2024-08-23 07:36:10 -04:00
parent e84bb2ea70
commit de3c83a6f5
3 changed files with 496 additions and 308 deletions

View File

@ -35,14 +35,15 @@ use crate::{
pub fn analyze(source: &str) -> Result<(), DustError> { pub fn analyze(source: &str) -> Result<(), DustError> {
let abstract_tree = parse(source)?; let abstract_tree = parse(source)?;
let context = core_library().create_child(); let context = core_library().create_child();
let analyzer = Analyzer::new(&abstract_tree, context); let mut analyzer = Analyzer::new(&abstract_tree, context);
analyzer analyzer.analyze();
.analyze()
.map_err(|analysis_error| DustError::Analysis { if analyzer.errors.is_empty() {
analysis_error, Ok(())
source, } else {
}) Err(DustError::analysis(analyzer.errors, source))
}
} }
/// Static analyzer that checks for potential runtime errors. /// Static analyzer that checks for potential runtime errors.
@ -61,58 +62,69 @@ pub fn analyze(source: &str) -> Result<(), DustError> {
pub struct Analyzer<'a> { pub struct Analyzer<'a> {
abstract_tree: &'a AbstractSyntaxTree, abstract_tree: &'a AbstractSyntaxTree,
context: Context, context: Context,
pub errors: Vec<AnalysisError>,
} }
impl<'recovered, 'a: 'recovered> Analyzer<'a> { impl<'a> Analyzer<'a> {
pub fn new(abstract_tree: &'a AbstractSyntaxTree, context: Context) -> Self { pub fn new(abstract_tree: &'a AbstractSyntaxTree, context: Context) -> Self {
Self { Self {
abstract_tree, abstract_tree,
context, context,
errors: Vec::new(),
} }
} }
pub fn analyze(&'recovered self) -> Result<(), AnalysisError> { pub fn analyze(&mut self) {
for statement in &self.abstract_tree.statements { for statement in &self.abstract_tree.statements {
self.analyze_statement(statement)?; self.analyze_statement(statement);
}
} }
Ok(()) fn analyze_statement(&mut self, statement: &Statement) {
}
fn analyze_statement(&'recovered self, statement: &Statement) -> Result<(), AnalysisError> {
match statement { match statement {
Statement::Expression(expression) => self.analyze_expression(expression)?, Statement::Expression(expression) => self.analyze_expression(expression),
Statement::ExpressionNullified(expression_node) => { Statement::ExpressionNullified(expression_node) => {
self.analyze_expression(&expression_node.inner)?; self.analyze_expression(&expression_node.inner);
} }
Statement::Let(let_statement) => match &let_statement.inner { Statement::Let(let_statement) => match &let_statement.inner {
LetStatement::Let { identifier, value } LetStatement::Let { identifier, value }
| LetStatement::LetMut { identifier, value } => { | LetStatement::LetMut { identifier, value } => {
let r#type = value.return_type(&self.context)?; let r#type = match value.return_type(&self.context) {
Ok(type_option) => type_option,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
None
}
};
if let Some(r#type) = r#type { if let Some(r#type) = r#type {
self.context let set_type = self.context.set_variable_type(
.set_variable_type(
identifier.inner.clone(), identifier.inner.clone(),
r#type, r#type.clone(),
identifier.position, identifier.position,
) );
.map_err(|error| AnalysisError::ContextError {
error, if let Err(context_error) = set_type {
self.errors.push(AnalysisError::ContextError {
error: context_error,
position: identifier.position, position: identifier.position,
})?; });
}
} else { } else {
return Err(AnalysisError::LetExpectedValueFromStatement { self.errors
.push(AnalysisError::LetExpectedValueFromStatement {
actual: value.clone(), actual: value.clone(),
}); });
} }
self.analyze_expression(value)?; self.analyze_expression(value);
} }
LetStatement::LetType { .. } => todo!(), LetStatement::LetType { .. } => todo!(),
LetStatement::LetMutType { .. } => todo!(), LetStatement::LetMutType { .. } => todo!(),
}, },
Statement::StructDefinition(struct_definition) => match &struct_definition.inner { Statement::StructDefinition(struct_definition) => {
let set_constructor_type = match &struct_definition.inner {
StructDefinition::Unit { name } => self.context.set_constructor_type( StructDefinition::Unit { name } => self.context.set_constructor_type(
name.inner.clone(), name.inner.clone(),
StructType::Unit { StructType::Unit {
@ -149,93 +161,114 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
name.position, name.position,
) )
} }
} };
.map_err(|error| AnalysisError::ContextError {
error, if let Err(context_error) = set_constructor_type {
self.errors.push(AnalysisError::ContextError {
error: context_error,
position: struct_definition.position, position: struct_definition.position,
})?, });
}
}
}
} }
Ok(()) fn analyze_expression(&mut self, expression: &Expression) {
}
fn analyze_expression(&self, expression: &Expression) -> Result<(), AnalysisError> {
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);
} }
} }
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);
for argument in arguments { for argument in arguments {
self.analyze_expression(argument)?; self.analyze_expression(argument);
} }
} }
Expression::FieldAccess(field_access_expression) => { Expression::FieldAccess(field_access_expression) => {
let FieldAccessExpression { container, field } = let FieldAccessExpression { container, .. } =
field_access_expression.inner.as_ref(); field_access_expression.inner.as_ref();
self.context self.analyze_expression(container);
.update_last_position(&field.inner, field.position)
.map_err(|error| AnalysisError::ContextError {
error,
position: field.position,
})?;
self.analyze_expression(container)?;
} }
Expression::Grouped(expression) => { Expression::Grouped(expression) => {
self.analyze_expression(expression.inner.as_ref())?; self.analyze_expression(expression.inner.as_ref());
} }
Expression::Identifier(identifier) => { Expression::Identifier(identifier) => {
let found = self let found = self
.context .context
.update_last_position(&identifier.inner, identifier.position) .update_last_position(&identifier.inner, identifier.position)
.map_err(|error| AnalysisError::ContextError { .map_err(|error| {
self.errors.push(AnalysisError::ContextError {
error, error,
position: identifier.position, position: identifier.position,
})?; })
});
if !found { if let Ok(false) = found {
return Err(AnalysisError::UndefinedVariable { self.errors.push(AnalysisError::UndefinedVariable {
identifier: identifier.clone(), identifier: identifier.clone(),
}); });
} }
} }
Expression::If(if_expression) => self.analyze_if(&if_expression.inner)?, 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);
self.analyze_expression(length_operand)?; self.analyze_expression(length_operand);
} }
ListExpression::Ordered(expressions) => { ListExpression::Ordered(expressions) => {
for expression in expressions { for expression in expressions {
self.analyze_expression(expression)?; self.analyze_expression(expression);
} }
} }
}, },
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);
self.analyze_expression(index)?; self.analyze_expression(index);
let list_type = list.return_type(&self.context)?; let list_type = match list.return_type(&self.context) {
let index_type = if let Some(r#type) = index.return_type(&self.context)? { Ok(Some(r#type)) => r#type,
r#type Ok(None) => {
} else { self.errors
return Err(AnalysisError::ExpectedValueFromExpression { .push(AnalysisError::ExpectedValueFromExpression {
expression: index.clone(), expression: list.clone(),
}); });
};
return;
}
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
return;
}
};
let index_type = match list.return_type(&self.context) {
Ok(Some(r#type)) => r#type,
Ok(None) => {
self.errors
.push(AnalysisError::ExpectedValueFromExpression {
expression: list.clone(),
});
return;
}
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
return;
}
};
let literal_type = if let Expression::Literal(Node { inner, .. }) = index { let literal_type = if let Expression::Literal(Node { inner, .. }) = index {
Some(inner.as_ref().clone()) Some(inner.as_ref().clone())
} else { } else {
@ -247,39 +280,21 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
))) = literal_type ))) = literal_type
{ {
if integer < 0 { if integer < 0 {
return Err(AnalysisError::NegativeIndex { self.errors.push(AnalysisError::NegativeIndex {
index: index.clone(), index: index.clone(),
index_value: integer, index_value: integer,
list: list.clone(), list: list.clone(),
}); });
} }
} else if let Type::Range { r#type } = &index_type {
if let RangeableType::Integer = r#type {
// Ok
} else {
return Err(AnalysisError::ExpectedType {
expected: Type::Range {
r#type: RangeableType::Integer,
},
actual: index_type,
actual_expression: index.clone(),
});
}
} else {
return Err(AnalysisError::ExpectedType {
expected: Type::Integer,
actual: index_type,
actual_expression: index.clone(),
});
} }
if let Some(Type::List { length, .. }) = list_type { if let Type::List { length, .. } = list_type {
if let Some(LiteralExpression::Primitive(PrimitiveValueExpression::Integer( if let Some(LiteralExpression::Primitive(PrimitiveValueExpression::Integer(
integer, integer,
))) = literal_type ))) = literal_type
{ {
if integer >= length as i64 { if integer >= length as i64 {
return Err(AnalysisError::IndexOutOfBounds { self.errors.push(AnalysisError::IndexOutOfBounds {
index: index.clone(), index: index.clone(),
length, length,
list: list.clone(), list: list.clone(),
@ -289,16 +304,16 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
} }
} }
if let Some(Type::String { if let Type::String {
length: Some(length), length: Some(length),
}) = list_type } = list_type
{ {
if let Some(LiteralExpression::Primitive(PrimitiveValueExpression::Integer( if let Some(LiteralExpression::Primitive(PrimitiveValueExpression::Integer(
integer, integer,
))) = literal_type ))) = literal_type
{ {
if integer >= length as i64 { if integer >= length as i64 {
return Err(AnalysisError::IndexOutOfBounds { self.errors.push(AnalysisError::IndexOutOfBounds {
index: index.clone(), index: index.clone(),
length, length,
list: list.clone(), list: list.clone(),
@ -307,133 +322,237 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
} }
} }
} }
if list_type.is_none() {
return Err(AnalysisError::ExpectedValueFromExpression {
expression: list.clone(),
});
}
} }
Expression::Literal(_) => { Expression::Literal(_) => {
// Literals don't need to be analyzed // Literals don't need to be analyzed
} }
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);
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);
self.analyze_block(&block.inner)?; self.analyze_block(&block.inner);
} }
}, },
Expression::Map(map_expression) => { Expression::Map(map_expression) => {
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);
} }
} }
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);
self.analyze_expression(value)?; self.analyze_expression(value);
} }
OperatorExpression::Comparison { left, right, .. } => { OperatorExpression::Comparison { left, right, .. } => {
self.analyze_expression(left)?; self.analyze_expression(left);
self.analyze_expression(right)?; self.analyze_expression(right);
} }
OperatorExpression::CompoundAssignment { OperatorExpression::CompoundAssignment {
assignee, modifier, .. assignee, modifier, ..
} => { } => {
self.analyze_expression(assignee)?; self.analyze_expression(assignee);
self.analyze_expression(modifier)?; self.analyze_expression(modifier);
let expected_type = assignee.return_type(&self.context)?; let (expected_type, actual_type) = match (
let actual_type = modifier.return_type(&self.context)?; assignee.return_type(&self.context),
modifier.return_type(&self.context),
if expected_type.is_none() { ) {
return Err(AnalysisError::ExpectedValueFromExpression { (Ok(Some(expected_type)), Ok(Some(actual_type))) => {
(expected_type, actual_type)
}
(Ok(None), Ok(None)) => {
self.errors
.push(AnalysisError::ExpectedValueFromExpression {
expression: assignee.clone(), expression: assignee.clone(),
}); });
} self.errors
.push(AnalysisError::ExpectedValueFromExpression {
if actual_type.is_none() {
return Err(AnalysisError::ExpectedValueFromExpression {
expression: modifier.clone(), expression: modifier.clone(),
}); });
return;
} }
(Ok(None), _) => {
self.errors
.push(AnalysisError::ExpectedValueFromExpression {
expression: assignee.clone(),
});
return;
}
(_, Ok(None)) => {
self.errors
.push(AnalysisError::ExpectedValueFromExpression {
expression: modifier.clone(),
});
return;
}
(Err(ast_error), _) => {
self.errors.push(AnalysisError::AstError(ast_error));
return;
}
(_, Err(ast_error)) => {
self.errors.push(AnalysisError::AstError(ast_error));
return;
}
};
if let (Some(expected_type), Some(actual_type)) = (expected_type, actual_type) { if actual_type != expected_type {
expected_type.check(&actual_type).map_err(|_| { self.errors.push(AnalysisError::ExpectedType {
AnalysisError::TypeConflict {
actual_expression: modifier.clone(),
actual_type,
expected: expected_type, expected: expected_type,
} actual: actual_type,
})?; actual_expression: modifier.clone(),
});
} }
} }
OperatorExpression::ErrorPropagation(_) => todo!(), OperatorExpression::ErrorPropagation(_) => todo!(),
OperatorExpression::Negation(expression) => { OperatorExpression::Negation(expression) => {
self.analyze_expression(expression)?; self.analyze_expression(expression);
} }
OperatorExpression::Not(expression) => { OperatorExpression::Not(expression) => {
self.analyze_expression(expression)?; self.analyze_expression(expression);
} }
OperatorExpression::Math { left, right, .. } => { OperatorExpression::Math { left, right, .. } => {
self.analyze_expression(left)?; self.analyze_expression(left);
self.analyze_expression(right)?; self.analyze_expression(right);
let left_type = left.return_type(&self.context)?; let (left_type, right_type) = match (
let right_type = right.return_type(&self.context)?; left.return_type(&self.context),
right.return_type(&self.context),
if left_type.is_none() { ) {
return Err(AnalysisError::ExpectedValueFromExpression { (Ok(Some(left_type)), Ok(Some(right_type))) => (left_type, right_type),
(Ok(None), Ok(None)) => {
self.errors
.push(AnalysisError::ExpectedValueFromExpression {
expression: left.clone(), expression: left.clone(),
}); });
} self.errors
.push(AnalysisError::ExpectedValueFromExpression {
if right_type.is_none() {
return Err(AnalysisError::ExpectedValueFromExpression {
expression: right.clone(), expression: right.clone(),
}); });
return;
} }
(Ok(None), _) => {
self.errors
.push(AnalysisError::ExpectedValueFromExpression {
expression: left.clone(),
});
return;
}
(_, Ok(None)) => {
self.errors
.push(AnalysisError::ExpectedValueFromExpression {
expression: right.clone(),
});
return;
}
(Err(ast_error), _) => {
self.errors.push(AnalysisError::AstError(ast_error));
return;
}
(_, Err(ast_error)) => {
self.errors.push(AnalysisError::AstError(ast_error));
return;
}
};
if left_type != right_type { match left_type {
return Err(AnalysisError::ExpectedType { Type::Integer => {
expected: left_type.unwrap(), if right_type != Type::Integer {
actual: right_type.unwrap(), self.errors.push(AnalysisError::ExpectedType {
expected: Type::Integer,
actual: right_type,
actual_expression: right.clone(), actual_expression: right.clone(),
}); });
} }
} }
Type::Float => {
if right_type != Type::Float {
self.errors.push(AnalysisError::ExpectedType {
expected: Type::Float,
actual: right_type,
actual_expression: right.clone(),
});
}
}
Type::String { .. } => {
if let Type::String { .. } = right_type {
} else {
self.errors.push(AnalysisError::ExpectedType {
expected: Type::String { length: None },
actual: right_type,
actual_expression: right.clone(),
});
}
}
_ => {
self.errors.push(AnalysisError::ExpectedTypeMultiple {
expected: vec![
Type::Float,
Type::Integer,
Type::String { length: None },
],
actual: left_type,
actual_expression: left.clone(),
});
}
}
}
OperatorExpression::Logic { left, right, .. } => { OperatorExpression::Logic { left, right, .. } => {
self.analyze_expression(left)?; self.analyze_expression(left);
self.analyze_expression(right)?; self.analyze_expression(right);
let left_type = left.return_type(&self.context)?; let (left_type, right_type) = match (
let right_type = right.return_type(&self.context)?; left.return_type(&self.context),
right.return_type(&self.context),
if left_type.is_none() { ) {
return Err(AnalysisError::ExpectedValueFromExpression { (Ok(Some(left_type)), Ok(Some(right_type))) => (left_type, right_type),
(Ok(None), Ok(None)) => {
self.errors
.push(AnalysisError::ExpectedValueFromExpression {
expression: left.clone(), expression: left.clone(),
}); });
} self.errors
.push(AnalysisError::ExpectedValueFromExpression {
if right_type.is_none() {
return Err(AnalysisError::ExpectedValueFromExpression {
expression: right.clone(), expression: right.clone(),
}); });
return;
} }
(Ok(None), _) => {
self.errors
.push(AnalysisError::ExpectedValueFromExpression {
expression: left.clone(),
});
return;
}
(_, Ok(None)) => {
self.errors
.push(AnalysisError::ExpectedValueFromExpression {
expression: right.clone(),
});
return;
}
(Err(ast_error), _) => {
self.errors.push(AnalysisError::AstError(ast_error));
return;
}
(_, Err(ast_error)) => {
self.errors.push(AnalysisError::AstError(ast_error));
return;
}
};
if left_type != right_type { if left_type != right_type {
return Err(AnalysisError::ExpectedType { self.errors.push(AnalysisError::ExpectedType {
expected: left_type.unwrap(), expected: left_type,
actual: right_type.unwrap(), actual: right_type,
actual_expression: right.clone(), actual_expression: right.clone(),
}); });
} }
@ -441,87 +560,84 @@ impl<'recovered, 'a: 'recovered> 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);
self.analyze_expression(end)?; self.analyze_expression(end);
} }
RangeExpression::Inclusive { start, end } => { RangeExpression::Inclusive { start, end } => {
self.analyze_expression(start)?; self.analyze_expression(start);
self.analyze_expression(end)?; self.analyze_expression(end);
} }
}, },
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 } => {
self.context let update_position = self
.update_last_position(&name.inner, name.position) .context
.map_err(|error| AnalysisError::ContextError { .update_last_position(&name.inner, name.position);
if let Err(error) = update_position {
self.errors.push(AnalysisError::ContextError {
error, error,
position: name.position, position: name.position,
})?; });
return;
}
for (_, expression) in fields { for (_, expression) in fields {
self.analyze_expression(expression)?; self.analyze_expression(expression);
} }
} }
}, },
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);
}
} }
} }
Ok(()) fn analyze_block(&mut self, block_expression: &BlockExpression) {
}
fn analyze_block(
&'recovered self,
block_expression: &BlockExpression,
) -> Result<(), AnalysisError> {
match block_expression { match block_expression {
BlockExpression::Async(statements) => { BlockExpression::Async(statements) => {
for statement in statements { for statement in statements {
self.analyze_statement(statement)?; self.analyze_statement(statement);
} }
} }
BlockExpression::Sync(statements) => { BlockExpression::Sync(statements) => {
for statement in statements { for statement in statements {
self.analyze_statement(statement)?; self.analyze_statement(statement);
}
} }
} }
} }
Ok(()) fn analyze_if(&mut self, if_expression: &IfExpression) {
}
fn analyze_if(&'recovered self, if_expression: &IfExpression) -> Result<(), AnalysisError> {
match if_expression { match if_expression {
IfExpression::If { IfExpression::If {
condition, condition,
if_block, if_block,
} => { } => {
self.analyze_expression(condition)?; self.analyze_expression(condition);
self.analyze_block(&if_block.inner)?; self.analyze_block(&if_block.inner);
} }
IfExpression::IfElse { IfExpression::IfElse {
condition, condition,
if_block, if_block,
r#else, r#else,
} => { } => {
self.analyze_expression(condition)?; self.analyze_expression(condition);
self.analyze_block(&if_block.inner)?; self.analyze_block(&if_block.inner);
match r#else { match r#else {
ElseExpression::Block(block_expression) => { ElseExpression::Block(block_expression) => {
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);
} }
} }
} }
} }
Ok(())
} }
} }
@ -577,7 +693,7 @@ pub enum AnalysisError {
}, },
UndefinedField { UndefinedField {
identifier: Expression, identifier: Expression,
statement: Expression, expression: Expression,
}, },
UndefinedType { UndefinedType {
identifier: Node<Identifier>, identifier: Node<Identifier>,
@ -712,7 +828,7 @@ impl Display for AnalysisError {
} }
AnalysisError::UndefinedField { AnalysisError::UndefinedField {
identifier, identifier,
statement: map, expression: map,
} => { } => {
write!(f, "Undefined field {} in map {}", identifier, map) write!(f, "Undefined field {} in map {}", identifier, map)
} }
@ -737,6 +853,30 @@ mod tests {
use super::*; use super::*;
#[test]
fn multiple_errors() {
let source = "1 + 1.0; 'a' + 1";
assert_eq!(
analyze(source),
Err(DustError::Analysis {
analysis_errors: vec![
AnalysisError::ExpectedType {
expected: Type::Integer,
actual: Type::Float,
actual_expression: Expression::literal(1.0, (4, 7)),
},
AnalysisError::ExpectedTypeMultiple {
expected: vec![Type::Float, Type::Integer, Type::String { length: None }],
actual: Type::Character,
actual_expression: Expression::literal('a', (9, 12)),
}
],
source,
})
);
}
#[test] #[test]
fn add_assign_wrong_type() { fn add_assign_wrong_type() {
let source = " let source = "
@ -747,11 +887,11 @@ mod tests {
assert_eq!( assert_eq!(
analyze(source), analyze(source),
Err(DustError::Analysis { Err(DustError::Analysis {
analysis_error: AnalysisError::TypeConflict { analysis_errors: vec![AnalysisError::ExpectedType {
actual_expression: Expression::literal(1.0, (45, 48)),
actual_type: Type::Float,
expected: Type::Integer, expected: Type::Integer,
}, actual: Type::Float,
actual_expression: Expression::literal(1.0, (45, 48)),
}],
source, source,
}) })
); );
@ -767,11 +907,11 @@ mod tests {
assert_eq!( assert_eq!(
analyze(source), analyze(source),
Err(DustError::Analysis { Err(DustError::Analysis {
analysis_error: AnalysisError::TypeConflict { analysis_errors: vec![AnalysisError::ExpectedType {
actual_expression: Expression::literal(1.0, (45, 48)),
actual_type: Type::Float,
expected: Type::Integer, expected: Type::Integer,
}, actual: Type::Float,
actual_expression: Expression::literal(1.0, (45, 48)),
}],
source, source,
}) })
); );
@ -787,11 +927,11 @@ mod tests {
assert_eq!( assert_eq!(
analyze(source), analyze(source),
Err(DustError::Analysis { Err(DustError::Analysis {
analysis_error: AnalysisError::TypeConflict { analysis_errors: vec![AnalysisError::ExpectedType {
actual_expression: Expression::literal(2, (52, 53)),
actual_type: Type::Integer,
expected: Type::Float, expected: Type::Float,
}, actual: Type::Integer,
actual_expression: Expression::literal(2, (52, 53)),
}],
source, source,
}) })
); );
@ -804,7 +944,7 @@ mod tests {
assert_eq!( assert_eq!(
analyze(source), analyze(source),
Err(DustError::Analysis { Err(DustError::Analysis {
analysis_error: AnalysisError::IndexOutOfBounds { analysis_errors: vec![AnalysisError::IndexOutOfBounds {
list: Expression::list( list: Expression::list(
vec![ vec![
Expression::literal(1, (1, 2)), Expression::literal(1, (1, 2)),
@ -816,7 +956,7 @@ mod tests {
index: Expression::literal(3, (10, 11)), index: Expression::literal(3, (10, 11)),
index_value: 3, index_value: 3,
length: 3, length: 3,
}, }],
source, source,
}) })
); );
@ -826,28 +966,63 @@ mod tests {
fn nonexistant_field_identifier() { fn nonexistant_field_identifier() {
let source = "{ x = 1 }.y"; let source = "{ x = 1 }.y";
assert_eq!(analyze(source), todo!()); assert_eq!(
analyze(source),
Err(DustError::Analysis {
analysis_errors: vec![AnalysisError::UndefinedField {
identifier: Expression::identifier(Identifier::new("y"), (11, 12)),
expression: Expression::map(
[(
Node::new(Identifier::new("x"), (2, 3)),
Expression::literal(1, (6, 7))
)],
(0, 11)
),
}],
source,
})
);
} }
#[test] #[test]
fn nonexistant_field_string() { fn nonexistant_field_string() {
let source = "{ x = 1 }.'y'"; let source = "{ x = 1 }.'y'";
assert_eq!(analyze(source), todo!()); 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)
),
}],
source,
})
);
} }
#[test] #[test]
fn malformed_list_index() { fn malformed_list_index() {
let source = "[1, 2, 3]['foo']"; let source = "[1, 2, 3][\"foo\"]";
assert_eq!( assert_eq!(
analyze(source), analyze(source),
Err(DustError::Analysis { Err(DustError::Analysis {
analysis_error: AnalysisError::ExpectedType { analysis_errors: vec![AnalysisError::ExpectedTypeMultiple {
expected: Type::Integer, expected: vec![
Type::Integer,
Type::Range {
r#type: RangeableType::Integer
}
],
actual: Type::String { length: Some(3) }, actual: Type::String { length: Some(3) },
actual_expression: Expression::literal("foo", (10, 15)), actual_expression: Expression::literal("foo", (10, 15)),
}, }],
source, source,
}) })
); );
@ -857,7 +1032,15 @@ mod tests {
fn malformed_field_access() { fn malformed_field_access() {
let source = "{ x = 1 }.0"; let source = "{ x = 1 }.0";
assert_eq!(analyze(source), todo!()); assert_eq!(
analyze(source),
Err(DustError::Analysis {
analysis_errors: vec![AnalysisError::ExpectedIdentifierOrString {
actual: Expression::literal(0, (10, 11))
}],
source,
})
);
} }
#[test] #[test]
@ -867,11 +1050,11 @@ mod tests {
assert_eq!( assert_eq!(
analyze(source), analyze(source),
Err(DustError::Analysis { Err(DustError::Analysis {
analysis_error: AnalysisError::ExpectedType { analysis_errors: vec![AnalysisError::ExpectedType {
expected: Type::Float, expected: Type::Float,
actual: Type::Integer, actual: Type::Integer,
actual_expression: Expression::literal(2, (7, 8)), actual_expression: Expression::literal(2, (7, 8)),
}, }],
source, source,
}) })
); );
@ -884,11 +1067,11 @@ mod tests {
assert_eq!( assert_eq!(
analyze(source), analyze(source),
Err(DustError::Analysis { Err(DustError::Analysis {
analysis_error: AnalysisError::ExpectedType { analysis_errors: vec![AnalysisError::ExpectedType {
expected: Type::Integer, expected: Type::Integer,
actual: Type::Boolean, actual: Type::Boolean,
actual_expression: Expression::literal(true, (5, 9)), actual_expression: Expression::literal(true, (5, 9)),
}, }],
source, source,
}) })
); );
@ -898,7 +1081,16 @@ mod tests {
fn nonexistant_field() { fn nonexistant_field() {
let source = "'hello'.foo"; let source = "'hello'.foo";
assert_eq!(analyze(source), todo!()); assert_eq!(
analyze(source),
Err(DustError::Analysis {
analysis_errors: vec![AnalysisError::UndefinedField {
expression: Expression::literal("hello", (0, 7)),
identifier: Expression::identifier(Identifier::new("foo"), (8, 11)),
}],
source,
})
);
} }
#[test] #[test]
@ -908,9 +1100,9 @@ mod tests {
assert_eq!( assert_eq!(
analyze(source), analyze(source),
Err(DustError::Analysis { Err(DustError::Analysis {
analysis_error: AnalysisError::UndefinedVariable { analysis_errors: vec![AnalysisError::UndefinedVariable {
identifier: Node::new(Identifier::new("foo"), (0, 3)) identifier: Node::new(Identifier::new("foo"), (0, 3))
}, }],
source, source,
}) })
); );

View File

@ -13,7 +13,7 @@ pub enum DustError<'src> {
source: &'src str, source: &'src str,
}, },
Analysis { Analysis {
analysis_error: AnalysisError, analysis_errors: Vec<AnalysisError>,
source: &'src str, source: &'src str,
}, },
Parse { Parse {
@ -34,9 +34,9 @@ impl<'src> DustError<'src> {
} }
} }
pub fn analysis(analysis_error: AnalysisError, source: &'src str) -> Self { pub fn analysis(analysis_errors: Vec<AnalysisError>, source: &'src str) -> Self {
DustError::Analysis { DustError::Analysis {
analysis_error, analysis_errors,
source, source,
} }
} }
@ -61,15 +61,6 @@ impl<'src> DustError<'src> {
} }
} }
pub fn position(&self) -> Span {
match self {
DustError::Runtime { runtime_error, .. } => runtime_error.position(),
DustError::Analysis { analysis_error, .. } => analysis_error.position(),
DustError::Parse { parse_error, .. } => parse_error.position(),
DustError::Lex { lex_error, .. } => lex_error.position(),
}
}
pub fn source(&self) -> &'src str { pub fn source(&self) -> &'src str {
match self { match self {
DustError::Runtime { source, .. } => source, DustError::Runtime { source, .. } => source,
@ -79,23 +70,27 @@ impl<'src> DustError<'src> {
} }
} }
pub fn primary_error_data(&self) -> (&'static str, Span, String) { pub fn error_data(&self) -> Vec<(&'static str, Span, String)> {
(self.title(), self.position(), self.to_string()) match self {
DustError::Runtime { runtime_error, .. } => vec![(
"Runtime error",
runtime_error.position(),
runtime_error.to_string(),
)],
DustError::Analysis {
analysis_errors, ..
} => analysis_errors
.iter()
.map(|error| ("Analysis error", error.position(), error.to_string()))
.collect(),
DustError::Parse { parse_error, .. } => vec![(
"Parse error",
parse_error.position(),
parse_error.to_string(),
)],
DustError::Lex { lex_error, .. } => {
vec![("Lex error", lex_error.position(), lex_error.to_string())]
} }
pub fn secondary_error_data(&self) -> Option<(&'static str, Span, String)> {
if let DustError::Runtime { runtime_error, .. } = self {
match runtime_error {
RuntimeError::Expression { error, .. } => {
Some(("Expression error", error.position(), error.to_string()))
}
RuntimeError::Statement { error, .. } => {
Some(("Statement error", error.position(), error.to_string()))
}
_ => None,
}
} else {
None
} }
} }
@ -103,16 +98,7 @@ impl<'src> DustError<'src> {
let mut report = String::new(); let mut report = String::new();
let renderer = Renderer::styled(); let renderer = Renderer::styled();
let (title, span, label) = self.primary_error_data(); for (title, span, label) in self.error_data() {
let message = Level::Error.title(title).snippet(
Snippet::source(self.source())
.annotation(Level::Info.span(span.0..span.1).label(&label)),
);
report.push_str(&format!("{}", renderer.render(message)));
if let Some((title, span, label)) = self.secondary_error_data() {
let message = Level::Error.title(title).snippet( let message = Level::Error.title(title).snippet(
Snippet::source(self.source()) Snippet::source(self.source())
.annotation(Level::Info.span(span.0..span.1).label(&label)), .annotation(Level::Info.span(span.0..span.1).label(&label)),
@ -129,7 +115,15 @@ impl Display for DustError<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
DustError::Runtime { runtime_error, .. } => write!(f, "{runtime_error}"), DustError::Runtime { runtime_error, .. } => write!(f, "{runtime_error}"),
DustError::Analysis { analysis_error, .. } => write!(f, "{analysis_error}"), DustError::Analysis {
analysis_errors, ..
} => {
for error in analysis_errors {
write!(f, "{error} ")?;
}
Ok(())
}
DustError::Parse { parse_error, .. } => write!(f, "{parse_error}"), DustError::Parse { parse_error, .. } => write!(f, "{parse_error}"),
DustError::Lex { lex_error, .. } => write!(f, "{lex_error}"), DustError::Lex { lex_error, .. } => write!(f, "{lex_error}"),
} }

View File

@ -57,14 +57,16 @@ pub fn run(source: &str) -> Result<Option<Value>, DustError> {
/// ``` /// ```
pub fn run_with_context(source: &str, context: Context) -> Result<Option<Value>, DustError> { pub fn run_with_context(source: &str, context: Context) -> Result<Option<Value>, DustError> {
let abstract_syntax_tree = parse(source)?; let abstract_syntax_tree = parse(source)?;
let analyzer = Analyzer::new(&abstract_syntax_tree, context.clone()); let mut analyzer = Analyzer::new(&abstract_syntax_tree, context.clone());
analyzer analyzer.analyze();
.analyze()
.map_err(|analysis_error| DustError::Analysis { if !analyzer.errors.is_empty() {
analysis_error, return Err(DustError::Analysis {
analysis_errors: analyzer.errors,
source, source,
})?; });
}
let mut vm = Vm::new(abstract_syntax_tree, context); let mut vm = Vm::new(abstract_syntax_tree, context);