-
-
Notifications
You must be signed in to change notification settings - Fork 11
Codify and Resolve modifiers #46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
a3a16a7
536dc49
4895855
1fdbb1e
a5cbb19
34edb09
ff6b56a
039f401
b99817a
17d2dff
65e5b01
7157b82
120d424
d7d2c96
6ef866a
477a343
1e8e94b
02ea0fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
use std::ops::{AddAssign, Deref}; | ||
T0mstone marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
macro_rules! declare_types { | ||
($(<$lt:lifetime>)? | ||
$(derive($($Der:ident),*),)? | ||
str = $s:ty, | ||
List = $List:ident<_> | ||
MDLC01 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
) => { | ||
/// A module of definitions. | ||
$(#[derive($($Der),*)])? | ||
pub struct Module<$($lt)?>($List<($s, Binding<$($lt)?>)>); | ||
|
||
/// A definition bound in a module, with metadata. | ||
$(#[derive($($Der),*)])? | ||
pub struct Binding<$($lt)?> { | ||
/// The bound definition. | ||
pub def: Def<$($lt)?>, | ||
/// A deprecation message for the definition, if it is deprecated. | ||
pub deprecation: Option<$s>, | ||
} | ||
|
||
impl<$($lt)?> Binding<$($lt)?> { | ||
/// Create a new bound definition. | ||
pub const fn new(definition: Def<$($lt)?>) -> Self { | ||
Self { def: definition, deprecation: None } | ||
} | ||
} | ||
|
||
/// A definition in a module. | ||
$(#[derive($($Der),*)])? | ||
pub enum Def<$($lt)?> { | ||
/// A symbol, potentially with modifiers. | ||
Symbol(Symbol<$($lt)?>), | ||
/// A nested module. | ||
Module(Module<$($lt)?>), | ||
} | ||
|
||
/// A symbol, either a leaf or with modifiers. | ||
$(#[derive($($Der),*)])? | ||
pub enum Symbol<$($lt)?> { | ||
/// A symbol without modifiers. | ||
Single(char), | ||
/// A symbol with named modifiers. | ||
/// The symbol defaults to its first variant. | ||
Multi($List<(ModifierSet<$s>, char)>), | ||
} | ||
}; | ||
} | ||
|
||
/// A set of modifiers. | ||
#[derive(Debug, Copy, Clone)] | ||
pub struct ModifierSet<S>(S); | ||
|
||
impl<S: Deref<Target = str>> ModifierSet<S> { | ||
/// Convert the underlying string to a slice. | ||
pub fn as_deref(&self) -> ModifierSet<&str> { | ||
ModifierSet(&self.0) | ||
} | ||
|
||
/// Construct a modifier set from a string, | ||
/// where modifiers are separated by the character `.`. | ||
/// | ||
/// It is not unsafe to use this function wrongly, but it can produce | ||
/// unexpected results down the line. Correct usage should ensure that | ||
/// `s` does not contain any empty modifiers (i.e. the sequence `..`) | ||
/// and that no modifier occurs twice. | ||
pub fn new_unchecked(s: S) -> Self { | ||
MDLC01 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
Self(s) | ||
} | ||
|
||
/// Construct an empty modifier set. | ||
pub fn empty() -> Self | ||
where | ||
S: Default, | ||
{ | ||
Self(S::default()) | ||
} | ||
T0mstone marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
/// Whether `self` is empty. | ||
pub fn is_empty(&self) -> bool { | ||
self.0.is_empty() | ||
} | ||
|
||
/// Add a modifier to the set, without checking that it is a valid modifier. | ||
/// | ||
/// It is not unsafe to use this method wrongly, but that can produce | ||
/// unexpected results down the line. Correct usage should ensure that | ||
/// `modifier` is not empty and doesn't contain the character `.`. | ||
pub fn add_unchecked(&mut self, m: &str) | ||
T0mstone marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
where | ||
S: for<'a> AddAssign<&'a str>, | ||
{ | ||
if !self.0.is_empty() { | ||
self.0 += "."; | ||
} | ||
self.0 += m; | ||
} | ||
|
||
/// Iterate over the list of modifiers in an arbitrary order. | ||
pub fn iter(&self) -> impl Iterator<Item = &str> { | ||
self.0.split('.').filter(|s| !s.is_empty()) | ||
} | ||
T0mstone marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
/// Whether the set contains the modifier `m`. | ||
pub fn contains(&self, m: &str) -> bool { | ||
self.iter().any(|lhs| lhs == m) | ||
} | ||
|
||
/// Whether all modifiers in `self` are also present in `other`. | ||
pub fn is_subset(&self, other: ModifierSet<&str>) -> bool { | ||
self.iter().all(|m| other.contains(m)) | ||
} | ||
|
||
/// Find the best match from the list. | ||
/// | ||
/// To be considered a match, the modifier set must be a superset of | ||
/// (or equal to) `self`. Among different matches, the best one is selected | ||
/// by the following two criteria (in order): | ||
/// 1. Number of modifiers in common with `self` (more is better). | ||
/// 2. Total number of modifiers (fewer is better). | ||
pub fn best_match_in<'a, T>( | ||
&self, | ||
variants: impl Iterator<Item = (ModifierSet<&'a str>, T)>, | ||
) -> Option<T> { | ||
let mut best = None; | ||
let mut best_score = None; | ||
|
||
// Find the best table entry with this name. | ||
for candidate in variants.filter(|(set, _)| self.is_subset(*set)) { | ||
let mut matching = 0; | ||
let mut total = 0; | ||
for modifier in candidate.0.iter() { | ||
if self.contains(modifier) { | ||
matching += 1; | ||
} | ||
total += 1; | ||
} | ||
|
||
let score = (matching, core::cmp::Reverse(total)); | ||
T0mstone marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
if best_score.map_or(true, |b| score > b) { | ||
T0mstone marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
best = Some(candidate.1); | ||
best_score = Some(score); | ||
} | ||
} | ||
|
||
best | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.