diff --git a/build.rs b/build.rs index 80286bd..337a7a7 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::fmt::Write; use std::iter::Peekable; use std::path::Path; @@ -30,6 +31,7 @@ enum Def<'a> { enum Symbol<'a> { Single(char), Multi(Vec<(&'a str, char)>), + MultiAlias(Vec<(Cow<'a, str>, char)>), } /// A single line during parsing. @@ -41,6 +43,7 @@ enum Line<'a> { ModuleEnd, Symbol(&'a str, Option), Variant(&'a str, char), + Alias(&'a str, &'a str, &'a str, bool), } fn main() { @@ -111,6 +114,23 @@ fn tokenize(line: &str) -> StrResult { } let c = decode_char(tail.ok_or("missing char")?)?; Line::Variant(rest, c) + } else if let Some(mut value) = tail.and_then(|tail| tail.strip_prefix("@= ")) { + let alias = head; + validate_ident(alias)?; + let mut deep = false; + if let Some(v) = value.strip_suffix(".*") { + deep = true; + value = v; + } + let (head, rest) = value.split_once('.').unwrap_or((value, "")); + validate_ident(head)?; + if !rest.is_empty() { + for part in rest.split('.') { + validate_ident(part)?; + } + } + + Line::Alias(alias, head, rest, deep) } else { validate_ident(head)?; let c = tail.map(decode_char).transpose()?; @@ -149,6 +169,7 @@ fn parse<'a>( ) -> StrResult)>> { let mut defs = vec![]; let mut deprecation = None; + let mut aliases = vec![]; loop { match p.next().transpose()? { None | Some(Line::ModuleEnd) => { @@ -158,6 +179,10 @@ fn parse<'a>( break; } Some(Line::Deprecated(message)) => deprecation = Some(message), + Some(Line::Alias(alias, name, variant, deep)) => { + aliases.push((alias, name, variant, deep, deprecation)); + deprecation = None; + } Some(Line::Symbol(name, c)) => { let mut variants = vec![]; while let Some(Line::Variant(name, c)) = p.peek().cloned().transpose()? { @@ -192,6 +217,95 @@ fn parse<'a>( other => return Err(format!("expected definition, found {other:?}")), } } + for (alias, name, variant, deep, deprecation) in aliases { + let aliased_symbol: &Symbol<'a> = defs + .iter() + .filter(|(n, _)| *n == name) + .find_map(|(_, b)| match &b.def { + Def::Symbol(s) => Some(s), + _ => None, + }) + .ok_or_else(|| format!("alias to nonexistent symbol: {name}"))?; + + match aliased_symbol { + &Symbol::Single(c) => { + if variant != "" { + return Err(format!( + "alias to nonexistent variant: {name}.{variant}" + )); + } + defs.push(( + alias, + Binding { def: Def::Symbol(Symbol::Single(c)), deprecation }, + )); + } + Symbol::MultiAlias(_) => { + return Err(format!("alias to alias: {name}.{variant}")); + } + Symbol::Multi(variants) => { + let variants: Vec<(Cow<'a, str>, char)> = variants + .iter() + .filter_map(|&(var, c)| { + if var == variant { + Some((Cow::Borrowed(""), c)) + } else if deep { + var.strip_prefix(variant)? + .strip_prefix('.') + .map(|v| (Cow::Borrowed(v), c)) + .or_else(|| { + // might just be in a different order + + let mut alias_modifs = if variant.is_empty() { + vec![] + } else { + variant.split('.').collect() + }; + + let mut new_variant = Cow::Borrowed(""); + for modif in var.split('.') { + if let Some(i) = + alias_modifs.iter().position(|m| *m == modif) + { + alias_modifs.swap_remove(i); + } else if new_variant.is_empty() { + new_variant = Cow::Borrowed(modif); + } else { + new_variant = Cow::Owned(format!( + "{new_variant}.{modif}" + )); + } + } + alias_modifs.is_empty().then_some((new_variant, c)) + }) + } else { + None + } + }) + .collect(); + if variants.is_empty() { + return Err(format!( + "alias to nonexistent variant: {name}.{variant}" + )); + } + if let [(ref s, c)] = variants[..] { + if s.is_empty() { + defs.push(( + alias, + Binding { def: Def::Symbol(Symbol::Single(c)), deprecation }, + )); + continue; + } + } + defs.push(( + alias, + Binding { + def: Def::Symbol(Symbol::MultiAlias(variants)), + deprecation, + }, + )); + } + } + } Ok(defs) } @@ -211,6 +325,7 @@ fn encode(buf: &mut String, module: &Module) { match symbol { Symbol::Single(c) => write!(buf, "Single({c:?})").unwrap(), Symbol::Multi(list) => write!(buf, "Multi(&{list:?})").unwrap(), + Symbol::MultiAlias(list) => write!(buf, "Multi(&{list:?})").unwrap(), } buf.push_str(")"); } diff --git a/src/modules/sym.txt b/src/modules/sym.txt index b4e8b32..029a093 100644 --- a/src/modules/sym.txt +++ b/src/modules/sym.txt @@ -199,7 +199,7 @@ breve ˘ caret ‸ caron ˇ hat ^ -diaer ¨ +diaer @= dot.double grave ` macron ¯ quote @@ -365,8 +365,7 @@ succ ≻ .not ⊁ .ntilde ⋩ .tilde ≿ -equiv ≡ - .not ≢ +equiv @= eq.triple.* smt ⪪ .eq ⪬ lat ⪫ @@ -384,12 +383,7 @@ emptyset ∅ .bar ⦱ .circle ⦲ .rev ⦰ -nothing ∅ - .arrow.r ⦳ - .arrow.l ⦴ - .bar ⦱ - .circle ⦲ - .rev ⦰ +nothing @= emptyset.* without ∖ complement ∁ in ∈ @@ -447,11 +441,11 @@ infinity ∞ .bar ⧞ .incomplete ⧜ .tie ⧝ -oo ∞ -diff ∂ +oo @= infinity +diff @= partial.* partial ∂ gradient ∇ -nabla ∇ +nabla @= gradient.* sum ∑ .integral ⨋ product ∏ @@ -481,8 +475,8 @@ laplace ∆ forall ∀ exists ∃ .not ∄ -top ⊤ -bot ⊥ +top @= tack.b +bot @= tack.t not ¬ and ∧ .big ⋀ @@ -494,8 +488,7 @@ or ∨ .curly ⋎ .dot ⟇ .double ⩔ -xor ⊕ - .big ⨁ +xor @= plus.circle.* models ⊧ forces ⊩ .not ⊮ @@ -504,10 +497,9 @@ because ∵ qed ∎ // Function and category theory. -mapsto ↦ - .long ⟼ -compose ∘ -convolve ∗ +mapsto @= arrow.r.bar.* +compose @= circle.stroked.tiny +convolve @= ast.op multimap ⊸ .double ⧟ @@ -999,13 +991,13 @@ Zeta Ζ // from Letterlike Symbols. // See https://github.com/typst/typst/pull/3375. aleph א -alef א +alef @= aleph beth ב -bet ב +bet @= beth gimmel ג -gimel ג +gimel @= gimmel daleth ד -dalet ד +dalet @= daleth shin ש // Double-struck.