just/src/assignment_resolver.rs

133 lines
3.3 KiB
Rust
Raw Normal View History

use crate::common::*;
2017-11-16 23:30:08 -08:00
2017-11-17 17:28:06 -08:00
use CompilationErrorKind::*;
pub(crate) struct AssignmentResolver<'a: 'b, 'b> {
assignments: &'b BTreeMap<&'a str, Assignment<'a>>,
stack: Vec<&'a str>,
seen: BTreeSet<&'a str>,
evaluated: BTreeSet<&'a str>,
2017-11-16 23:30:08 -08:00
}
impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
pub(crate) fn resolve_assignments(
assignments: &BTreeMap<&'a str, Assignment<'a>>,
) -> CompilationResult<'a, ()> {
let mut resolver = AssignmentResolver {
stack: empty(),
seen: empty(),
2018-03-05 13:21:35 -08:00
evaluated: empty(),
assignments,
};
for name in assignments.keys() {
resolver.resolve_assignment(name)?;
}
Ok(())
}
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(assignment) = self.assignments.get(name) {
self.resolve_expression(&assignment.expression)?;
2017-11-16 23:30:08 -08:00
self.evaluated.insert(name);
} else {
let message = format!("attempted to resolve unknown assignment `{}`", name);
return Err(CompilationError {
src: "",
offset: 0,
line: 0,
2017-11-16 23:30:08 -08:00
column: 0,
width: 0,
kind: Internal { message },
2017-11-16 23:30:08 -08:00
});
}
Ok(())
}
fn resolve_expression(&mut self, expression: &Expression<'a>) -> CompilationResult<'a, ()> {
match expression {
Expression::Variable { name } => {
let variable = name.lexeme();
if self.evaluated.contains(variable) {
2017-11-16 23:30:08 -08:00
return Ok(());
} else if self.seen.contains(variable) {
let token = self.assignments[variable].name.token();
self.stack.push(variable);
2017-11-17 17:28:06 -08:00
return Err(token.error(CircularVariableDependency {
variable: variable,
circle: self.stack.clone(),
2017-11-16 23:30:08 -08:00
}));
} else if self.assignments.contains_key(variable) {
self.resolve_assignment(variable)?;
2017-11-16 23:30:08 -08:00
} else {
return Err(name.token().error(UndefinedVariable { variable }));
2017-11-16 23:30:08 -08:00
}
}
Expression::Call {
function,
arguments,
} => Function::resolve(&function.token(), arguments.len())?,
Expression::Concatination { lhs, rhs } => {
2017-11-16 23:30:08 -08:00
self.resolve_expression(lhs)?;
self.resolve_expression(rhs)?;
}
Expression::StringLiteral { .. } | Expression::Backtick { .. } => {}
Expression::Group { contents } => self.resolve_expression(contents)?,
2017-11-16 23:30:08 -08:00
}
Ok(())
}
}
#[cfg(test)]
mod tests {
2017-11-16 23:30:08 -08:00
use super::*;
analysis_error! {
name: circular_variable_dependency,
input: "a = b\nb = a",
offset: 0,
line: 0,
column: 0,
width: 1,
kind: CircularVariableDependency{variable: "a", circle: vec!["a", "b", "a"]},
2017-11-17 17:28:06 -08:00
}
2017-11-16 23:30:08 -08:00
analysis_error! {
name: self_variable_dependency,
input: "a = a",
offset: 0,
line: 0,
column: 0,
width: 1,
kind: CircularVariableDependency{variable: "a", circle: vec!["a", "a"]},
}
analysis_error! {
name: unknown_expression_variable,
input: "x = yy",
offset: 4,
line: 0,
column: 4,
width: 2,
kind: UndefinedVariable{variable: "yy"},
2017-11-17 17:28:06 -08:00
}
analysis_error! {
name: unknown_function,
input: "a = foo()",
offset: 4,
line: 0,
column: 4,
width: 3,
kind: UnknownFunction{function: "foo"},
}
2017-11-16 23:30:08 -08:00
}