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) => {
|
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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user