Resolve alias targets (#548)
During analysis, resolve alias targets from `Name`s to `Rc<Recipe>`, giving us type-level assurance that alias resolution was performed, and avoiding the need to look up alias targets in a separate table when running.
This commit is contained in:
parent
30c77f8d03
commit
4f08bb4d77
38
src/alias.rs
38
src/alias.rs
@ -2,28 +2,39 @@ use crate::common::*;
|
|||||||
|
|
||||||
/// An alias, e.g. `name := target`
|
/// An alias, e.g. `name := target`
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub(crate) struct Alias<'src> {
|
pub(crate) struct Alias<'src, T = Rc<Recipe<'src>>> {
|
||||||
pub(crate) name: Name<'src>,
|
pub(crate) name: Name<'src>,
|
||||||
pub(crate) target: Name<'src>,
|
pub(crate) target: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'src> Alias<'src, Name<'src>> {
|
||||||
|
pub(crate) fn line_number(&self) -> usize {
|
||||||
|
self.name.line
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resolve(self, target: Rc<Recipe<'src>>) -> Alias<'src> {
|
||||||
|
assert_eq!(self.target.lexeme(), target.name.lexeme());
|
||||||
|
|
||||||
|
Alias {
|
||||||
|
name: self.name,
|
||||||
|
target,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Alias<'_> {
|
impl Alias<'_> {
|
||||||
pub(crate) fn is_private(&self) -> bool {
|
pub(crate) fn is_private(&self) -> bool {
|
||||||
self.name.lexeme().starts_with('_')
|
self.name.lexeme().starts_with('_')
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn line_number(&self) -> usize {
|
|
||||||
self.name.line
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> Keyed<'src> for Alias<'src> {
|
impl<'src, T> Keyed<'src> for Alias<'src, T> {
|
||||||
fn key(&self) -> &'src str {
|
fn key(&self) -> &'src str {
|
||||||
self.name.lexeme()
|
self.name.lexeme()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Display for Alias<'a> {
|
impl<'src> Display for Alias<'src, Name<'src>> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
@ -33,3 +44,14 @@ impl<'a> Display for Alias<'a> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'src> Display for Alias<'src> {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"alias {} := {}",
|
||||||
|
self.name.lexeme(),
|
||||||
|
self.target.name.lexeme()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
use crate::common::*;
|
|
||||||
use CompilationErrorKind::*;
|
|
||||||
|
|
||||||
pub(crate) struct AliasResolver<'a, 'b>
|
|
||||||
where
|
|
||||||
'a: 'b,
|
|
||||||
{
|
|
||||||
aliases: &'b Table<'a, Alias<'a>>,
|
|
||||||
recipes: &'b Table<'a, Rc<Recipe<'a>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a: 'b, 'b> AliasResolver<'a, 'b> {
|
|
||||||
pub(crate) fn resolve_aliases(
|
|
||||||
aliases: &Table<'a, Alias<'a>>,
|
|
||||||
recipes: &Table<'a, Rc<Recipe<'a>>>,
|
|
||||||
) -> CompilationResult<'a, ()> {
|
|
||||||
let resolver = AliasResolver { aliases, recipes };
|
|
||||||
|
|
||||||
resolver.resolve()?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve(&self) -> CompilationResult<'a, ()> {
|
|
||||||
for alias in self.aliases.values() {
|
|
||||||
self.resolve_alias(alias)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_alias(&self, alias: &Alias<'a>) -> CompilationResult<'a, ()> {
|
|
||||||
let token = alias.name.token();
|
|
||||||
// Make sure the alias doesn't conflict with any recipe
|
|
||||||
if let Some(recipe) = self.recipes.get(alias.name.lexeme()) {
|
|
||||||
return Err(token.error(AliasShadowsRecipe {
|
|
||||||
alias: alias.name.lexeme(),
|
|
||||||
recipe_line: recipe.line_number(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the target recipe exists
|
|
||||||
if self.recipes.get(alias.target.lexeme()).is_none() {
|
|
||||||
return Err(token.error(UnknownAliasTarget {
|
|
||||||
alias: alias.name.lexeme(),
|
|
||||||
target: alias.target.lexeme(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,7 +5,7 @@ use CompilationErrorKind::*;
|
|||||||
pub(crate) struct Analyzer<'src> {
|
pub(crate) struct Analyzer<'src> {
|
||||||
recipes: Table<'src, Recipe<'src, Name<'src>>>,
|
recipes: Table<'src, Recipe<'src, Name<'src>>>,
|
||||||
assignments: Table<'src, Assignment<'src>>,
|
assignments: Table<'src, Assignment<'src>>,
|
||||||
aliases: Table<'src, Alias<'src>>,
|
aliases: Table<'src, Alias<'src, Name<'src>>>,
|
||||||
sets: Table<'src, Set<'src>>,
|
sets: Table<'src, Set<'src>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +51,6 @@ impl<'src> Analyzer<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let assignments = self.assignments;
|
let assignments = self.assignments;
|
||||||
let aliases = self.aliases;
|
|
||||||
|
|
||||||
AssignmentResolver::resolve_assignments(&assignments)?;
|
AssignmentResolver::resolve_assignments(&assignments)?;
|
||||||
|
|
||||||
@ -67,7 +66,10 @@ impl<'src> Analyzer<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AliasResolver::resolve_aliases(&aliases, &recipes)?;
|
let mut aliases = Table::new();
|
||||||
|
while let Some(alias) = self.aliases.pop() {
|
||||||
|
aliases.insert(Self::resolve_alias(&recipes, alias)?);
|
||||||
|
}
|
||||||
|
|
||||||
let mut settings = Settings::new();
|
let mut settings = Settings::new();
|
||||||
|
|
||||||
@ -161,7 +163,7 @@ impl<'src> Analyzer<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_alias(&self, alias: &Alias<'src>) -> CompilationResult<'src, ()> {
|
fn analyze_alias(&self, alias: &Alias<'src, Name<'src>>) -> CompilationResult<'src, ()> {
|
||||||
let name = alias.name.lexeme();
|
let name = alias.name.lexeme();
|
||||||
|
|
||||||
if let Some(original) = self.aliases.get(name) {
|
if let Some(original) = self.aliases.get(name) {
|
||||||
@ -184,6 +186,29 @@ impl<'src> Analyzer<'src> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_alias(
|
||||||
|
recipes: &Table<'src, Rc<Recipe<'src>>>,
|
||||||
|
alias: Alias<'src, Name<'src>>,
|
||||||
|
) -> CompilationResult<'src, Alias<'src>> {
|
||||||
|
let token = alias.name.token();
|
||||||
|
// Make sure the alias doesn't conflict with any recipe
|
||||||
|
if let Some(recipe) = recipes.get(alias.name.lexeme()) {
|
||||||
|
return Err(token.error(AliasShadowsRecipe {
|
||||||
|
alias: alias.name.lexeme(),
|
||||||
|
recipe_line: recipe.line_number(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the target recipe exists
|
||||||
|
match recipes.get(alias.target.lexeme()) {
|
||||||
|
Some(target) => Ok(alias.resolve(target.clone())),
|
||||||
|
None => Err(token.error(UnknownAliasTarget {
|
||||||
|
alias: alias.name.lexeme(),
|
||||||
|
target: alias.target.lexeme(),
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -47,7 +47,7 @@ pub(crate) use crate::{
|
|||||||
|
|
||||||
// structs and enums
|
// structs and enums
|
||||||
pub(crate) use crate::{
|
pub(crate) use crate::{
|
||||||
alias::Alias, alias_resolver::AliasResolver, analyzer::Analyzer, assignment::Assignment,
|
alias::Alias, analyzer::Analyzer, assignment::Assignment,
|
||||||
assignment_evaluator::AssignmentEvaluator, assignment_resolver::AssignmentResolver, color::Color,
|
assignment_evaluator::AssignmentEvaluator, assignment_resolver::AssignmentResolver, color::Color,
|
||||||
compilation_error::CompilationError, compilation_error_kind::CompilationErrorKind,
|
compilation_error::CompilationError, compilation_error_kind::CompilationErrorKind,
|
||||||
compiler::Compiler, config::Config, config_error::ConfigError, count::Count,
|
compiler::Compiler, config::Config, config_error::ConfigError, count::Count,
|
||||||
|
@ -441,10 +441,10 @@ impl Config {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !recipe_aliases.contains_key(alias.target.lexeme()) {
|
if !recipe_aliases.contains_key(alias.target.name.lexeme()) {
|
||||||
recipe_aliases.insert(alias.target.lexeme(), vec![alias.name.lexeme()]);
|
recipe_aliases.insert(alias.target.name.lexeme(), vec![alias.name.lexeme()]);
|
||||||
} else {
|
} else {
|
||||||
let aliases = recipe_aliases.get_mut(alias.target.lexeme()).unwrap();
|
let aliases = recipe_aliases.get_mut(alias.target.name.lexeme()).unwrap();
|
||||||
aliases.push(alias.name.lexeme());
|
aliases.push(alias.name.lexeme());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -542,7 +542,7 @@ impl Config {
|
|||||||
|
|
||||||
fn show(&self, name: &str, justfile: Justfile) -> Result<(), i32> {
|
fn show(&self, name: &str, justfile: Justfile) -> Result<(), i32> {
|
||||||
if let Some(alias) = justfile.get_alias(name) {
|
if let Some(alias) = justfile.get_alias(name) {
|
||||||
let recipe = justfile.get_recipe(alias.target.lexeme()).unwrap();
|
let recipe = justfile.get_recipe(alias.target.name.lexeme()).unwrap();
|
||||||
println!("{}", alias);
|
println!("{}", alias);
|
||||||
println!("{}", recipe);
|
println!("{}", recipe);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -3,7 +3,7 @@ use crate::common::*;
|
|||||||
/// A single top-level item
|
/// A single top-level item
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum Item<'src> {
|
pub(crate) enum Item<'src> {
|
||||||
Alias(Alias<'src>),
|
Alias(Alias<'src, Name<'src>>),
|
||||||
Assignment(Assignment<'src>),
|
Assignment(Assignment<'src>),
|
||||||
Recipe(Recipe<'src, Name<'src>>),
|
Recipe(Recipe<'src, Name<'src>>),
|
||||||
Set(Set<'src>),
|
Set(Set<'src>),
|
||||||
|
@ -4,7 +4,7 @@ use crate::common::*;
|
|||||||
pub(crate) struct Justfile<'src> {
|
pub(crate) struct Justfile<'src> {
|
||||||
pub(crate) recipes: Table<'src, Rc<Recipe<'src>>>,
|
pub(crate) recipes: Table<'src, Rc<Recipe<'src>>>,
|
||||||
pub(crate) assignments: Table<'src, Assignment<'src>>,
|
pub(crate) assignments: Table<'src, Assignment<'src>>,
|
||||||
pub(crate) aliases: Table<'src, Alias<'src>>,
|
pub(crate) aliases: Table<'src, Alias<'src, Rc<Recipe<'src>>>>,
|
||||||
pub(crate) settings: Settings<'src>,
|
pub(crate) settings: Settings<'src>,
|
||||||
pub(crate) warnings: Vec<Warning<'src>>,
|
pub(crate) warnings: Vec<Warning<'src>>,
|
||||||
}
|
}
|
||||||
@ -166,7 +166,7 @@ impl<'src> Justfile<'src> {
|
|||||||
if let Some(recipe) = self.recipes.get(name) {
|
if let Some(recipe) = self.recipes.get(name) {
|
||||||
Some(recipe)
|
Some(recipe)
|
||||||
} else if let Some(alias) = self.aliases.get(name) {
|
} else if let Some(alias) = self.aliases.get(name) {
|
||||||
self.recipes.get(alias.target.lexeme()).map(Rc::as_ref)
|
Some(alias.target.as_ref())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ pub mod node;
|
|||||||
pub(crate) mod fuzzing;
|
pub(crate) mod fuzzing;
|
||||||
|
|
||||||
mod alias;
|
mod alias;
|
||||||
mod alias_resolver;
|
|
||||||
mod analyzer;
|
mod analyzer;
|
||||||
mod assignment;
|
mod assignment;
|
||||||
mod assignment_evaluator;
|
mod assignment_evaluator;
|
||||||
|
@ -26,7 +26,7 @@ impl<'src> Node<'src> for Item<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> Node<'src> for Alias<'src> {
|
impl<'src> Node<'src> for Alias<'src, Name<'src>> {
|
||||||
fn tree(&self) -> Tree<'src> {
|
fn tree(&self) -> Tree<'src> {
|
||||||
Tree::atom(keyword::ALIAS)
|
Tree::atom(keyword::ALIAS)
|
||||||
.push(self.name.lexeme())
|
.push(self.name.lexeme())
|
||||||
|
@ -325,7 +325,7 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an alias, e.g `alias name := target`
|
/// Parse an alias, e.g `alias name := target`
|
||||||
fn parse_alias(&mut self) -> CompilationResult<'src, Alias<'src>> {
|
fn parse_alias(&mut self) -> CompilationResult<'src, Alias<'src, Name<'src>>> {
|
||||||
self.presume_name(keyword::ALIAS)?;
|
self.presume_name(keyword::ALIAS)?;
|
||||||
let name = self.parse_name()?;
|
let name = self.parse_name()?;
|
||||||
self.presume_any(&[Equals, ColonEquals])?;
|
self.presume_any(&[Equals, ColonEquals])?;
|
||||||
|
@ -8,6 +8,12 @@ pub(crate) struct Table<'key, V: Keyed<'key>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'key, V: Keyed<'key>> Table<'key, V> {
|
impl<'key, V: Keyed<'key>> Table<'key, V> {
|
||||||
|
pub(crate) fn new() -> Table<'key, V> {
|
||||||
|
Table {
|
||||||
|
map: BTreeMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn insert(&mut self, value: V) {
|
pub(crate) fn insert(&mut self, value: V) {
|
||||||
self.map.insert(value.key(), value);
|
self.map.insert(value.key(), value);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user