Clean up; Add an analyzer test

This commit is contained in:
Jeff 2024-08-10 04:45:30 -04:00
parent a46e5dd365
commit 1687fd7fe3
4 changed files with 51 additions and 22 deletions

View File

@ -160,7 +160,9 @@ impl<'a> Analyzer<'a> {
} }
} }
Statement::Identifier(identifier) => { Statement::Identifier(identifier) => {
if !self.context.contains(identifier) { let exists = self.context.add_allowed_use(identifier);
if !exists {
return Err(AnalyzerError::UndefinedVariable { return Err(AnalyzerError::UndefinedVariable {
identifier: node.clone(), identifier: node.clone(),
}); });
@ -241,11 +243,6 @@ pub enum AnalyzerError {
actual: Node<Statement>, actual: Node<Statement>,
position: Span, position: Span,
}, },
ExpectedSameType {
left: Node<Statement>,
right: Node<Statement>,
position: Span,
},
ExpectedString { ExpectedString {
actual: Node<Statement>, actual: Node<Statement>,
position: (usize, usize), position: (usize, usize),
@ -278,7 +275,6 @@ impl AnalyzerError {
AnalyzerError::ExpectedInteger { position, .. } => *position, AnalyzerError::ExpectedInteger { position, .. } => *position,
AnalyzerError::ExpectedIntegerOrFloat { position, .. } => *position, AnalyzerError::ExpectedIntegerOrFloat { position, .. } => *position,
AnalyzerError::ExpectedIntegerFloatOrString { position, .. } => *position, AnalyzerError::ExpectedIntegerFloatOrString { position, .. } => *position,
AnalyzerError::ExpectedSameType { position, .. } => *position,
AnalyzerError::ExpectedString { position, .. } => *position, AnalyzerError::ExpectedString { position, .. } => *position,
AnalyzerError::UndefinedVariable { identifier } => identifier.position, AnalyzerError::UndefinedVariable { identifier } => identifier.position,
AnalyzerError::UnexpectedIdentifier { position, .. } => *position, AnalyzerError::UnexpectedIdentifier { position, .. } => *position,
@ -313,9 +309,6 @@ impl Display for AnalyzerError {
AnalyzerError::ExpectedIntegerFloatOrString { actual, .. } => { AnalyzerError::ExpectedIntegerFloatOrString { actual, .. } => {
write!(f, "Expected integer, float, or string, found {}", actual) write!(f, "Expected integer, float, or string, found {}", actual)
} }
AnalyzerError::ExpectedSameType { left, right, .. } => {
write!(f, "Expected same type, found {} and {}", left, right)
}
AnalyzerError::ExpectedString { actual, .. } => { AnalyzerError::ExpectedString { actual, .. } => {
write!(f, "Expected string, found {}", actual) write!(f, "Expected string, found {}", actual)
} }
@ -341,6 +334,31 @@ mod tests {
use super::*; use super::*;
#[test]
fn math_requires_same_types() {
let abstract_tree = AbstractSyntaxTree {
nodes: [Node::new(
Statement::BinaryOperation {
left: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
operator: Node::new(BinaryOperator::Add, (1, 2)),
right: Box::new(Node::new(Statement::Constant(Value::float(1.0)), (3, 4))),
},
(0, 2),
)]
.into(),
};
let mut context = Context::new();
let mut analyzer = Analyzer::new(&abstract_tree, &mut context);
assert_eq!(
analyzer.analyze(),
Err(AnalyzerError::ExpectedInteger {
actual: Node::new(Statement::Constant(Value::float(1.0)), (3, 4)),
position: (3, 4)
})
)
}
#[test] #[test]
fn float_plus_integer() { fn float_plus_integer() {
let abstract_tree = AbstractSyntaxTree { let abstract_tree = AbstractSyntaxTree {

View File

@ -31,9 +31,13 @@ impl Context {
} }
} }
pub fn get_value(&self, identifier: &Identifier) -> Option<&Value> { pub fn use_value(&mut self, identifier: &Identifier) -> Option<&Value> {
match self.variables.get(identifier) { match self.variables.get_mut(identifier) {
Some((VariableData::Value(value), _)) => Some(value), Some((VariableData::Value(value), usage_data)) => {
usage_data.used += 1;
Some(value)
}
_ => None, _ => None,
} }
} }
@ -63,6 +67,16 @@ impl Context {
self.variables self.variables
.retain(|_, (_, usage_data)| usage_data.used < usage_data.allowed_uses); .retain(|_, (_, usage_data)| usage_data.used < usage_data.allowed_uses);
} }
pub fn add_allowed_use(&mut self, identifier: &Identifier) -> bool {
if let Some((_, usage_data)) = self.variables.get_mut(identifier) {
usage_data.allowed_uses += 1;
true
} else {
false
}
}
} }
impl Default for Context { impl Default for Context {

View File

@ -90,7 +90,7 @@ impl Vm {
position: right_position, position: right_position,
}); });
}; };
let left_value = context.get_value(&identifier).ok_or_else(|| { let left_value = context.use_value(&identifier).ok_or_else(|| {
VmError::UndefinedVariable { VmError::UndefinedVariable {
identifier: Node::new( identifier: Node::new(
Statement::Identifier(identifier.clone()), Statement::Identifier(identifier.clone()),
@ -236,7 +236,7 @@ impl Vm {
Ok(function.clone().call(None, value_parameters, context)?) Ok(function.clone().call(None, value_parameters, context)?)
} }
Statement::Identifier(identifier) => { Statement::Identifier(identifier) => {
if let Some(value) = context.get_value(&identifier) { if let Some(value) = context.use_value(&identifier) {
Ok(Some(value.clone())) Ok(Some(value.clone()))
} else { } else {
Err(VmError::UndefinedVariable { Err(VmError::UndefinedVariable {

View File

@ -1,14 +1,12 @@
use std.io
count = 1 count = 1
while count <= 15 { while count <= 15 {
divides_by_3 = count % 3 == 0 divides_by_3 = count % 3 == 0
divides_by_5 = count % 5 == 0 divides_by_5 = count % 5 == 0
output = if divides_by_3 && divides_by_5 { output = if divides_by_3 && divides_by_5 {
'fizzbuzz' 'fizzbuzz'
} else if divides_by_3 { } else if divides_by_3 {
'fizz' 'fizz'
} else if divides_by_5 { } else if divides_by_5 {
'buzz' 'buzz'
@ -16,8 +14,7 @@ while count <= 15 {
count as str count as str
} }
io.write_line(output) write_line(output)
count += 1 count += 1
} }