Pass tests
This commit is contained in:
parent
4846b3f74d
commit
169c1a9e3f
@ -16,7 +16,8 @@ use crate::{
|
|||||||
LoopExpression, MapExpression, Node, OperatorExpression, RangeExpression, Span, Statement,
|
LoopExpression, MapExpression, Node, OperatorExpression, RangeExpression, Span, Statement,
|
||||||
StructDefinition, StructExpression, TupleAccessExpression,
|
StructDefinition, StructExpression, TupleAccessExpression,
|
||||||
},
|
},
|
||||||
parse, Context, ContextError, DustError, Expression, Identifier, StructType, Type,
|
core_library, parse, Context, ContextError, DustError, Expression, Identifier, StructType,
|
||||||
|
Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Analyzes the abstract syntax tree for errors.
|
/// Analyzes the abstract syntax tree for errors.
|
||||||
@ -32,7 +33,7 @@ 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 = Context::new();
|
let context = core_library().create_child();
|
||||||
let analyzer = Analyzer::new(&abstract_tree, context);
|
let analyzer = Analyzer::new(&abstract_tree, context);
|
||||||
|
|
||||||
analyzer
|
analyzer
|
||||||
@ -149,7 +150,7 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_expression(&'recovered self, expression: &Expression) -> Result<(), AnalysisError> {
|
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::Call(call_expression) => {
|
Expression::Call(call_expression) => {
|
||||||
@ -173,8 +174,15 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
|||||||
self.analyze_expression(expression.inner.as_ref())?;
|
self.analyze_expression(expression.inner.as_ref())?;
|
||||||
}
|
}
|
||||||
Expression::Identifier(identifier) => {
|
Expression::Identifier(identifier) => {
|
||||||
self.context
|
let found = self
|
||||||
|
.context
|
||||||
.update_last_position(&identifier.inner, identifier.position)?;
|
.update_last_position(&identifier.inner, identifier.position)?;
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return Err(AnalysisError::UndefinedVariable {
|
||||||
|
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() {
|
||||||
@ -573,7 +581,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn subtract_assign_wrong_type() {
|
fn subtract_assign_wrong_type() {
|
||||||
let source = "
|
let source = "
|
||||||
a = 1
|
let mut a = 1;
|
||||||
a -= 1.0
|
a -= 1.0
|
||||||
";
|
";
|
||||||
|
|
||||||
@ -674,6 +682,14 @@ mod tests {
|
|||||||
fn undefined_variable() {
|
fn undefined_variable() {
|
||||||
let source = "foo";
|
let source = "foo";
|
||||||
|
|
||||||
assert_eq!(analyze(source), todo!());
|
assert_eq!(
|
||||||
|
analyze(source),
|
||||||
|
Err(DustError::Analysis {
|
||||||
|
analysis_error: AnalysisError::UndefinedVariable {
|
||||||
|
identifier: Node::new(Identifier::new("foo"), (0, 3))
|
||||||
|
},
|
||||||
|
source,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ pub enum AstError {
|
|||||||
impl AstError {
|
impl AstError {
|
||||||
pub fn position(&self) -> Option<Span> {
|
pub fn position(&self) -> Option<Span> {
|
||||||
match self {
|
match self {
|
||||||
AstError::ContextError(error) => None,
|
AstError::ContextError(_) => None,
|
||||||
AstError::ExpectedType { position } => Some(*position),
|
AstError::ExpectedType { position } => Some(*position),
|
||||||
AstError::ExpectedTupleType { position } => Some(*position),
|
AstError::ExpectedTupleType { position } => Some(*position),
|
||||||
AstError::ExpectedNonEmptyList { position } => Some(*position),
|
AstError::ExpectedNonEmptyList { position } => Some(*position),
|
||||||
|
@ -47,7 +47,13 @@ impl Context {
|
|||||||
|
|
||||||
/// Returns a boolean indicating whether the identifier is in the context.
|
/// Returns a boolean indicating whether the identifier is in the context.
|
||||||
pub fn contains(&self, identifier: &Identifier) -> Result<bool, ContextError> {
|
pub fn contains(&self, identifier: &Identifier) -> Result<bool, ContextError> {
|
||||||
Ok(self.associations.read()?.contains_key(identifier))
|
if self.associations.read()?.contains_key(identifier) {
|
||||||
|
Ok(true)
|
||||||
|
} else if let Some(parent) = &self.parent {
|
||||||
|
parent.contains(identifier)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the full ContextData and Span if the context contains the given identifier.
|
/// Returns the full ContextData and Span if the context contains the given identifier.
|
||||||
@ -224,7 +230,9 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Updates an associated identifier's last known position, allowing it to live longer in the
|
/// Updates an associated identifier's last known position, allowing it to live longer in the
|
||||||
/// program. Returns a boolean indicating whether the identifier.
|
/// program. Returns a boolean indicating whether the identifier was found. If the identifier is
|
||||||
|
/// not found in the current context, the parent context is searched but parent context's
|
||||||
|
/// position is not updated.
|
||||||
pub fn update_last_position(
|
pub fn update_last_position(
|
||||||
&self,
|
&self,
|
||||||
identifier: &Identifier,
|
identifier: &Identifier,
|
||||||
@ -236,6 +244,8 @@ impl Context {
|
|||||||
log::trace!("Updating {identifier}'s last position to {position:?}");
|
log::trace!("Updating {identifier}'s last position to {position:?}");
|
||||||
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
} else if let Some(parent) = &self.parent {
|
||||||
|
parent.contains(identifier)
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
@ -1462,8 +1462,6 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn while_loop() {
|
fn while_loop() {
|
||||||
env_logger::builder().is_test(true).try_init().ok();
|
|
||||||
|
|
||||||
let input = "let mut x = 0; while x < 5 { x += 1 } x";
|
let input = "let mut x = 0; while x < 5 { x += 1 } x";
|
||||||
|
|
||||||
assert_eq!(run(input), Ok(Some(Value::mutable_from(5))));
|
assert_eq!(run(input), Ok(Some(Value::mutable_from(5))));
|
||||||
|
Loading…
Reference in New Issue
Block a user