1
0

Pass tests

This commit is contained in:
Jeff 2024-08-20 12:24:41 -04:00
parent 4846b3f74d
commit 169c1a9e3f
4 changed files with 35 additions and 11 deletions

View File

@ -16,7 +16,8 @@ use crate::{
LoopExpression, MapExpression, Node, OperatorExpression, RangeExpression, Span, Statement,
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.
@ -32,7 +33,7 @@ use crate::{
/// ```
pub fn analyze(source: &str) -> Result<(), DustError> {
let abstract_tree = parse(source)?;
let context = Context::new();
let context = core_library().create_child();
let analyzer = Analyzer::new(&abstract_tree, context);
analyzer
@ -149,7 +150,7 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
Ok(())
}
fn analyze_expression(&'recovered self, expression: &Expression) -> Result<(), AnalysisError> {
fn analyze_expression(&self, expression: &Expression) -> Result<(), AnalysisError> {
match expression {
Expression::Block(block_expression) => self.analyze_block(&block_expression.inner)?,
Expression::Call(call_expression) => {
@ -173,8 +174,15 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
self.analyze_expression(expression.inner.as_ref())?;
}
Expression::Identifier(identifier) => {
self.context
let found = self
.context
.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::List(list_expression) => match list_expression.inner.as_ref() {
@ -573,7 +581,7 @@ mod tests {
#[test]
fn subtract_assign_wrong_type() {
let source = "
a = 1
let mut a = 1;
a -= 1.0
";
@ -674,6 +682,14 @@ mod tests {
fn undefined_variable() {
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,
})
);
}
}

View File

@ -72,7 +72,7 @@ pub enum AstError {
impl AstError {
pub fn position(&self) -> Option<Span> {
match self {
AstError::ContextError(error) => None,
AstError::ContextError(_) => None,
AstError::ExpectedType { position } => Some(*position),
AstError::ExpectedTupleType { position } => Some(*position),
AstError::ExpectedNonEmptyList { position } => Some(*position),

View File

@ -47,7 +47,13 @@ impl Context {
/// Returns a boolean indicating whether the identifier is in the context.
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.
@ -224,7 +230,9 @@ impl Context {
}
/// 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(
&self,
identifier: &Identifier,
@ -236,6 +244,8 @@ impl Context {
log::trace!("Updating {identifier}'s last position to {position:?}");
Ok(true)
} else if let Some(parent) = &self.parent {
parent.contains(identifier)
} else {
Ok(false)
}

View File

@ -1462,8 +1462,6 @@ mod tests {
#[test]
fn while_loop() {
env_logger::builder().is_test(true).try_init().ok();
let input = "let mut x = 0; while x < 5 { x += 1 } x";
assert_eq!(run(input), Ok(Some(Value::mutable_from(5))));