Compare commits
6 Commits
master
...
analyzer-r
Author | SHA1 | Date | |
---|---|---|---|
|
fc70cea9b0 | ||
|
ed2c5c45e5 | ||
|
ed11e76600 | ||
|
32d7d2f03d | ||
|
61f7ab33c6 | ||
|
9f965094ba |
148
src/analyzer.rs
148
src/analyzer.rs
@ -1,10 +1,46 @@
|
|||||||
use {super::*, CompileErrorKind::*};
|
use {super::*, CompileErrorKind::*};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct DefinitionTracker<'src> {
|
||||||
|
definitions: HashMap<&'src str, (&'static str, Name<'src>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'src> DefinitionTracker<'src> {
|
||||||
|
fn define(
|
||||||
|
&mut self,
|
||||||
|
name: Name<'src>,
|
||||||
|
second_type: &'static str,
|
||||||
|
duplicates_allowed: bool,
|
||||||
|
) -> CompileResult<'src> {
|
||||||
|
if let Some((first_type, original)) = self.definitions.get(name.lexeme()) {
|
||||||
|
if !(*first_type == second_type && duplicates_allowed) {
|
||||||
|
let (original, redefinition) = if name.line < original.line {
|
||||||
|
(name, *original)
|
||||||
|
} else {
|
||||||
|
(*original, name)
|
||||||
|
};
|
||||||
|
|
||||||
|
return Err(redefinition.token.error(Redefinition {
|
||||||
|
first_type,
|
||||||
|
second_type,
|
||||||
|
name: name.lexeme(),
|
||||||
|
first: original.line,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.definitions.insert(name.lexeme(), (second_type, name));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct Analyzer<'src> {
|
pub(crate) struct Analyzer<'src> {
|
||||||
assignments: Table<'src, Assignment<'src>>,
|
assignments: Table<'src, Assignment<'src>>,
|
||||||
aliases: Table<'src, Alias<'src, Name<'src>>>,
|
aliases: Table<'src, Alias<'src, Name<'src>>>,
|
||||||
sets: Table<'src, Set<'src>>,
|
sets: Table<'src, Set<'src>>,
|
||||||
|
warnings: Vec<Warning>,
|
||||||
|
modules: Table<'src, Justfile<'src>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> Analyzer<'src> {
|
impl<'src> Analyzer<'src> {
|
||||||
@ -15,66 +51,25 @@ impl<'src> Analyzer<'src> {
|
|||||||
root: &Path,
|
root: &Path,
|
||||||
name: Option<Name<'src>>,
|
name: Option<Name<'src>>,
|
||||||
) -> CompileResult<'src, Justfile<'src>> {
|
) -> CompileResult<'src, Justfile<'src>> {
|
||||||
Self::default().justfile(loaded, paths, asts, root, name)
|
let mut analyzer = Self::default();
|
||||||
}
|
|
||||||
|
|
||||||
fn justfile(
|
let mut definitions = DefinitionTracker::default();
|
||||||
mut self,
|
let mut unprocessed_recipes = Vec::new();
|
||||||
loaded: &[PathBuf],
|
let mut unprocessed_assignments = Vec::new();
|
||||||
paths: &HashMap<PathBuf, PathBuf>,
|
|
||||||
asts: &HashMap<PathBuf, Ast<'src>>,
|
|
||||||
root: &Path,
|
|
||||||
name: Option<Name<'src>>,
|
|
||||||
) -> CompileResult<'src, Justfile<'src>> {
|
|
||||||
let mut recipes = Vec::new();
|
|
||||||
|
|
||||||
let mut assignments = Vec::new();
|
|
||||||
|
|
||||||
let mut stack = Vec::new();
|
let mut stack = Vec::new();
|
||||||
stack.push(asts.get(root).unwrap());
|
stack.push(asts.get(root).unwrap());
|
||||||
|
|
||||||
let mut warnings = Vec::new();
|
|
||||||
|
|
||||||
let mut modules: Table<Justfile> = Table::new();
|
|
||||||
|
|
||||||
let mut definitions: HashMap<&str, (&'static str, Name)> = HashMap::new();
|
|
||||||
|
|
||||||
let mut define = |name: Name<'src>,
|
|
||||||
second_type: &'static str,
|
|
||||||
duplicates_allowed: bool|
|
|
||||||
-> CompileResult<'src> {
|
|
||||||
if let Some((first_type, original)) = definitions.get(name.lexeme()) {
|
|
||||||
if !(*first_type == second_type && duplicates_allowed) {
|
|
||||||
let (original, redefinition) = if name.line < original.line {
|
|
||||||
(name, *original)
|
|
||||||
} else {
|
|
||||||
(*original, name)
|
|
||||||
};
|
|
||||||
|
|
||||||
return Err(redefinition.token.error(Redefinition {
|
|
||||||
first_type,
|
|
||||||
second_type,
|
|
||||||
name: name.lexeme(),
|
|
||||||
first: original.line,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
definitions.insert(name.lexeme(), (second_type, name));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
while let Some(ast) = stack.pop() {
|
while let Some(ast) = stack.pop() {
|
||||||
for item in &ast.items {
|
for item in &ast.items {
|
||||||
match item {
|
match item {
|
||||||
Item::Alias(alias) => {
|
Item::Alias(alias) => {
|
||||||
define(alias.name, "alias", false)?;
|
definitions.define(alias.name, "alias", false)?;
|
||||||
Self::analyze_alias(alias)?;
|
Self::analyze_alias(alias)?;
|
||||||
self.aliases.insert(alias.clone());
|
analyzer.aliases.insert(alias.clone());
|
||||||
}
|
}
|
||||||
Item::Assignment(assignment) => {
|
Item::Assignment(assignment) => {
|
||||||
assignments.push(assignment);
|
unprocessed_assignments.push(assignment);
|
||||||
}
|
}
|
||||||
Item::Comment(_) => (),
|
Item::Comment(_) => (),
|
||||||
Item::Import { absolute, .. } => {
|
Item::Import { absolute, .. } => {
|
||||||
@ -84,64 +79,67 @@ impl<'src> Analyzer<'src> {
|
|||||||
}
|
}
|
||||||
Item::Module { absolute, name, .. } => {
|
Item::Module { absolute, name, .. } => {
|
||||||
if let Some(absolute) = absolute {
|
if let Some(absolute) = absolute {
|
||||||
define(*name, "module", false)?;
|
definitions.define(*name, "module", false)?;
|
||||||
modules.insert(Self::analyze(loaded, paths, asts, absolute, Some(*name))?);
|
analyzer
|
||||||
|
.modules
|
||||||
|
.insert(Self::analyze(loaded, paths, asts, absolute, Some(*name))?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Item::Recipe(recipe) => {
|
Item::Recipe(recipe) => {
|
||||||
if recipe.enabled() {
|
if recipe.enabled() {
|
||||||
Self::analyze_recipe(recipe)?;
|
Self::analyze_recipe(recipe)?;
|
||||||
recipes.push(recipe);
|
unprocessed_recipes.push(recipe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Item::Set(set) => {
|
Item::Set(set) => {
|
||||||
self.analyze_set(set)?;
|
analyzer.analyze_set(set)?;
|
||||||
self.sets.insert(set.clone());
|
analyzer.sets.insert(set.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
warnings.extend(ast.warnings.iter().cloned());
|
analyzer.warnings.extend(ast.warnings.iter().cloned());
|
||||||
}
|
}
|
||||||
|
|
||||||
let settings = Settings::from_setting_iter(self.sets.into_iter().map(|(_, set)| set.value));
|
let settings = Settings::from_setting_iter(analyzer.sets.into_iter().map(|(_, set)| set.value));
|
||||||
|
|
||||||
let mut recipe_table: Table<'src, UnresolvedRecipe<'src>> = Table::default();
|
for assignment in unprocessed_assignments {
|
||||||
|
|
||||||
for assignment in assignments {
|
|
||||||
if !settings.allow_duplicate_variables
|
if !settings.allow_duplicate_variables
|
||||||
&& self.assignments.contains_key(assignment.name.lexeme())
|
&& analyzer.assignments.contains_key(assignment.name.lexeme())
|
||||||
{
|
{
|
||||||
return Err(assignment.name.token.error(DuplicateVariable {
|
return Err(assignment.name.token.error(DuplicateVariable {
|
||||||
variable: assignment.name.lexeme(),
|
variable: assignment.name.lexeme(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self
|
if analyzer
|
||||||
.assignments
|
.assignments
|
||||||
.get(assignment.name.lexeme())
|
.get(assignment.name.lexeme())
|
||||||
.map_or(true, |original| assignment.depth <= original.depth)
|
.map_or(true, |original| assignment.depth <= original.depth)
|
||||||
{
|
{
|
||||||
self.assignments.insert(assignment.clone());
|
analyzer.assignments.insert(assignment.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AssignmentResolver::resolve_assignments(&self.assignments)?;
|
AssignmentResolver::resolve_assignments(&analyzer.assignments)?;
|
||||||
|
|
||||||
for recipe in recipes {
|
let recipes = {
|
||||||
define(recipe.name, "recipe", settings.allow_duplicate_recipes)?;
|
let mut recipe_table: Table<'src, UnresolvedRecipe<'src>> = Table::default();
|
||||||
if recipe_table
|
for recipe in unprocessed_recipes {
|
||||||
.get(recipe.name.lexeme())
|
definitions.define(recipe.name, "recipe", settings.allow_duplicate_recipes)?;
|
||||||
.map_or(true, |original| recipe.file_depth <= original.file_depth)
|
if recipe_table
|
||||||
{
|
.get(recipe.name.lexeme())
|
||||||
recipe_table.insert(recipe.clone());
|
.map_or(true, |original| recipe.file_depth <= original.file_depth)
|
||||||
|
{
|
||||||
|
recipe_table.insert(recipe.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let recipes = RecipeResolver::resolve_recipes(recipe_table, &self.assignments)?;
|
RecipeResolver::resolve_recipes(recipe_table, &analyzer.assignments)?
|
||||||
|
};
|
||||||
|
|
||||||
let mut aliases = Table::new();
|
let mut aliases = Table::new();
|
||||||
while let Some(alias) = self.aliases.pop() {
|
while let Some(alias) = analyzer.aliases.pop() {
|
||||||
aliases.insert(Self::resolve_alias(&recipes, alias)?);
|
aliases.insert(Self::resolve_alias(&recipes, alias)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +147,7 @@ impl<'src> Analyzer<'src> {
|
|||||||
|
|
||||||
Ok(Justfile {
|
Ok(Justfile {
|
||||||
aliases,
|
aliases,
|
||||||
assignments: self.assignments,
|
assignments: analyzer.assignments,
|
||||||
default: recipes
|
default: recipes
|
||||||
.values()
|
.values()
|
||||||
.filter(|recipe| recipe.name.path == root)
|
.filter(|recipe| recipe.name.path == root)
|
||||||
@ -162,12 +160,12 @@ impl<'src> Analyzer<'src> {
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
loaded: loaded.into(),
|
loaded: loaded.into(),
|
||||||
modules,
|
modules: analyzer.modules,
|
||||||
name,
|
name,
|
||||||
recipes,
|
recipes,
|
||||||
settings,
|
settings,
|
||||||
source: root.into(),
|
source: root.into(),
|
||||||
warnings,
|
warnings: analyzer.warnings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user