2019-04-11 15:23:14 -07:00
|
|
|
use crate::common::*;
|
2017-11-16 23:30:08 -08:00
|
|
|
|
2017-11-17 17:28:06 -08:00
|
|
|
use CompilationErrorKind::*;
|
|
|
|
|
2019-12-07 03:09:21 -08:00
|
|
|
pub(crate) struct AssignmentResolver<'src: 'run, 'run> {
|
|
|
|
assignments: &'run Table<'src, Assignment<'src>>,
|
2020-02-10 20:07:06 -08:00
|
|
|
stack: Vec<&'src str>,
|
|
|
|
seen: BTreeSet<&'src str>,
|
|
|
|
evaluated: BTreeSet<&'src str>,
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
|
|
|
|
2019-12-07 03:09:21 -08:00
|
|
|
impl<'src: 'run, 'run> AssignmentResolver<'src, 'run> {
|
2019-09-21 15:35:03 -07:00
|
|
|
pub(crate) fn resolve_assignments(
|
2019-12-07 03:09:21 -08:00
|
|
|
assignments: &Table<'src, Assignment<'src>>,
|
|
|
|
) -> CompilationResult<'src, ()> {
|
2017-11-18 01:18:04 -08:00
|
|
|
let mut resolver = AssignmentResolver {
|
2018-12-08 14:29:41 -08:00
|
|
|
stack: empty(),
|
|
|
|
seen: empty(),
|
2018-03-05 13:21:35 -08:00
|
|
|
evaluated: empty(),
|
|
|
|
assignments,
|
2017-11-18 01:18:04 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
for name in assignments.keys() {
|
|
|
|
resolver.resolve_assignment(name)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-12-07 03:09:21 -08:00
|
|
|
fn resolve_assignment(&mut self, name: &'src str) -> CompilationResult<'src, ()> {
|
2017-11-16 23:30:08 -08:00
|
|
|
if self.evaluated.contains(name) {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
self.seen.insert(name);
|
|
|
|
self.stack.push(name);
|
|
|
|
|
2019-11-07 10:55:15 -08:00
|
|
|
if let Some(assignment) = self.assignments.get(name) {
|
2019-12-07 03:09:21 -08:00
|
|
|
self.resolve_expression(&assignment.value)?;
|
2017-11-16 23:30:08 -08:00
|
|
|
self.evaluated.insert(name);
|
|
|
|
} else {
|
|
|
|
let message = format!("attempted to resolve unknown assignment `{}`", name);
|
2019-11-13 19:32:50 -08:00
|
|
|
let token = Token {
|
2020-02-10 20:07:06 -08:00
|
|
|
src: "",
|
2019-04-15 22:40:02 -07:00
|
|
|
offset: 0,
|
2020-02-10 20:07:06 -08:00
|
|
|
line: 0,
|
2017-11-16 23:30:08 -08:00
|
|
|
column: 0,
|
2019-11-13 19:32:50 -08:00
|
|
|
length: 0,
|
2020-02-10 20:07:06 -08:00
|
|
|
kind: TokenKind::Unspecified,
|
2019-11-13 19:32:50 -08:00
|
|
|
};
|
|
|
|
return Err(CompilationError {
|
2018-12-08 14:29:41 -08:00
|
|
|
kind: Internal { message },
|
2019-11-13 19:32:50 -08:00
|
|
|
token,
|
2017-11-16 23:30:08 -08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-12-07 03:09:21 -08:00
|
|
|
fn resolve_expression(&mut self, expression: &Expression<'src>) -> CompilationResult<'src, ()> {
|
2019-04-11 23:58:08 -07:00
|
|
|
match expression {
|
2019-11-07 10:55:15 -08:00
|
|
|
Expression::Variable { name } => {
|
|
|
|
let variable = name.lexeme();
|
|
|
|
if self.evaluated.contains(variable) {
|
2019-11-21 10:14:10 -08:00
|
|
|
Ok(())
|
2019-11-07 10:55:15 -08:00
|
|
|
} else if self.seen.contains(variable) {
|
|
|
|
let token = self.assignments[variable].name.token();
|
|
|
|
self.stack.push(variable);
|
2019-11-21 10:14:10 -08:00
|
|
|
Err(token.error(CircularVariableDependency {
|
Gargantuan refactor (#522)
- Instead of changing the current directory with `env::set_current_dir`
to be implicitly inherited by subprocesses, we now use
`Command::current_dir` to set it explicitly. This feels much better,
since we aren't dependent on the implicit state of the process's
current directory.
- Subcommand execution is much improved.
- Added a ton of tests for config parsing, config execution, working
dir, and search dir.
- Error messages are improved. Many more will be colored.
- The Config is now onwed, instead of borrowing from the arguments and
the `clap::ArgMatches` object. This is a huge ergonomic improvement,
especially in tests, and I don't think anyone will notice.
- `--edit` now uses `$VISUAL`, `$EDITOR`, or `vim`, in that order,
matching git, which I think is what most people will expect.
- Added a cute `tmptree!{}` macro, for creating temporary directories
populated with directories and files for tests.
- Admitted that grammer is LL(k) and I don't know what `k` is.
2019-11-09 21:43:20 -08:00
|
|
|
variable,
|
2018-12-08 14:29:41 -08:00
|
|
|
circle: self.stack.clone(),
|
2019-11-21 10:14:10 -08:00
|
|
|
}))
|
2019-11-07 10:55:15 -08:00
|
|
|
} else if self.assignments.contains_key(variable) {
|
2019-11-21 10:14:10 -08:00
|
|
|
self.resolve_assignment(variable)
|
2017-11-16 23:30:08 -08:00
|
|
|
} else {
|
2019-11-21 10:14:10 -08:00
|
|
|
Err(name.token().error(UndefinedVariable { variable }))
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
2020-02-10 20:07:06 -08:00
|
|
|
},
|
2019-11-21 10:14:10 -08:00
|
|
|
Expression::Call { thunk } => match thunk {
|
|
|
|
Thunk::Nullary { .. } => Ok(()),
|
|
|
|
Thunk::Unary { arg, .. } => self.resolve_expression(arg),
|
|
|
|
Thunk::Binary { args: [a, b], .. } => {
|
|
|
|
self.resolve_expression(a)?;
|
|
|
|
self.resolve_expression(b)
|
2020-02-10 20:07:06 -08:00
|
|
|
},
|
2019-11-21 10:14:10 -08:00
|
|
|
},
|
2019-11-07 10:55:15 -08:00
|
|
|
Expression::Concatination { lhs, rhs } => {
|
2017-11-16 23:30:08 -08:00
|
|
|
self.resolve_expression(lhs)?;
|
2019-11-21 10:14:10 -08:00
|
|
|
self.resolve_expression(rhs)
|
2020-02-10 20:07:06 -08:00
|
|
|
},
|
2019-11-21 10:14:10 -08:00
|
|
|
Expression::StringLiteral { .. } | Expression::Backtick { .. } => Ok(()),
|
|
|
|
Expression::Group { contents } => self.resolve_expression(contents),
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
2019-11-07 10:55:15 -08:00
|
|
|
mod tests {
|
2017-11-16 23:30:08 -08:00
|
|
|
use super::*;
|
|
|
|
|
2019-11-07 10:55:15 -08:00
|
|
|
analysis_error! {
|
2017-11-18 01:44:59 -08:00
|
|
|
name: circular_variable_dependency,
|
|
|
|
input: "a = b\nb = a",
|
2019-04-15 22:40:02 -07:00
|
|
|
offset: 0,
|
2017-11-18 01:44:59 -08:00
|
|
|
line: 0,
|
|
|
|
column: 0,
|
2019-04-15 22:40:02 -07:00
|
|
|
width: 1,
|
2017-11-18 01:44:59 -08:00
|
|
|
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
|
|
|
|
2019-11-07 10:55:15 -08:00
|
|
|
analysis_error! {
|
2017-11-18 01:44:59 -08:00
|
|
|
name: self_variable_dependency,
|
|
|
|
input: "a = a",
|
2019-04-15 22:40:02 -07:00
|
|
|
offset: 0,
|
2017-11-18 01:44:59 -08:00
|
|
|
line: 0,
|
|
|
|
column: 0,
|
2019-04-15 22:40:02 -07:00
|
|
|
width: 1,
|
2017-11-18 01:44:59 -08:00
|
|
|
kind: CircularVariableDependency{variable: "a", circle: vec!["a", "a"]},
|
|
|
|
}
|
|
|
|
|
2019-11-07 10:55:15 -08:00
|
|
|
analysis_error! {
|
2017-11-18 01:44:59 -08:00
|
|
|
name: unknown_expression_variable,
|
|
|
|
input: "x = yy",
|
2019-04-15 22:40:02 -07:00
|
|
|
offset: 4,
|
2017-11-18 01:44:59 -08:00
|
|
|
line: 0,
|
|
|
|
column: 4,
|
2019-04-15 22:40:02 -07:00
|
|
|
width: 2,
|
2017-11-18 01:44:59 -08:00
|
|
|
kind: UndefinedVariable{variable: "yy"},
|
2017-11-17 17:28:06 -08:00
|
|
|
}
|
2017-12-02 05:37:10 -08:00
|
|
|
|
2019-11-07 10:55:15 -08:00
|
|
|
analysis_error! {
|
2019-11-21 10:14:10 -08:00
|
|
|
name: unknown_function_parameter,
|
|
|
|
input: "x := env_var(yy)",
|
|
|
|
offset: 13,
|
2017-12-02 05:37:10 -08:00
|
|
|
line: 0,
|
2019-11-21 10:14:10 -08:00
|
|
|
column: 13,
|
|
|
|
width: 2,
|
|
|
|
kind: UndefinedVariable{variable: "yy"},
|
|
|
|
}
|
|
|
|
|
|
|
|
analysis_error! {
|
|
|
|
name: unknown_function_parameter_binary_first,
|
|
|
|
input: "x := env_var_or_default(yy, 'foo')",
|
|
|
|
offset: 24,
|
|
|
|
line: 0,
|
|
|
|
column: 24,
|
|
|
|
width: 2,
|
|
|
|
kind: UndefinedVariable{variable: "yy"},
|
|
|
|
}
|
|
|
|
|
|
|
|
analysis_error! {
|
|
|
|
name: unknown_function_parameter_binary_second,
|
|
|
|
input: "x := env_var_or_default('foo', yy)",
|
|
|
|
offset: 31,
|
|
|
|
line: 0,
|
|
|
|
column: 31,
|
|
|
|
width: 2,
|
|
|
|
kind: UndefinedVariable{variable: "yy"},
|
2017-12-02 05:37:10 -08:00
|
|
|
}
|
2017-11-16 23:30:08 -08:00
|
|
|
}
|