Pass analyzer tests
This commit is contained in:
parent
e84bb2ea70
commit
de3c83a6f5
@ -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,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -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}"),
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user