diff --git a/build.rs b/build.rs index ca4ba40..7e62989 100644 --- a/build.rs +++ b/build.rs @@ -36,6 +36,7 @@ enum Def<'a> { enum Symbol<'a> { Single(char), Multi(Vec<(ModifierSet<&'a str>, char, Option<&'a str>)>), + MultiAlias(Vec<(ModifierSet, char, Option<&'a str>)>), } /// A single line during parsing. @@ -47,6 +48,7 @@ enum Line<'a> { ModuleEnd, Symbol(&'a str, Option), Variant(ModifierSet<&'a str>, char), + Alias(&'a str, &'a str, ModifierSet<&'a str>, bool), Eof, } @@ -56,6 +58,7 @@ enum Declaration<'a> { ModuleEnd, Symbol(&'a str, Option, Option<&'a str>), Variant(ModifierSet<&'a str>, char, Option<&'a str>), + Alias(&'a str, &'a str, ModifierSet<&'a str>, bool, Option<&'a str>), } fn main() { @@ -77,43 +80,46 @@ 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) - .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 + let mut iter = + text.lines() + .inspect(|_| line_nr += 1) + .map(tokenize) + .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::ModuleStart(name)) => { + Some(Ok(Declaration::ModuleStart(name, deprecation.take()))) } - } - 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(); + 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::Alias(alias, head, variant, deep)) => Some(Ok( + Declaration::Alias(alias, head, variant, deep, deprecation.take()), + )), + Ok(Line::Eof) => { + deprecation.map(|_| Err(String::from("dangling `@deprecated:`"))) + } + }) + .peekable(); let module = match parse(&mut iter) { Ok(defs) => Module::new(defs), @@ -158,6 +164,23 @@ fn tokenize(line: &str) -> StrResult { } let c = decode_char(tail.ok_or("missing char")?)?; Line::Variant(ModifierSet::from_raw_dotted(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, ModifierSet::from_raw_dotted(rest), deep) } else { validate_ident(head)?; let c = tail.map(decode_char).transpose()?; @@ -195,11 +218,15 @@ fn parse<'a>( p: &mut Peekable>>>, ) -> StrResult)>> { let mut defs = vec![]; + let mut aliases = vec![]; loop { match p.next().transpose()? { None | Some(Declaration::ModuleEnd) => { break; } + Some(Declaration::Alias(alias, name, variant, deep, deprecation)) => { + aliases.push((alias, name, variant, deep, deprecation)); + } Some(Declaration::Symbol(name, c, deprecation)) => { let mut variants = vec![]; while let Some(Declaration::Variant(name, c, deprecation)) = @@ -234,6 +261,80 @@ 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.is_empty() { + return Err(format!( + "alias to nonexistent variant: {name}.{}", + variant.as_str() + )); + } + defs.push(( + alias, + Binding { def: Def::Symbol(Symbol::Single(c)), deprecation }, + )); + } + Symbol::MultiAlias(_) => { + return Err(format!("alias to alias: {name}.{}", variant.as_str())); + } + Symbol::Multi(variants) => { + let variants: Vec<(ModifierSet, char, Option<&str>)> = variants + .iter() + .filter_map(|&(var, c, deprecation)| { + if !variant.is_subset(var) { + return None; + } + + let new_var = var.iter().filter(|&v| !variant.contains(v)).fold( + ModifierSet::default(), + |mut set, m| { + set.insert_raw(m); + set + }, + ); + + if !(deep || new_var.is_empty()) { + return None; + } + + Some((new_var, c, deprecation)) + }) + .collect(); + if variants.is_empty() { + return Err(format!( + "alias to nonexistent variant: {name}.{}", + variant.as_str() + )); + } + if let [(ref s, c, deprecation)] = 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) } @@ -253,6 +354,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(')'); } diff --git a/src/modules/sym.txt b/src/modules/sym.txt index 68ccf3f..e7195cb 100644 --- a/src/modules/sym.txt +++ b/src/modules/sym.txt @@ -206,7 +206,7 @@ breve ˘ caret ‸ caron ˇ hat ^ -diaer ¨ +diaer @= dot.double grave ` macron ¯ quote @@ -368,8 +368,7 @@ succ ≻ .not ⊁ .ntilde ⋩ .tilde ≿ -equiv ≡ - .not ≢ +equiv @= eq.triple.* smt ⪪ .eq ⪬ lat ⪫ @@ -387,12 +386,7 @@ emptyset ∅ .bar ⦱ .circle ⦲ .rev ⦰ -nothing ∅ - .arrow.r ⦳ - .arrow.l ⦴ - .bar ⦱ - .circle ⦲ - .rev ⦰ +nothing @= emptyset.* without ∖ complement ∁ in ∈ @@ -459,12 +453,12 @@ infinity ∞ .bar ⧞ .incomplete ⧜ .tie ⧝ -oo ∞ +oo @= infinity @deprecated: `diff` is deprecated, use `partial` instead -diff ∂ +diff @= partial.* partial ∂ gradient ∇ -nabla ∇ +nabla @= gradient.* sum ∑ .integral ⨋ product ∏ @@ -496,8 +490,8 @@ laplace ∆ forall ∀ exists ∃ .not ∄ -top ⊤ -bot ⊥ +top @= tack.b +bot @= tack.t not ¬ and ∧ .big ⋀ @@ -509,8 +503,7 @@ or ∨ .curly ⋎ .dot ⟇ .double ⩔ -xor ⊕ - .big ⨁ +xor @= plus.circle.* models ⊧ forces ⊩ .not ⊮ @@ -519,10 +512,9 @@ because ∵ qed ∎ // Function and category theory. -mapsto ↦ - .long ⟼ -compose ∘ -convolve ∗ +mapsto @= arrow.r.bar.* +compose @= circle.stroked.tiny +convolve @= ast.op multimap ⊸ .double ⧟ @@ -1088,16 +1080,16 @@ Sha Ш // See https://github.com/typst/typst/pull/3375. aleph א @deprecated: `alef` is deprecated, use `aleph` instead -alef א +alef @= aleph beth ב @deprecated: `bet` is deprecated, use `beth` instead -bet ב +bet @= beth gimel ג @deprecated: `gimmel` is deprecated, use `gimel` instead -gimmel ג +gimmel @= gimel daleth ד @deprecated: `dalet` is deprecated, use `daleth` instead -dalet ד +dalet @= daleth @deprecated: `shin` is deprecated, perhaps you meant to use `sha` instead shin ש