Refactor evaluators and resolvers into common form (#258)
This commit is contained in:
parent
bc79d16eac
commit
28a57d9828
@ -2,31 +2,6 @@ use common::*;
|
|||||||
|
|
||||||
use brev;
|
use brev;
|
||||||
|
|
||||||
pub fn evaluate_assignments<'a>(
|
|
||||||
assignments: &Map<&'a str, Expression<'a>>,
|
|
||||||
overrides: &Map<&str, &str>,
|
|
||||||
quiet: bool,
|
|
||||||
shell: &'a str,
|
|
||||||
dry_run: bool,
|
|
||||||
) -> RunResult<'a, Map<&'a str, String>> {
|
|
||||||
let mut evaluator = AssignmentEvaluator {
|
|
||||||
assignments: assignments,
|
|
||||||
evaluated: empty(),
|
|
||||||
exports: &empty(),
|
|
||||||
overrides: overrides,
|
|
||||||
quiet: quiet,
|
|
||||||
scope: &empty(),
|
|
||||||
shell: shell,
|
|
||||||
dry_run: dry_run,
|
|
||||||
};
|
|
||||||
|
|
||||||
for name in assignments.keys() {
|
|
||||||
evaluator.evaluate_assignment(name)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(evaluator.evaluated)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AssignmentEvaluator<'a: 'b, 'b> {
|
pub struct AssignmentEvaluator<'a: 'b, 'b> {
|
||||||
pub assignments: &'b Map<&'a str, Expression<'a>>,
|
pub assignments: &'b Map<&'a str, Expression<'a>>,
|
||||||
pub evaluated: Map<&'a str, String>,
|
pub evaluated: Map<&'a str, String>,
|
||||||
@ -39,6 +14,31 @@ pub struct AssignmentEvaluator<'a: 'b, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> AssignmentEvaluator<'a, 'b> {
|
impl<'a, 'b> AssignmentEvaluator<'a, 'b> {
|
||||||
|
pub fn evaluate_assignments(
|
||||||
|
assignments: &Map<&'a str, Expression<'a>>,
|
||||||
|
overrides: &Map<&str, &str>,
|
||||||
|
quiet: bool,
|
||||||
|
shell: &'a str,
|
||||||
|
dry_run: bool,
|
||||||
|
) -> RunResult<'a, Map<&'a str, String>> {
|
||||||
|
let mut evaluator = AssignmentEvaluator {
|
||||||
|
assignments: assignments,
|
||||||
|
evaluated: empty(),
|
||||||
|
exports: &empty(),
|
||||||
|
overrides: overrides,
|
||||||
|
quiet: quiet,
|
||||||
|
scope: &empty(),
|
||||||
|
shell: shell,
|
||||||
|
dry_run: dry_run,
|
||||||
|
};
|
||||||
|
|
||||||
|
for name in assignments.keys() {
|
||||||
|
evaluator.evaluate_assignment(name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(evaluator.evaluated)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn evaluate_line(
|
pub fn evaluate_line(
|
||||||
&mut self,
|
&mut self,
|
||||||
line: &[Fragment<'a>],
|
line: &[Fragment<'a>],
|
||||||
|
@ -2,27 +2,7 @@ use common::*;
|
|||||||
|
|
||||||
use CompilationErrorKind::*;
|
use CompilationErrorKind::*;
|
||||||
|
|
||||||
pub fn resolve_assignments<'a>(
|
pub struct AssignmentResolver<'a: 'b, 'b> {
|
||||||
assignments: &Map<&'a str, Expression<'a>>,
|
|
||||||
assignment_tokens: &Map<&'a str, Token<'a>>,
|
|
||||||
) -> CompilationResult<'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>>,
|
assignments: &'b Map<&'a str, Expression<'a>>,
|
||||||
assignment_tokens: &'b Map<&'a str, Token<'a>>,
|
assignment_tokens: &'b Map<&'a str, Token<'a>>,
|
||||||
stack: Vec<&'a str>,
|
stack: Vec<&'a str>,
|
||||||
@ -31,6 +11,26 @@ struct AssignmentResolver<'a: 'b, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
|
impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
|
||||||
|
pub fn resolve_assignments(
|
||||||
|
assignments: &Map<&'a str, Expression<'a>>,
|
||||||
|
assignment_tokens: &Map<&'a str, Token<'a>>,
|
||||||
|
) -> CompilationResult<'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(())
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_assignment(&mut self, name: &'a str) -> CompilationResult<'a, ()> {
|
fn resolve_assignment(&mut self, name: &'a str) -> CompilationResult<'a, ()> {
|
||||||
if self.evaluated.contains(name) {
|
if self.evaluated.contains(name) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use common::*;
|
use common::*;
|
||||||
|
|
||||||
use edit_distance::edit_distance;
|
use edit_distance::edit_distance;
|
||||||
use assignment_evaluator::evaluate_assignments;
|
|
||||||
use range_ext::RangeExt;
|
|
||||||
|
|
||||||
pub struct Justfile<'a> {
|
pub struct Justfile<'a> {
|
||||||
pub recipes: Map<&'a str, Recipe<'a>>,
|
pub recipes: Map<&'a str, Recipe<'a>>,
|
||||||
@ -55,7 +53,7 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
|||||||
return Err(RuntimeError::UnknownOverrides{overrides: unknown_overrides});
|
return Err(RuntimeError::UnknownOverrides{overrides: unknown_overrides});
|
||||||
}
|
}
|
||||||
|
|
||||||
let scope = evaluate_assignments(
|
let scope = AssignmentEvaluator::evaluate_assignments(
|
||||||
&self.assignments,
|
&self.assignments,
|
||||||
&configuration.overrides,
|
&configuration.overrides,
|
||||||
configuration.quiet,
|
configuration.quiet,
|
||||||
|
@ -53,6 +53,7 @@ mod common {
|
|||||||
pub use tempdir::TempDir;
|
pub use tempdir::TempDir;
|
||||||
|
|
||||||
pub use assignment_evaluator::AssignmentEvaluator;
|
pub use assignment_evaluator::AssignmentEvaluator;
|
||||||
|
pub use assignment_resolver::AssignmentResolver;
|
||||||
pub use command_ext::CommandExt;
|
pub use command_ext::CommandExt;
|
||||||
pub use compilation_error::{CompilationError, CompilationErrorKind, CompilationResult};
|
pub use compilation_error::{CompilationError, CompilationErrorKind, CompilationResult};
|
||||||
pub use configuration::Configuration;
|
pub use configuration::Configuration;
|
||||||
@ -63,7 +64,9 @@ mod common {
|
|||||||
pub use misc::{default, empty};
|
pub use misc::{default, empty};
|
||||||
pub use parameter::Parameter;
|
pub use parameter::Parameter;
|
||||||
pub use parser::Parser;
|
pub use parser::Parser;
|
||||||
|
pub use range_ext::RangeExt;
|
||||||
pub use recipe::Recipe;
|
pub use recipe::Recipe;
|
||||||
|
pub use recipe_resolver::RecipeResolver;
|
||||||
pub use runtime_error::{RuntimeError, RunResult};
|
pub use runtime_error::{RuntimeError, RunResult};
|
||||||
pub use shebang::Shebang;
|
pub use shebang::Shebang;
|
||||||
pub use token::{Token, TokenKind};
|
pub use token::{Token, TokenKind};
|
||||||
|
@ -3,8 +3,6 @@ use common::*;
|
|||||||
use itertools;
|
use itertools;
|
||||||
use TokenKind::*;
|
use TokenKind::*;
|
||||||
use CompilationErrorKind::*;
|
use CompilationErrorKind::*;
|
||||||
use recipe_resolver::resolve_recipes;
|
|
||||||
use assignment_resolver::resolve_assignments;
|
|
||||||
|
|
||||||
pub struct Parser<'a> {
|
pub struct Parser<'a> {
|
||||||
text: &'a str,
|
text: &'a str,
|
||||||
@ -350,7 +348,7 @@ impl<'a> Parser<'a> {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve_recipes(&self.recipes, &self.assignments, self.text)?;
|
RecipeResolver::resolve_recipes(&self.recipes, &self.assignments, self.text)?;
|
||||||
|
|
||||||
for recipe in self.recipes.values() {
|
for recipe in self.recipes.values() {
|
||||||
for parameter in &recipe.parameters {
|
for parameter in &recipe.parameters {
|
||||||
@ -371,7 +369,7 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve_assignments(&self.assignments, &self.assignment_tokens)?;
|
AssignmentResolver::resolve_assignments(&self.assignments, &self.assignment_tokens)?;
|
||||||
|
|
||||||
Ok(Justfile {
|
Ok(Justfile {
|
||||||
recipes: self.recipes,
|
recipes: self.recipes,
|
||||||
|
@ -2,63 +2,7 @@ use common::*;
|
|||||||
|
|
||||||
use CompilationErrorKind::*;
|
use CompilationErrorKind::*;
|
||||||
|
|
||||||
pub fn resolve_recipes<'a>(
|
pub struct RecipeResolver<'a: 'b, 'b> {
|
||||||
recipes: &Map<&'a str, Recipe<'a>>,
|
|
||||||
assignments: &Map<&'a str, Expression<'a>>,
|
|
||||||
text: &'a str,
|
|
||||||
) -> CompilationResult<'a, ()> {
|
|
||||||
let mut resolver = RecipeResolver {
|
|
||||||
seen: empty(),
|
|
||||||
stack: empty(),
|
|
||||||
resolved: empty(),
|
|
||||||
recipes: recipes,
|
|
||||||
};
|
|
||||||
|
|
||||||
for recipe in recipes.values() {
|
|
||||||
resolver.resolve(recipe)?;
|
|
||||||
resolver.seen = empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
for recipe in recipes.values() {
|
|
||||||
for line in &recipe.lines {
|
|
||||||
for fragment in line {
|
|
||||||
if let Fragment::Expression{ref expression, ..} = *fragment {
|
|
||||||
for variable in expression.variables() {
|
|
||||||
let name = variable.lexeme;
|
|
||||||
let undefined = !assignments.contains_key(name)
|
|
||||||
&& !recipe.parameters.iter().any(|p| p.name == name);
|
|
||||||
if undefined {
|
|
||||||
// There's a borrow issue here that seems too difficult to solve.
|
|
||||||
// The error derived from the variable token has too short a lifetime,
|
|
||||||
// so we create a new error from its contents, which do live long
|
|
||||||
// enough.
|
|
||||||
//
|
|
||||||
// I suspect the solution here is to give recipes, pieces, and expressions
|
|
||||||
// two lifetime parameters instead of one, with one being the lifetime
|
|
||||||
// of the struct, and the second being the lifetime of the tokens
|
|
||||||
// that it contains
|
|
||||||
let error = variable.error(UndefinedVariable{variable: name});
|
|
||||||
return Err(CompilationError {
|
|
||||||
text: text,
|
|
||||||
index: error.index,
|
|
||||||
line: error.line,
|
|
||||||
column: error.column,
|
|
||||||
width: error.width,
|
|
||||||
kind: UndefinedVariable {
|
|
||||||
variable: &text[error.index..error.index + error.width.unwrap()],
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RecipeResolver<'a: 'b, 'b> {
|
|
||||||
stack: Vec<&'a str>,
|
stack: Vec<&'a str>,
|
||||||
seen: Set<&'a str>,
|
seen: Set<&'a str>,
|
||||||
resolved: Set<&'a str>,
|
resolved: Set<&'a str>,
|
||||||
@ -66,7 +10,63 @@ struct RecipeResolver<'a: 'b, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> RecipeResolver<'a, 'b> {
|
impl<'a, 'b> RecipeResolver<'a, 'b> {
|
||||||
fn resolve(&mut self, recipe: &Recipe<'a>) -> CompilationResult<'a, ()> {
|
pub fn resolve_recipes(
|
||||||
|
recipes: &Map<&'a str, Recipe<'a>>,
|
||||||
|
assignments: &Map<&'a str, Expression<'a>>,
|
||||||
|
text: &'a str,
|
||||||
|
) -> CompilationResult<'a, ()> {
|
||||||
|
let mut resolver = RecipeResolver {
|
||||||
|
seen: empty(),
|
||||||
|
stack: empty(),
|
||||||
|
resolved: empty(),
|
||||||
|
recipes: recipes,
|
||||||
|
};
|
||||||
|
|
||||||
|
for recipe in recipes.values() {
|
||||||
|
resolver.resolve_recipe(recipe)?;
|
||||||
|
resolver.seen = empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
for recipe in recipes.values() {
|
||||||
|
for line in &recipe.lines {
|
||||||
|
for fragment in line {
|
||||||
|
if let Fragment::Expression{ref expression, ..} = *fragment {
|
||||||
|
for variable in expression.variables() {
|
||||||
|
let name = variable.lexeme;
|
||||||
|
let undefined = !assignments.contains_key(name)
|
||||||
|
&& !recipe.parameters.iter().any(|p| p.name == name);
|
||||||
|
if undefined {
|
||||||
|
// There's a borrow issue here that seems too difficult to solve.
|
||||||
|
// The error derived from the variable token has too short a lifetime,
|
||||||
|
// so we create a new error from its contents, which do live long
|
||||||
|
// enough.
|
||||||
|
//
|
||||||
|
// I suspect the solution here is to give recipes, pieces, and expressions
|
||||||
|
// two lifetime parameters instead of one, with one being the lifetime
|
||||||
|
// of the struct, and the second being the lifetime of the tokens
|
||||||
|
// that it contains
|
||||||
|
let error = variable.error(UndefinedVariable{variable: name});
|
||||||
|
return Err(CompilationError {
|
||||||
|
text: text,
|
||||||
|
index: error.index,
|
||||||
|
line: error.line,
|
||||||
|
column: error.column,
|
||||||
|
width: error.width,
|
||||||
|
kind: UndefinedVariable {
|
||||||
|
variable: &text[error.index..error.index + error.width.unwrap()],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_recipe(&mut self, recipe: &Recipe<'a>) -> CompilationResult<'a, ()> {
|
||||||
if self.resolved.contains(recipe.name) {
|
if self.resolved.contains(recipe.name) {
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
@ -85,7 +85,7 @@ impl<'a, 'b> RecipeResolver<'a, 'b> {
|
|||||||
.cloned().collect()
|
.cloned().collect()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
self.resolve(dependency)?;
|
self.resolve_recipe(dependency)?;
|
||||||
},
|
},
|
||||||
None => return Err(dependency_token.error(UnknownDependency {
|
None => return Err(dependency_token.error(UnknownDependency {
|
||||||
recipe: recipe.name,
|
recipe: recipe.name,
|
||||||
|
Loading…
Reference in New Issue
Block a user