Refactor evaluators and resolvers into common form (#258)

This commit is contained in:
Casey Rodarmor 2017-11-18 01:18:04 -08:00 committed by GitHub
parent bc79d16eac
commit 28a57d9828
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 111 additions and 112 deletions

View File

@ -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>],

View File

@ -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(());

View File

@ -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,

View File

@ -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};

View File

@ -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,

View File

@ -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,