From 17aa7f07c6252a356a8b13fbc81bd6a75c66d9ef Mon Sep 17 00:00:00 2001 From: T0mstone Date: Mon, 20 Jan 2025 17:29:38 +0100 Subject: [PATCH 1/2] Implement and use aliases --- build.rs | 102 ++++++++++++++++++++++++++++++++++++++++++++ src/modules/sym.txt | 35 ++++++--------- 2 files changed, 116 insertions(+), 21 deletions(-) diff --git a/build.rs b/build.rs index 2e2fd45..baea1c2 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; @@ -24,6 +25,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. @@ -34,6 +36,7 @@ enum Line<'a> { ModuleEnd, Symbol(&'a str, Option), Variant(&'a str, char), + Alias(&'a str, &'a str, &'a str, bool), } fn main() { @@ -102,6 +105,23 @@ fn tokenize(line: &str) -> StrResult { } let c = decode_char(tail.ok_or("missing char")?)?; Line::Variant(rest, c) + } else if let Some(alias) = head.strip_prefix('@') { + validate_ident(alias)?; + let mut value = tail.ok_or("missing value")?; + 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()?; @@ -139,9 +159,13 @@ fn parse<'a>( p: &mut Peekable>>>, ) -> StrResult)>> { let mut defs = vec![]; + let mut aliases = vec![]; loop { match p.next().transpose()? { None | Some(Line::ModuleEnd) => break, + Some(Line::Alias(alias, name, variant, deep)) => { + aliases.push((alias, name, variant, deep)); + } Some(Line::Symbol(name, c)) => { let mut variants = vec![]; while let Some(Line::Variant(name, c)) = p.peek().cloned().transpose()? { @@ -169,6 +193,83 @@ fn parse<'a>( other => return Err(format!("expected definition, found {other:?}")), } } + for (alias, name, variant, deep) in aliases { + let aliased_symbol: &Symbol<'a> = defs + .iter() + .filter(|(n, _)| *n == name) + .find_map(|(_, def)| match 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, Def::Symbol(Symbol::Single(c)))); + } + 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, Def::Symbol(Symbol::Single(c)))); + continue; + } + } + defs.push((alias, Def::Symbol(Symbol::MultiAlias(variants)))); + } + } + } Ok(defs) } @@ -188,6 +289,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 cdd0448..1df412f 100644 --- a/src/modules/sym.txt +++ b/src/modules/sym.txt @@ -193,7 +193,7 @@ breve ˘ caret ‸ caron ˇ hat ^ -diaer ¨ +@diaer dot.double grave ` macron ¯ quote @@ -359,8 +359,7 @@ succ ≻ .not ⊁ .ntilde ⋩ .tilde ≿ -equiv ≡ - .not ≢ +@equiv eq.triple.* smt ⪪ .eq ⪬ lat ⪫ @@ -378,12 +377,7 @@ emptyset ∅ .bar ⦱ .circle ⦲ .rev ⦰ -nothing ∅ - .arrow.r ⦳ - .arrow.l ⦴ - .bar ⦱ - .circle ⦲ - .rev ⦰ +@nothing emptyset.* without ∖ complement ∁ in ∈ @@ -441,10 +435,10 @@ infinity ∞ .bar ⧞ .incomplete ⧜ .tie ⧝ -oo ∞ +@oo infinity partial ∂ gradient ∇ -nabla ∇ +@nabla gradient.* sum ∑ .integral ⨋ product ∏ @@ -474,8 +468,8 @@ laplace ∆ forall ∀ exists ∃ .not ∄ -top ⊤ -bot ⊥ +@top tack.b +@bot tack.t not ¬ and ∧ .big ⋀ @@ -487,8 +481,7 @@ or ∨ .curly ⋎ .dot ⟇ .double ⩔ -xor ⊕ - .big ⨁ +@xor plus.circle.* models ⊧ forces ⊩ .not ⊮ @@ -497,8 +490,8 @@ because ∵ qed ∎ // Function and category theory. -compose ∘ -convolve ∗ +@compose circle.stroked.tiny +@convolve ast.op multimap ⊸ .double ⧟ @@ -987,13 +980,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. From f749380e8eb9fef42eedef307ce4e689a1dd34e9 Mon Sep 17 00:00:00 2001 From: T0mstone Date: Mon, 20 Jan 2025 21:05:53 +0100 Subject: [PATCH 2/2] Change syntax This avoids it visually colliding with the deprecation annotation from #19. --- build.rs | 4 ++-- src/modules/sym.txt | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/build.rs b/build.rs index baea1c2..8e3036e 100644 --- a/build.rs +++ b/build.rs @@ -105,9 +105,9 @@ fn tokenize(line: &str) -> StrResult { } let c = decode_char(tail.ok_or("missing char")?)?; Line::Variant(rest, c) - } else if let Some(alias) = head.strip_prefix('@') { + } else if let Some(mut value) = tail.and_then(|tail| tail.strip_prefix("@= ")) { + let alias = head; validate_ident(alias)?; - let mut value = tail.ok_or("missing value")?; let mut deep = false; if let Some(v) = value.strip_suffix(".*") { deep = true; diff --git a/src/modules/sym.txt b/src/modules/sym.txt index 1df412f..1f26962 100644 --- a/src/modules/sym.txt +++ b/src/modules/sym.txt @@ -193,7 +193,7 @@ breve ˘ caret ‸ caron ˇ hat ^ -@diaer dot.double +diaer @= dot.double grave ` macron ¯ quote @@ -359,7 +359,7 @@ succ ≻ .not ⊁ .ntilde ⋩ .tilde ≿ -@equiv eq.triple.* +equiv @= eq.triple.* smt ⪪ .eq ⪬ lat ⪫ @@ -377,7 +377,7 @@ emptyset ∅ .bar ⦱ .circle ⦲ .rev ⦰ -@nothing emptyset.* +nothing @= emptyset.* without ∖ complement ∁ in ∈ @@ -435,10 +435,10 @@ infinity ∞ .bar ⧞ .incomplete ⧜ .tie ⧝ -@oo infinity +oo @= infinity partial ∂ gradient ∇ -@nabla gradient.* +nabla @= gradient.* sum ∑ .integral ⨋ product ∏ @@ -468,8 +468,8 @@ laplace ∆ forall ∀ exists ∃ .not ∄ -@top tack.b -@bot tack.t +top @= tack.b +bot @= tack.t not ¬ and ∧ .big ⋀ @@ -481,7 +481,7 @@ or ∨ .curly ⋎ .dot ⟇ .double ⩔ -@xor plus.circle.* +xor @= plus.circle.* models ⊧ forces ⊩ .not ⊮ @@ -490,8 +490,8 @@ because ∵ qed ∎ // Function and category theory. -@compose circle.stroked.tiny -@convolve ast.op +compose @= circle.stroked.tiny +convolve @= ast.op multimap ⊸ .double ⧟ @@ -980,13 +980,13 @@ Zeta Ζ // from Letterlike Symbols. // See https://github.com/typst/typst/pull/3375. aleph א -@alef aleph +alef @= aleph beth ב -@bet beth +bet @= beth gimmel ג -@gimel gimmel +gimel @= gimmel daleth ד -@dalet daleth +dalet @= daleth shin ש // Double-struck.