Skip to content

Commit

Permalink
Make it possible to deprecate symbols and modules (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
MDLC01 authored Feb 3, 2025
1 parent e43cb08 commit 0672cf9
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 22 deletions.
46 changes: 35 additions & 11 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@ use std::path::Path;
type StrResult<T> = Result<T, String>;

/// A module of definitions.
struct Module<'a>(Vec<(&'a str, Def<'a>)>);
struct Module<'a>(Vec<(&'a str, Binding<'a>)>);

impl<'a> Module<'a> {
fn new(mut list: Vec<(&'a str, Def<'a>)>) -> Self {
fn new(mut list: Vec<(&'a str, Binding<'a>)>) -> Self {
list.sort_by_key(|&(name, _)| name);
Self(list)
}
}

/// A definition bound in a module, with metadata.
struct Binding<'a> {
def: Def<'a>,
deprecation: Option<&'a str>,
}

/// A definition in a module.
enum Def<'a> {
Symbol(Symbol<'a>),
Expand All @@ -30,6 +36,7 @@ enum Symbol<'a> {
#[derive(Debug, Copy, Clone)]
enum Line<'a> {
Blank,
Deprecated(&'a str),
ModuleStart(&'a str),
ModuleEnd,
Symbol(&'a str, Option<char>),
Expand Down Expand Up @@ -91,7 +98,9 @@ fn tokenize(line: &str) -> StrResult<Line> {
None => (line, None),
};

Ok(if tail == Some("{") {
Ok(if head == "@deprecated:" {
Line::Deprecated(tail.ok_or("missing deprecation message")?.trim())
} else if tail == Some("{") {
validate_ident(head)?;
Line::ModuleStart(head)
} else if head == "}" && tail.is_none() {
Expand Down Expand Up @@ -137,11 +146,18 @@ 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>>>>,
) -> StrResult<Vec<(&'a str, Def<'a>)>> {
) -> StrResult<Vec<(&'a str, Binding<'a>)>> {
let mut defs = vec![];
let mut deprecation = None;
loop {
match p.next().transpose()? {
None | Some(Line::ModuleEnd) => break,
None | Some(Line::ModuleEnd) => {
if let Some(message) = deprecation {
return Err(format!("dangling `@deprecated: {}`", message));
}
break;
}
Some(Line::Deprecated(message)) => deprecation = Some(message),
Some(Line::Symbol(name, c)) => {
let mut variants = vec![];
while let Some(Line::Variant(name, c)) = p.peek().cloned().transpose()? {
Expand All @@ -159,11 +175,19 @@ fn parse<'a>(
Symbol::Single(c)
};

defs.push((name, Def::Symbol(symbol)));
defs.push((name, Binding { def: Def::Symbol(symbol), deprecation }));
deprecation = None;
}
Some(Line::ModuleStart(name)) => {
let module_defs = parse(p)?;
defs.push((name, Def::Module(Module::new(module_defs))));
defs.push((
name,
Binding {
def: Def::Module(Module::new(module_defs)),
deprecation,
},
));
deprecation = None;
}
other => return Err(format!("expected definition, found {other:?}")),
}
Expand All @@ -174,9 +198,9 @@ fn parse<'a>(
/// Encodes a `Module` into Rust code.
fn encode(buf: &mut String, module: &Module) {
buf.push_str("Module(&[");
for (name, def) in &module.0 {
write!(buf, "({name:?},").unwrap();
match def {
for (name, entry) in &module.0 {
write!(buf, "({name:?}, Binding {{ def: ").unwrap();
match &entry.def {
Def::Module(module) => {
buf.push_str("Def::Module(");
encode(buf, module);
Expand All @@ -191,7 +215,7 @@ fn encode(buf: &mut String, module: &Module) {
buf.push_str(")");
}
}
buf.push_str("),");
write!(buf, ", deprecation: {:?} }}),", entry.deprecation).unwrap();
}
buf.push_str("])");
}
40 changes: 29 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,42 @@ Human-friendly notation for Unicode symbols.
*/

/// A module of definitions.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Module(&'static [(&'static str, Def)]);
#[derive(Debug, Copy, Clone)]
pub struct Module(&'static [(&'static str, Binding)]);

impl Module {
/// Try to get a definition from the module.
pub fn get(&self, name: &str) -> Option<Def> {
/// Try to get a bound definition in the module.
pub fn get(&self, name: &str) -> Option<Binding> {
self.0
.binary_search_by_key(&name, |(k, _)| k)
.ok()
.map(|i| self.0[i].1)
}

/// Iterate over the module's definition.
pub fn iter(&self) -> impl Iterator<Item = (&'static str, Def)> {
pub fn iter(&self) -> impl Iterator<Item = (&'static str, Binding)> {
self.0.iter().copied()
}
}

/// A definition bound in a module, with metadata.
#[derive(Debug, Copy, Clone)]
pub struct Binding {
/// The bound definition.
pub def: Def,
/// A deprecation message for the definition, if it is deprecated.
pub deprecation: Option<&'static str>,
}

impl Binding {
/// Create a new bound definition.
pub const fn new(definition: Def) -> Self {
Self { def: definition, deprecation: None }
}
}

/// A definition in a module.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[derive(Debug, Copy, Clone)]
pub enum Def {
/// A symbol, potentially with modifiers.
Symbol(Symbol),
Expand All @@ -31,7 +47,7 @@ pub enum Def {
}

/// A symbol, either a leaf or with modifiers.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[derive(Debug, Copy, Clone)]
pub enum Symbol {
/// A symbol without modifiers.
Single(char),
Expand All @@ -40,8 +56,10 @@ pub enum Symbol {
}

/// A module that contains the other top-level modules.
pub const ROOT: Module =
Module(&[("emoji", Def::Module(EMOJI)), ("sym", Def::Module(SYM))]);
pub const ROOT: Module = Module(&[
("emoji", Binding::new(Def::Module(EMOJI))),
("sym", Binding::new(Def::Module(SYM))),
]);

include!(concat!(env!("OUT_DIR"), "/out.rs"));

Expand All @@ -54,8 +72,8 @@ mod test {
fn assert_sorted_recursively(root: Module) {
assert!(root.0.is_sorted_by_key(|(k, _)| k));

for (_, def) in root.iter() {
if let Def::Module(module) = def {
for (_, entry) in root.iter() {
if let Def::Module(module) = entry.def {
assert_sorted_recursively(module)
}
}
Expand Down

0 comments on commit 0672cf9

Please sign in to comment.