Pass analyzer tests
This commit is contained in:
parent
e22d0254f5
commit
e1e40cf931
@ -225,7 +225,18 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
|||||||
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(index)?;
|
||||||
|
|
||||||
let list_type = list.return_type(&self.context)?;
|
let list_type = list.return_type(&self.context)?;
|
||||||
|
let index_type = if let Some(r#type) = index.return_type(&self.context)? {
|
||||||
|
r#type
|
||||||
|
} else {
|
||||||
|
return Err(AnalysisError::ExpectedValueFromExpression {
|
||||||
|
expression: index.clone(),
|
||||||
|
found_type: None,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
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())
|
||||||
@ -233,12 +244,31 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(LiteralExpression::Primitive(PrimitiveValueExpression::Integer(
|
||||||
|
integer,
|
||||||
|
))) = literal_type
|
||||||
|
{
|
||||||
|
if integer < 0 {
|
||||||
|
return Err(AnalysisError::NegativeIndex {
|
||||||
|
index: index.clone(),
|
||||||
|
index_value: integer,
|
||||||
|
list: list.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 Some(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 < 0 || integer >= length as i64 {
|
if integer >= length as i64 {
|
||||||
return Err(AnalysisError::IndexOutOfBounds {
|
return Err(AnalysisError::IndexOutOfBounds {
|
||||||
index: index.clone(),
|
index: index.clone(),
|
||||||
length,
|
length,
|
||||||
@ -257,7 +287,7 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
|||||||
integer,
|
integer,
|
||||||
))) = literal_type
|
))) = literal_type
|
||||||
{
|
{
|
||||||
if integer < 0 || integer >= length as i64 {
|
if integer >= length as i64 {
|
||||||
return Err(AnalysisError::IndexOutOfBounds {
|
return Err(AnalysisError::IndexOutOfBounds {
|
||||||
index: index.clone(),
|
index: index.clone(),
|
||||||
length,
|
length,
|
||||||
@ -274,9 +304,6 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
|||||||
found_type: list_type,
|
found_type: list_type,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.analyze_expression(list)?;
|
|
||||||
self.analyze_expression(index)?;
|
|
||||||
}
|
}
|
||||||
Expression::Literal(_) => {
|
Expression::Literal(_) => {
|
||||||
// Literals don't need to be analyzed
|
// Literals don't need to be analyzed
|
||||||
@ -536,6 +563,11 @@ pub enum AnalysisError {
|
|||||||
index_value: i64,
|
index_value: i64,
|
||||||
length: usize,
|
length: usize,
|
||||||
},
|
},
|
||||||
|
NegativeIndex {
|
||||||
|
list: Expression,
|
||||||
|
index: Expression,
|
||||||
|
index_value: i64,
|
||||||
|
},
|
||||||
TypeConflict {
|
TypeConflict {
|
||||||
actual_expression: Expression,
|
actual_expression: Expression,
|
||||||
actual_type: Type,
|
actual_type: Type,
|
||||||
@ -582,6 +614,7 @@ impl AnalysisError {
|
|||||||
AnalysisError::ExpectedValueFromStatement { actual } => actual.position(),
|
AnalysisError::ExpectedValueFromStatement { actual } => actual.position(),
|
||||||
AnalysisError::ExpectedValueArgumentCount { position, .. } => *position,
|
AnalysisError::ExpectedValueArgumentCount { position, .. } => *position,
|
||||||
AnalysisError::IndexOutOfBounds { index, .. } => index.position(),
|
AnalysisError::IndexOutOfBounds { index, .. } => index.position(),
|
||||||
|
AnalysisError::NegativeIndex { index, .. } => index.position(),
|
||||||
AnalysisError::TypeConflict {
|
AnalysisError::TypeConflict {
|
||||||
actual_expression, ..
|
actual_expression, ..
|
||||||
} => actual_expression.position(),
|
} => actual_expression.position(),
|
||||||
@ -601,7 +634,6 @@ impl Display for AnalysisError {
|
|||||||
match self {
|
match self {
|
||||||
AnalysisError::AstError(ast_error) => write!(f, "{}", ast_error),
|
AnalysisError::AstError(ast_error) => write!(f, "{}", ast_error),
|
||||||
AnalysisError::ContextError { error, .. } => write!(f, "{}", error),
|
AnalysisError::ContextError { error, .. } => write!(f, "{}", error),
|
||||||
|
|
||||||
AnalysisError::ExpectedType {
|
AnalysisError::ExpectedType {
|
||||||
expected,
|
expected,
|
||||||
actual,
|
actual,
|
||||||
@ -657,6 +689,9 @@ impl Display for AnalysisError {
|
|||||||
"Index {} out of bounds for list {} with length {}",
|
"Index {} out of bounds for list {} with length {}",
|
||||||
index_value, list, length
|
index_value, list, length
|
||||||
),
|
),
|
||||||
|
AnalysisError::NegativeIndex {
|
||||||
|
list, index_value, ..
|
||||||
|
} => write!(f, "Negative index {} for list {}", index_value, list),
|
||||||
AnalysisError::TypeConflict {
|
AnalysisError::TypeConflict {
|
||||||
actual_expression: actual_statement,
|
actual_expression: actual_statement,
|
||||||
actual_type,
|
actual_type,
|
||||||
@ -798,7 +833,17 @@ mod tests {
|
|||||||
fn malformed_list_index() {
|
fn malformed_list_index() {
|
||||||
let source = "[1, 2, 3]['foo']";
|
let source = "[1, 2, 3]['foo']";
|
||||||
|
|
||||||
assert_eq!(analyze(source), todo!());
|
assert_eq!(
|
||||||
|
analyze(source),
|
||||||
|
Err(DustError::Analysis {
|
||||||
|
analysis_error: AnalysisError::ExpectedType {
|
||||||
|
expected: Type::Integer,
|
||||||
|
actual: Type::String { length: Some(3) },
|
||||||
|
actual_expression: Expression::literal("foo", (10, 15)),
|
||||||
|
},
|
||||||
|
source,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -829,19 +874,22 @@ mod tests {
|
|||||||
fn integer_plus_boolean() {
|
fn integer_plus_boolean() {
|
||||||
let source = "42 + true";
|
let source = "42 + true";
|
||||||
|
|
||||||
assert_eq!(analyze(source), todo!());
|
assert_eq!(
|
||||||
|
analyze(source),
|
||||||
|
Err(DustError::Analysis {
|
||||||
|
analysis_error: AnalysisError::ExpectedType {
|
||||||
|
expected: Type::Integer,
|
||||||
|
actual: Type::Boolean,
|
||||||
|
actual_expression: Expression::literal(true, (5, 9)),
|
||||||
|
},
|
||||||
|
source,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_even_expects_number() {
|
fn nonexistant_field() {
|
||||||
let source = "is_even('hello')";
|
let source = "'hello'.foo";
|
||||||
|
|
||||||
assert_eq!(analyze(source), todo!());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn is_odd_expects_number() {
|
|
||||||
let source = "is_odd('hello')";
|
|
||||||
|
|
||||||
assert_eq!(analyze(source), todo!());
|
assert_eq!(analyze(source), todo!());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user