Compare commits

...

6 Commits

Author SHA1 Message Date
Greg Shuflin
fc70cea9b0 analyzer: definition tracker 2024-06-03 22:07:39 -05:00
Greg Shuflin
ed2c5c45e5 Store modules on analyzer struct 2024-06-03 16:02:13 -07:00
Greg Shuflin
ed11e76600 Warnings on analyzer 2024-06-03 15:58:15 -07:00
Greg Shuflin
32d7d2f03d consolidate recipe table 2024-06-03 15:56:27 -07:00
Greg Shuflin
61f7ab33c6 unprocessed_assignments rename 2024-06-03 15:54:20 -07:00
Greg Shuflin
9f965094ba Combine analyze() and justfile() functions 2024-06-03 15:47:56 -07:00

View File

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