Skip to content
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
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