Clean up; Add an analyzer test
This commit is contained in:
parent
a46e5dd365
commit
1687fd7fe3
@ -160,7 +160,9 @@ impl<'a> Analyzer<'a> {
|
||||
}
|
||||
}
|
||||
Statement::Identifier(identifier) => {
|
||||
if !self.context.contains(identifier) {
|
||||
let exists = self.context.add_allowed_use(identifier);
|
||||
|
||||
if !exists {
|
||||
return Err(AnalyzerError::UndefinedVariable {
|
||||
identifier: node.clone(),
|
||||
});
|
||||
@ -241,11 +243,6 @@ pub enum AnalyzerError {
|
||||
actual: Node<Statement>,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedSameType {
|
||||
left: Node<Statement>,
|
||||
right: Node<Statement>,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedString {
|
||||
actual: Node<Statement>,
|
||||
position: (usize, usize),
|
||||
@ -278,7 +275,6 @@ impl AnalyzerError {
|
||||
AnalyzerError::ExpectedInteger { position, .. } => *position,
|
||||
AnalyzerError::ExpectedIntegerOrFloat { position, .. } => *position,
|
||||
AnalyzerError::ExpectedIntegerFloatOrString { position, .. } => *position,
|
||||
AnalyzerError::ExpectedSameType { position, .. } => *position,
|
||||
AnalyzerError::ExpectedString { position, .. } => *position,
|
||||
AnalyzerError::UndefinedVariable { identifier } => identifier.position,
|
||||
AnalyzerError::UnexpectedIdentifier { position, .. } => *position,
|
||||
@ -313,9 +309,6 @@ impl Display for AnalyzerError {
|
||||
AnalyzerError::ExpectedIntegerFloatOrString { 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, .. } => {
|
||||
write!(f, "Expected string, found {}", actual)
|
||||
}
|
||||
@ -341,6 +334,31 @@ mod tests {
|
||||
|
||||
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]
|
||||
fn float_plus_integer() {
|
||||
let abstract_tree = AbstractSyntaxTree {
|
||||
|
@ -31,9 +31,13 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_value(&self, identifier: &Identifier) -> Option<&Value> {
|
||||
match self.variables.get(identifier) {
|
||||
Some((VariableData::Value(value), _)) => Some(value),
|
||||
pub fn use_value(&mut self, identifier: &Identifier) -> Option<&Value> {
|
||||
match self.variables.get_mut(identifier) {
|
||||
Some((VariableData::Value(value), usage_data)) => {
|
||||
usage_data.used += 1;
|
||||
|
||||
Some(value)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -63,6 +67,16 @@ impl Context {
|
||||
self.variables
|
||||
.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 {
|
||||
|
@ -90,7 +90,7 @@ impl Vm {
|
||||
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 {
|
||||
identifier: Node::new(
|
||||
Statement::Identifier(identifier.clone()),
|
||||
@ -236,7 +236,7 @@ impl Vm {
|
||||
Ok(function.clone().call(None, value_parameters, context)?)
|
||||
}
|
||||
Statement::Identifier(identifier) => {
|
||||
if let Some(value) = context.get_value(&identifier) {
|
||||
if let Some(value) = context.use_value(&identifier) {
|
||||
Ok(Some(value.clone()))
|
||||
} else {
|
||||
Err(VmError::UndefinedVariable {
|
||||
|
@ -1,14 +1,12 @@
|
||||
use std.io
|
||||
|
||||
count = 1
|
||||
|
||||
while count <= 15 {
|
||||
divides_by_3 = count % 3 == 0
|
||||
divides_by_5 = count % 5 == 0
|
||||
|
||||
output = if divides_by_3 && divides_by_5 {
|
||||
output = if divides_by_3 && divides_by_5 {
|
||||
'fizzbuzz'
|
||||
} else if divides_by_3 {
|
||||
} else if divides_by_3 {
|
||||
'fizz'
|
||||
} else if divides_by_5 {
|
||||
'buzz'
|
||||
@ -16,8 +14,7 @@ while count <= 15 {
|
||||
count as str
|
||||
}
|
||||
|
||||
io.write_line(output)
|
||||
|
||||
write_line(output)
|
||||
|
||||
count += 1
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user