Skip to content

Allow deprecating variants #86

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

Merged
merged 3 commits into from
Jun 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 54 additions & 17 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::fmt::Write;
use std::iter;
use std::iter::Peekable;
use std::path::Path;

Expand Down Expand Up @@ -31,10 +32,10 @@ enum Def<'a> {
Module(Module<'a>),
}

/// A symbol, either a leaf or with modifiers.
/// A symbol, either a leaf or with modifiers with optional deprecation.
enum Symbol<'a> {
Single(char),
Multi(Vec<(ModifierSet<&'a str>, char)>),
Multi(Vec<(ModifierSet<&'a str>, char, Option<&'a str>)>),
}

/// A single line during parsing.
Expand All @@ -46,6 +47,15 @@ enum Line<'a> {
ModuleEnd,
Symbol(&'a str, Option<char>),
Variant(ModifierSet<&'a str>, char),
Eof,
}

#[derive(Debug, Copy, Clone)]
enum Declaration<'a> {
ModuleStart(&'a str, Option<&'a str>),
ModuleEnd,
Symbol(&'a str, Option<char>, Option<&'a str>),
Variant(ModifierSet<&'a str>, char, Option<&'a str>),
}

fn main() {
Expand All @@ -66,11 +76,43 @@ fn process(buf: &mut String, file: &Path, name: &str, desc: &str) {

let text = std::fs::read_to_string(file).unwrap();
let mut line_nr = 0;
let mut deprecation = None;
let mut iter = text
.lines()
.inspect(|_| line_nr += 1)
.map(tokenize)
.filter(|line| !matches!(line, Ok(Line::Blank)))
.chain(iter::once(Ok(Line::Eof)))
.filter_map(|line| match line {
Err(message) => Some(Err(message)),
Ok(Line::Blank) => None,
Ok(Line::Deprecated(message)) => {
if deprecation.is_some() {
Some(Err(String::from("duplicate `@deprecated:`")))
} else {
deprecation = Some(message);
None
}
}
Ok(Line::ModuleStart(name)) => {
Some(Ok(Declaration::ModuleStart(name, deprecation.take())))
}
Ok(Line::ModuleEnd) => {
if deprecation.is_some() {
Some(Err(String::from("dangling `@deprecated:`")))
} else {
Some(Ok(Declaration::ModuleEnd))
}
}
Ok(Line::Symbol(name, c)) => {
Some(Ok(Declaration::Symbol(name, c, deprecation.take())))
}
Ok(Line::Variant(modifiers, c)) => {
Some(Ok(Declaration::Variant(modifiers, c, deprecation.take())))
}
Ok(Line::Eof) => {
deprecation.map(|_| Err(String::from("dangling `@deprecated:`")))
}
})
.peekable();

let module = match parse(&mut iter) {
Expand Down Expand Up @@ -150,29 +192,26 @@ fn decode_char(text: &str) -> StrResult<char> {

/// Turns a stream of lines into a list of definitions.
fn parse<'a>(
p: &mut Peekable<impl Iterator<Item = StrResult<Line<'a>>>>,
p: &mut Peekable<impl Iterator<Item = StrResult<Declaration<'a>>>>,
) -> StrResult<Vec<(&'a str, Binding<'a>)>> {
let mut defs = vec![];
let mut deprecation = None;
loop {
match p.next().transpose()? {
None | Some(Line::ModuleEnd) => {
if let Some(message) = deprecation {
return Err(format!("dangling `@deprecated: {}`", message));
}
None | Some(Declaration::ModuleEnd) => {
break;
}
Some(Line::Deprecated(message)) => deprecation = Some(message),
Some(Line::Symbol(name, c)) => {
Some(Declaration::Symbol(name, c, deprecation)) => {
let mut variants = vec![];
while let Some(Line::Variant(name, c)) = p.peek().cloned().transpose()? {
variants.push((name, c));
while let Some(Declaration::Variant(name, c, deprecation)) =
p.peek().cloned().transpose()?
{
variants.push((name, c, deprecation));
p.next();
}

let symbol = if !variants.is_empty() {
if let Some(c) = c {
variants.insert(0, (ModifierSet::default(), c));
variants.insert(0, (ModifierSet::default(), c, None));
}
Symbol::Multi(variants)
} else {
Expand All @@ -181,9 +220,8 @@ fn parse<'a>(
};

defs.push((name, Binding { def: Def::Symbol(symbol), deprecation }));
deprecation = None;
}
Some(Line::ModuleStart(name)) => {
Some(Declaration::ModuleStart(name, deprecation)) => {
let module_defs = parse(p)?;
defs.push((
name,
Expand All @@ -192,7 +230,6 @@ fn parse<'a>(
deprecation,
},
));
deprecation = None;
}
other => return Err(format!("expected definition, found {other:?}")),
}
Expand Down
32 changes: 22 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,44 +55,56 @@ pub enum Def {
Module(Module),
}

/// A symbol, either a leaf or with modifiers.
/// A symbol, either a leaf or with modifiers and optional deprecation.
#[derive(Debug, Copy, Clone)]
pub enum Symbol {
/// A symbol without modifiers.
Single(char),
/// A symbol with named modifiers. The symbol defaults to its first variant.
Multi(&'static [(ModifierSet<&'static str>, char)]),
Multi(&'static [(ModifierSet<&'static str>, char, Option<&'static str>)]),
}

impl Symbol {
/// Get the symbol's character for a given set of modifiers.
pub fn get(&self, modifs: ModifierSet<&str>) -> Option<char> {
/// Get the symbol's character for a given set of modifiers, alongside an optional deprecation
/// message.
pub fn get(&self, modifs: ModifierSet<&str>) -> Option<(char, Option<&str>)> {
match self {
Self::Single(c) => modifs.is_empty().then_some(*c),
Self::Multi(list) => modifs.best_match_in(list.iter().copied()),
Self::Single(c) => modifs.is_empty().then_some((*c, None)),
Self::Multi(list) => {
modifs.best_match_in(list.iter().copied().map(|(m, c, d)| (m, (c, d))))
}
}
}

/// The characters that are covered by this symbol.
pub fn variants(&self) -> impl Iterator<Item = (ModifierSet<&str>, char)> {
///
/// Each variant is represented by a tuple `(modifiers, character, deprecation)`.
pub fn variants(
&self,
) -> impl Iterator<Item = (ModifierSet<&str>, char, Option<&str>)> {
enum Variants {
Single(std::iter::Once<char>),
Multi(std::slice::Iter<'static, (ModifierSet<&'static str>, char)>),
Multi(
std::slice::Iter<
'static,
(ModifierSet<&'static str>, char, Option<&'static str>),
>,
),
}
let mut iter = match self {
Self::Single(c) => Variants::Single(std::iter::once(*c)),
Self::Multi(sl) => Variants::Multi(sl.iter()),
};
std::iter::from_fn(move || match &mut iter {
Variants::Single(iter) => Some((ModifierSet::default(), iter.next()?)),
Variants::Single(iter) => Some((ModifierSet::default(), iter.next()?, None)),
Variants::Multi(iter) => iter.next().copied(),
})
}

/// Possible modifiers for this symbol.
pub fn modifiers(&self) -> impl Iterator<Item = &str> + '_ {
self.variants()
.flat_map(|(m, _)| m.into_iter())
.flat_map(|(m, _, _)| m.into_iter())
.collect::<std::collections::BTreeSet<_>>()
.into_iter()
}
Expand Down
4 changes: 2 additions & 2 deletions src/modules/sym.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ angle ∠
.spheric ∢
.spheric.rev ⦠
.spheric.t ⦡
// Deprecated.
@deprecated: `angle.spheric.top` is deprecated, use `angle.spheric.t` instead
.spheric.top ⦡
ceil
.l ⌈
Expand Down Expand Up @@ -481,7 +481,7 @@ integral ∫
.double ∬
.quad ⨌
.inter ⨙
// Deprecated.
@deprecated: `integral.sect` is deprecated, use `integral.inter` instead
.sect ⨙
.slash ⨏
.square ⨖
Expand Down