Skip to content

Commit

Permalink
refactor: Allow the aliases map to map to arbitrary types.
Browse files Browse the repository at this point in the history
For jj-vcs#3673, we will have aliases such as:
```toml
'upload(revision)' = [
  ["fix", "-r", "$revision"],
  ["lint", "-r", "$revision"],
  ["git", "push", "-r", "$revision"],
]
```

Template aliases:
1) Start as Config::Value
2) Are converted to String
3) Are placed in the alias map
4) Expand to a TemplateExpression type via expand_defn.

However, command aliases:
1) Start as Config::Value
2) Are converted to Vec<Vec<String>>
3) Are placed in an alias map
4) Do not expand

Thus, AliasesMap will need to support non-string values.
  • Loading branch information
matts1 committed Oct 4, 2024
1 parent 3d26af8 commit 0383806
Showing 1 changed file with 39 additions and 19 deletions.
58 changes: 39 additions & 19 deletions lib/src/dsl_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use std::array;
use std::collections::HashMap;
use std::fmt;
use std::ops::Deref;
use std::slice;

use itertools::Itertools as _;
Expand Down Expand Up @@ -423,16 +424,28 @@ impl<R: RuleType> StringLiteralParser<R> {
}

/// Map of symbol and function aliases.
#[derive(Clone, Debug, Default)]
pub struct AliasesMap<P> {
symbol_aliases: HashMap<String, String>,
#[derive(Clone, Debug)]
pub struct AliasesMap<P, V: Deref = String> {
symbol_aliases: HashMap<String, V>,
// name: [(params, defn)] (sorted by arity)
function_aliases: HashMap<String, Vec<(Vec<String>, String)>>,
function_aliases: HashMap<String, Vec<(Vec<String>, V)>>,
// Parser type P helps prevent misuse of AliasesMap of different language.
parser: P,
}

impl<P> AliasesMap<P> {
// Unfortunately, #[derive(Default)] doesn't work correctly since V isn't
// Default. https://stackoverflow.com/questions/59538071/the-trait-bound-t-stddefaultdefault-is-not-satisfied-when-using-phantomda
impl<P: Default, V: Deref> Default for AliasesMap<P, V> {
fn default() -> Self {
Self {
symbol_aliases: Default::default(),
function_aliases: Default::default(),
parser: Default::default(),
}
}
}

impl<P, V: Deref> AliasesMap<P, V> {
/// Creates an empty aliases map with default-constructed parser.
pub fn new() -> Self
where
Expand All @@ -445,7 +458,7 @@ impl<P> AliasesMap<P> {
///
/// Returns error if `decl` is invalid. The `defn` part isn't checked. A bad
/// `defn` will be reported when the alias is substituted.
pub fn insert(&mut self, decl: impl AsRef<str>, defn: impl Into<String>) -> Result<(), P::Error>
pub fn insert(&mut self, decl: impl AsRef<str>, defn: impl Into<V>) -> Result<(), P::Error>
where
P: AliasDeclarationParser,
{
Expand Down Expand Up @@ -475,46 +488,53 @@ impl<P> AliasesMap<P> {
}

/// Looks up symbol alias by name. Returns identifier and definition text.
pub fn get_symbol(&self, name: &str) -> Option<(AliasId<'_>, &str)> {
pub fn get_symbol(&self, name: &str) -> Option<(AliasId<'_>, &<V as Deref>::Target)> {
self.symbol_aliases
.get_key_value(name)
.map(|(name, defn)| (AliasId::Symbol(name), defn.as_ref()))
.map(|(name, defn)| (AliasId::Symbol(name), defn.deref()))
}

/// Looks up function alias by name and arity. Returns identifier, list of
/// parameter names, and definition text.
pub fn get_function(&self, name: &str, arity: usize) -> Option<(AliasId<'_>, &[String], &str)> {
pub fn get_function(
&self,
name: &str,
arity: usize,
) -> Option<(AliasId<'_>, &[String], &<V as Deref>::Target)> {
let overloads = self.get_function_overloads(name)?;
overloads.find_by_arity(arity)
}

/// Looks up function aliases by name.
fn get_function_overloads(&self, name: &str) -> Option<AliasFunctionOverloads<'_>> {
fn get_function_overloads(&self, name: &str) -> Option<AliasFunctionOverloads<'_, V>> {
let (name, overloads) = self.function_aliases.get_key_value(name)?;
Some(AliasFunctionOverloads { name, overloads })
}
}

#[derive(Clone, Copy, Debug)]
struct AliasFunctionOverloads<'a> {
struct AliasFunctionOverloads<'a, V: Deref> {
name: &'a String,
overloads: &'a Vec<(Vec<String>, String)>,
overloads: &'a Vec<(Vec<String>, V)>,
}

impl<'a> AliasFunctionOverloads<'a> {
fn arities(self) -> impl DoubleEndedIterator<Item = usize> + ExactSizeIterator + 'a {
impl<'a, V: Deref> AliasFunctionOverloads<'a, V> {
fn arities(&self) -> impl DoubleEndedIterator<Item = usize> + ExactSizeIterator + 'a {
self.overloads.iter().map(|(params, _)| params.len())
}

fn min_arity(self) -> usize {
fn min_arity(&self) -> usize {
self.arities().next().unwrap()
}

fn max_arity(self) -> usize {
fn max_arity(&self) -> usize {
self.arities().next_back().unwrap()
}

fn find_by_arity(self, arity: usize) -> Option<(AliasId<'a>, &'a [String], &'a str)> {
fn find_by_arity(
&self,
arity: usize,
) -> Option<(AliasId<'a>, &'a [String], &'a <V as Deref>::Target)> {
let index = self
.overloads
.binary_search_by_key(&arity, |(params, _)| params.len())
Expand Down Expand Up @@ -609,7 +629,7 @@ pub trait AliasExpandError: Sized {
#[derive(Debug)]
struct AliasExpander<'i, T, P> {
/// Alias symbols and functions that are globally available.
aliases_map: &'i AliasesMap<P>,
aliases_map: &'i AliasesMap<P, String>,
/// Stack of aliases and local parameters currently expanding.
states: Vec<AliasExpandingState<'i, T>>,
}
Expand Down Expand Up @@ -708,7 +728,7 @@ where
/// Expands aliases recursively.
pub fn expand_aliases<'i, T, P>(
node: ExpressionNode<'i, T>,
aliases_map: &'i AliasesMap<P>,
aliases_map: &'i AliasesMap<P, String>,
) -> Result<ExpressionNode<'i, T>, P::Error>
where
T: AliasExpandableExpression<'i> + Clone,
Expand Down

0 comments on commit 0383806

Please sign in to comment.