Skip to content

Commit

Permalink
Refactor analyzer (#2378)
Browse files Browse the repository at this point in the history
  • Loading branch information
neunenak authored Sep 24, 2024
1 parent dc89b74 commit bfaea9f
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 72 deletions.
140 changes: 71 additions & 69 deletions src/analyzer.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
use {super::*, CompileErrorKind::*};

#[derive(Default)]
pub(crate) struct Analyzer<'src> {
assignments: Table<'src, Assignment<'src>>,
pub(crate) struct Analyzer<'run, 'src> {
aliases: Table<'src, Alias<'src, Name<'src>>>,
assignments: Vec<&'run Binding<'src, Expression<'src>>>,
modules: Table<'src, Justfile<'src>>,
recipes: Vec<&'run Recipe<'src, UnresolvedDependency<'src>>>,
sets: Table<'src, Set<'src>>,
unexports: HashSet<String>,
warnings: Vec<Warning>,
}

impl<'src> Analyzer<'src> {
impl<'run, 'src> Analyzer<'run, 'src> {
pub(crate) fn analyze(
asts: &HashMap<PathBuf, Ast<'src>>,
asts: &'run HashMap<PathBuf, Ast<'src>>,
doc: Option<String>,
groups: &[String],
loaded: &[PathBuf],
Expand All @@ -22,66 +26,30 @@ impl<'src> Analyzer<'src> {

fn justfile(
mut self,
asts: &HashMap<PathBuf, Ast<'src>>,
asts: &'run HashMap<PathBuf, Ast<'src>>,
doc: Option<String>,
groups: &[String],
loaded: &[PathBuf],
name: Option<Name<'src>>,
paths: &HashMap<PathBuf, PathBuf>,
root: &Path,
) -> CompileResult<'src, Justfile<'src>> {
let mut recipes = Vec::new();

let mut assignments = Vec::new();
let mut definitions = HashMap::new();

let mut stack = Vec::new();
let ast = asts.get(root).unwrap();
stack.push(ast);

let mut warnings = Vec::new();

let mut modules: Table<Justfile> = Table::new();

let mut unexports: HashSet<String> = HashSet::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 ((first_type, second_type), (original, redefinition)) = if name.line < original.line {
((second_type, *first_type), (name, *original))
} else {
((*first_type, second_type), (*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() {
for item in &ast.items {
match item {
Item::Alias(alias) => {
define(alias.name, "alias", false)?;
Self::define(&mut definitions, alias.name, "alias", false)?;
Self::analyze_alias(alias)?;
self.aliases.insert(alias.clone());
}
Item::Assignment(assignment) => {
assignments.push(assignment);
self.assignments.push(assignment);
}
Item::Comment(_) => (),
Item::Import { absolute, .. } => {
Expand Down Expand Up @@ -113,8 +81,8 @@ impl<'src> Analyzer<'src> {
}

if let Some(absolute) = absolute {
define(*name, "module", false)?;
modules.insert(Self::analyze(
Self::define(&mut definitions, *name, "module", false)?;
self.modules.insert(Self::analyze(
asts,
doc_attr.or(*doc).map(ToOwned::to_owned),
groups.as_slice(),
Expand All @@ -128,15 +96,15 @@ impl<'src> Analyzer<'src> {
Item::Recipe(recipe) => {
if recipe.enabled() {
Self::analyze_recipe(recipe)?;
recipes.push(recipe);
self.recipes.push(recipe);
}
}
Item::Set(set) => {
self.analyze_set(set)?;
self.sets.insert(set.clone());
}
Item::Unexport { name } => {
if !unexports.insert(name.lexeme().to_string()) {
if !self.unexports.insert(name.lexeme().to_string()) {
return Err(name.token.error(DuplicateUnexport {
variable: name.lexeme(),
}));
Expand All @@ -145,52 +113,56 @@ impl<'src> Analyzer<'src> {
}
}

warnings.extend(ast.warnings.iter().cloned());
self.warnings.extend(ast.warnings.iter().cloned());
}

let settings = Settings::from_setting_iter(self.sets.into_iter().map(|(_, set)| set.value));

let mut recipe_table: Table<'src, UnresolvedRecipe<'src>> = Table::default();
let settings = Settings::from_table(self.sets);

for assignment in assignments {
let mut assignments: Table<'src, Assignment<'src>> = Table::default();
for assignment in self.assignments {
let variable = assignment.name.lexeme();

if !settings.allow_duplicate_variables && self.assignments.contains_key(variable) {
if !settings.allow_duplicate_variables && assignments.contains_key(variable) {
return Err(assignment.name.token.error(DuplicateVariable { variable }));
}

if self.assignments.get(variable).map_or(true, |original| {
if assignments.get(variable).map_or(true, |original| {
assignment.file_depth <= original.file_depth
}) {
self.assignments.insert(assignment.clone());
assignments.insert(assignment.clone());
}

if unexports.contains(variable) {
if self.unexports.contains(variable) {
return Err(assignment.name.token.error(ExportUnexported { variable }));
}
}

AssignmentResolver::resolve_assignments(&self.assignments)?;
AssignmentResolver::resolve_assignments(&assignments)?;

let mut deduplicated_recipes = Table::<'src, UnresolvedRecipe<'src>>::default();
for recipe in self.recipes {
Self::define(
&mut definitions,
recipe.name,
"recipe",
settings.allow_duplicate_recipes,
)?;

for recipe in recipes {
define(recipe.name, "recipe", settings.allow_duplicate_recipes)?;
if recipe_table
if deduplicated_recipes
.get(recipe.name.lexeme())
.map_or(true, |original| recipe.file_depth <= original.file_depth)
{
recipe_table.insert(recipe.clone());
deduplicated_recipes.insert(recipe.clone());
}
}

let recipes = RecipeResolver::resolve_recipes(&self.assignments, &settings, recipe_table)?;
let recipes = RecipeResolver::resolve_recipes(&assignments, &settings, deduplicated_recipes)?;

let mut aliases = Table::new();
while let Some(alias) = self.aliases.pop() {
aliases.insert(Self::resolve_alias(&recipes, alias)?);
}

let root = paths.get(root).unwrap();

let mut unstable_features = BTreeSet::new();

for recipe in recipes.values() {
Expand All @@ -206,9 +178,11 @@ impl<'src> Analyzer<'src> {
unstable_features.insert(UnstableFeature::ScriptInterpreterSetting);
}

let root = paths.get(root).unwrap();

Ok(Justfile {
aliases,
assignments: self.assignments,
assignments,
default: recipes
.values()
.filter(|recipe| recipe.name.path == root)
Expand All @@ -223,18 +197,46 @@ impl<'src> Analyzer<'src> {
doc,
groups: groups.into(),
loaded: loaded.into(),
modules,
modules: self.modules,
name,
recipes,
settings,
source: root.into(),
unexports,
unexports: self.unexports,
unstable_features,
warnings,
warnings: self.warnings,
working_directory: ast.working_directory.clone(),
})
}

fn define(
definitions: &mut HashMap<&'src str, (&'static str, Name<'src>)>,
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 ((first_type, second_type), (original, redefinition)) = if name.line < original.line {
((second_type, *first_type), (name, *original))
} else {
((*first_type, second_type), (*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(())
}

fn analyze_recipe(recipe: &UnresolvedRecipe<'src>) -> CompileResult<'src> {
let mut parameters = BTreeSet::new();
let mut passed_default = false;
Expand Down
6 changes: 3 additions & 3 deletions src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ pub(crate) struct Settings<'src> {
}

impl<'src> Settings<'src> {
pub(crate) fn from_setting_iter(iter: impl Iterator<Item = Setting<'src>>) -> Self {
pub(crate) fn from_table(sets: Table<'src, Set<'src>>) -> Self {
let mut settings = Self::default();

for set in iter {
match set {
for (_name, set) in sets {
match set.value {
Setting::AllowDuplicateRecipes(allow_duplicate_recipes) => {
settings.allow_duplicate_recipes = allow_duplicate_recipes;
}
Expand Down

0 comments on commit bfaea9f

Please sign in to comment.