just/src/assignment_resolver.rs

136 lines
3.4 KiB
Rust
Raw Normal View History

2017-11-16 23:30:08 -08:00
use common::*;
2017-11-17 17:28:06 -08:00
use CompilationErrorKind::*;
2017-11-16 23:30:08 -08:00
pub fn resolve_assignments<'a>(
assignments: &Map<&'a str, Expression<'a>>,
assignment_tokens: &Map<&'a str, Token<'a>>,
2017-11-17 17:28:06 -08:00
) -> CompilationResult<'a, ()> {
2017-11-16 23:30:08 -08:00
let mut resolver = AssignmentResolver {
assignments: assignments,
assignment_tokens: assignment_tokens,
stack: empty(),
seen: empty(),
evaluated: empty(),
};
for name in assignments.keys() {
resolver.resolve_assignment(name)?;
}
Ok(())
}
struct AssignmentResolver<'a: 'b, 'b> {
assignments: &'b Map<&'a str, Expression<'a>>,
assignment_tokens: &'b Map<&'a str, Token<'a>>,
stack: Vec<&'a str>,
seen: Set<&'a str>,
evaluated: Set<&'a str>,
}
impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
2017-11-17 17:28:06 -08:00
fn resolve_assignment(&mut self, name: &'a str) -> CompilationResult<'a, ()> {
2017-11-16 23:30:08 -08:00
if self.evaluated.contains(name) {
return Ok(());
}
self.seen.insert(name);
self.stack.push(name);
if let Some(expression) = self.assignments.get(name) {
self.resolve_expression(expression)?;
self.evaluated.insert(name);
} else {
let message = format!("attempted to resolve unknown assignment `{}`", name);
return Err(CompilationError {
text: "",
index: 0,
line: 0,
column: 0,
width: None,
2017-11-17 17:28:06 -08:00
kind: Internal{message}
2017-11-16 23:30:08 -08:00
});
}
Ok(())
}
2017-11-17 17:28:06 -08:00
fn resolve_expression(
&mut self, expression: &Expression<'a>) -> CompilationResult<'a, ()> {
2017-11-16 23:30:08 -08:00
match *expression {
Expression::Variable{name, ref token} => {
if self.evaluated.contains(name) {
return Ok(());
} else if self.seen.contains(name) {
let token = &self.assignment_tokens[name];
self.stack.push(name);
2017-11-17 17:28:06 -08:00
return Err(token.error(CircularVariableDependency {
2017-11-16 23:30:08 -08:00
variable: name,
circle: self.stack.clone(),
}));
} else if self.assignments.contains_key(name) {
self.resolve_assignment(name)?;
} else {
2017-11-17 17:28:06 -08:00
return Err(token.error(UndefinedVariable{variable: name}));
2017-11-16 23:30:08 -08:00
}
}
Expression::Concatination{ref lhs, ref rhs} => {
self.resolve_expression(lhs)?;
self.resolve_expression(rhs)?;
}
Expression::String{..} | Expression::Backtick{..} => {}
}
Ok(())
}
}
#[cfg(test)]
mod test {
use testing::parse_error;
use super::*;
2017-11-17 17:28:06 -08:00
#[test]
fn circular_variable_dependency() {
let text = "a = b\nb = a";
let variable = "a";
let circle = vec!["a", "b", "a"];
parse_error(text, CompilationError {
text: text,
index: 0,
line: 0,
column: 0,
width: Some(1),
kind: CircularVariableDependency{variable, circle}
});
}
2017-11-16 23:30:08 -08:00
2017-11-17 17:28:06 -08:00
#[test]
fn self_variable_dependency() {
let text = "a = a";
let variable = "a";
let circle = vec!["a", "a"];
parse_error(text, CompilationError {
text: text,
index: 0,
line: 0,
column: 0,
width: Some(1),
kind: CircularVariableDependency{variable, circle}
});
}
2017-11-16 23:30:08 -08:00
2017-11-17 17:28:06 -08:00
#[test]
fn unknown_expression_variable() {
let text = "x = yy";
parse_error(text, CompilationError {
text: text,
index: 4,
line: 0,
column: 4,
width: Some(2),
kind: UndefinedVariable{variable: "yy"},
});
}
2017-11-16 23:30:08 -08:00
}