130 lines
3.3 KiB
Rust
130 lines
3.3 KiB
Rust
|
use common::*;
|
||
|
|
||
|
pub fn resolve_assignments<'a>(
|
||
|
assignments: &Map<&'a str, Expression<'a>>,
|
||
|
assignment_tokens: &Map<&'a str, Token<'a>>,
|
||
|
) -> Result<(), CompilationError<'a>> {
|
||
|
|
||
|
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> {
|
||
|
fn resolve_assignment(&mut self, name: &'a str) -> Result<(), CompilationError<'a>> {
|
||
|
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,
|
||
|
kind: CompilationErrorKind::Internal{message}
|
||
|
});
|
||
|
}
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
fn resolve_expression(&mut self, expression: &Expression<'a>) -> Result<(), CompilationError<'a>> {
|
||
|
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);
|
||
|
return Err(token.error(CompilationErrorKind::CircularVariableDependency {
|
||
|
variable: name,
|
||
|
circle: self.stack.clone(),
|
||
|
}));
|
||
|
} else if self.assignments.contains_key(name) {
|
||
|
self.resolve_assignment(name)?;
|
||
|
} else {
|
||
|
return Err(token.error(CompilationErrorKind::UndefinedVariable{variable: name}));
|
||
|
}
|
||
|
}
|
||
|
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::*;
|
||
|
|
||
|
#[test]
|
||
|
fn circular_variable_dependency() {
|
||
|
let text = "a = b\nb = a";
|
||
|
parse_error(text, CompilationError {
|
||
|
text: text,
|
||
|
index: 0,
|
||
|
line: 0,
|
||
|
column: 0,
|
||
|
width: Some(1),
|
||
|
kind: CompilationErrorKind::CircularVariableDependency{variable: "a", circle: vec!["a", "b", "a"]}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
|
||
|
#[test]
|
||
|
fn self_variable_dependency() {
|
||
|
let text = "a = a";
|
||
|
parse_error(text, CompilationError {
|
||
|
text: text,
|
||
|
index: 0,
|
||
|
line: 0,
|
||
|
column: 0,
|
||
|
width: Some(1),
|
||
|
kind: CompilationErrorKind::CircularVariableDependency{variable: "a", circle: vec!["a", "a"]}
|
||
|
});
|
||
|
}
|
||
|
#[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: CompilationErrorKind::UndefinedVariable{variable: "yy"},
|
||
|
});
|
||
|
}
|
||
|
|
||
|
}
|