analyzer: definition tracker

This commit is contained in:
Greg Shuflin 2024-06-03 22:07:39 -05:00
parent ed2c5c45e5
commit fc70cea9b0

View File

@ -1,5 +1,39 @@
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>>,
@ -19,45 +53,18 @@ impl<'src> Analyzer<'src> {
) -> CompileResult<'src, Justfile<'src>> { ) -> CompileResult<'src, Justfile<'src>> {
let mut analyzer = Self::default(); let mut analyzer = Self::default();
let mut definitions = DefinitionTracker::default();
let mut unprocessed_recipes = Vec::new(); let mut unprocessed_recipes = Vec::new();
let mut unprocessed_assignments = Vec::new(); let mut unprocessed_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 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)?;
analyzer.aliases.insert(alias.clone()); analyzer.aliases.insert(alias.clone());
} }
@ -72,7 +79,7 @@ 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)?;
analyzer analyzer
.modules .modules
.insert(Self::analyze(loaded, paths, asts, absolute, Some(*name))?); .insert(Self::analyze(loaded, paths, asts, absolute, Some(*name))?);
@ -119,7 +126,7 @@ impl<'src> Analyzer<'src> {
let recipes = { let recipes = {
let mut recipe_table: Table<'src, UnresolvedRecipe<'src>> = Table::default(); let mut recipe_table: Table<'src, UnresolvedRecipe<'src>> = Table::default();
for recipe in unprocessed_recipes { for recipe in unprocessed_recipes {
define(recipe.name, "recipe", settings.allow_duplicate_recipes)?; definitions.define(recipe.name, "recipe", settings.allow_duplicate_recipes)?;
if recipe_table if recipe_table
.get(recipe.name.lexeme()) .get(recipe.name.lexeme())
.map_or(true, |original| recipe.file_depth <= original.file_depth) .map_or(true, |original| recipe.file_depth <= original.file_depth)