1use std::collections::BTreeSet;
2use std::fmt::{self, Debug, Display, Formatter};
3use std::sync::Arc;
4
5use codex::ModifierSet;
6use ecow::{EcoString, eco_format};
7use rustc_hash::FxHashMap;
8use serde::{Serialize, Serializer};
9use typst_syntax::{Span, Spanned, is_ident};
10use typst_utils::hash128;
11use unicode_segmentation::UnicodeSegmentation;
12
13use crate::diag::{SourceResult, StrResult, WarningSink, bail, error};
14use crate::foundations::{
15 Array, Content, Func, NativeElement, Packed, PlainText, Repr, cast, elem, func,
16 scope, ty,
17};
18
19#[ty(scope, cast)]
50#[derive(Debug, Clone, Eq, PartialEq, Hash)]
51pub struct Symbol(SymbolInner);
52
53#[derive(Clone, Eq, PartialEq, Hash)]
55enum SymbolInner {
56 Single(&'static str),
58 Complex(&'static [Variant<&'static str>]),
60 Modified(Arc<Modified>),
62}
63
64#[derive(Debug, Clone, Eq, PartialEq, Hash)]
68struct Modified {
69 list: List,
71 modifiers: ModifierSet<EcoString>,
73 deprecated: bool,
76}
77
78type Variant<S> = (ModifierSet<S>, S, Option<S>);
81
82#[derive(Clone, Eq, PartialEq, Hash)]
84enum List {
85 Static(&'static [Variant<&'static str>]),
86 Runtime(Box<[Variant<EcoString>]>),
87}
88
89impl Symbol {
90 pub const fn single(value: &'static str) -> Self {
92 Self(SymbolInner::Single(value))
93 }
94
95 #[track_caller]
97 pub const fn list(list: &'static [Variant<&'static str>]) -> Self {
98 debug_assert!(!list.is_empty());
99 Self(SymbolInner::Complex(list))
100 }
101
102 pub fn runtime_char(c: char) -> Self {
104 Self::runtime(Box::new([(ModifierSet::default(), c.into(), None)]))
105 }
106
107 #[track_caller]
109 pub fn runtime(list: Box<[Variant<EcoString>]>) -> Self {
110 debug_assert!(!list.is_empty());
111 Self(SymbolInner::Modified(Arc::new(Modified {
112 list: List::Runtime(list),
113 modifiers: ModifierSet::default(),
114 deprecated: false,
115 })))
116 }
117
118 pub fn get(&self) -> &str {
120 match &self.0 {
121 SymbolInner::Single(value) => value,
122 SymbolInner::Complex(_) => ModifierSet::<&'static str>::default()
123 .best_match_in(self.variants().map(|(m, v, _)| (m, v)))
124 .unwrap(),
125 SymbolInner::Modified(arc) => arc
126 .modifiers
127 .best_match_in(self.variants().map(|(m, v, _)| (m, v)))
128 .unwrap(),
129 }
130 }
131
132 pub fn func(&self) -> StrResult<Func> {
134 let value = self.get();
135 crate::math::accent::get_accent_func(value)
136 .or_else(|| crate::math::get_lr_wrapper_func(value))
137 .ok_or_else(|| eco_format!("symbol {self} is not callable"))
138 }
139
140 pub fn modified(
142 mut self,
143 mut sink: impl WarningSink,
144 modifier: &str,
145 ) -> StrResult<Self> {
146 if let SymbolInner::Complex(list) = self.0 {
147 self.0 = SymbolInner::Modified(Arc::new(Modified {
148 list: List::Static(list),
149 modifiers: ModifierSet::default(),
150 deprecated: false,
151 }));
152 }
153
154 if let SymbolInner::Modified(arc) = &mut self.0 {
155 let modified = Arc::make_mut(arc);
156 modified.modifiers.insert_raw(modifier);
157 if let Some(deprecation) = modified
158 .modifiers
159 .best_match_in(modified.list.variants().map(|(m, _, d)| (m, d)))
160 {
161 if !modified.deprecated
164 && let Some(message) = deprecation
165 {
166 modified.deprecated = true;
167 sink.emit(message.into());
168 }
169 return Ok(self);
170 }
171 }
172
173 bail!("unknown symbol modifier")
174 }
175
176 pub fn variants(&self) -> impl Iterator<Item = Variant<&str>> {
178 match &self.0 {
179 SymbolInner::Single(value) => Variants::Single(std::iter::once(*value)),
180 SymbolInner::Complex(list) => Variants::Static(list.iter()),
181 SymbolInner::Modified(arc) => arc.list.variants(),
182 }
183 }
184
185 pub fn modifiers(&self) -> impl Iterator<Item = &str> + '_ {
187 let modifiers = match &self.0 {
188 SymbolInner::Modified(arc) => arc.modifiers.as_deref(),
189 _ => ModifierSet::default(),
190 };
191 self.variants()
192 .flat_map(|(m, _, _)| m)
193 .filter(|modifier| !modifier.is_empty() && !modifiers.contains(modifier))
194 .collect::<BTreeSet<_>>()
195 .into_iter()
196 }
197}
198
199#[scope]
200impl Symbol {
201 #[func(constructor)]
219 pub fn construct(
220 span: Span,
221 #[variadic]
230 variants: Vec<Spanned<SymbolVariant>>,
231 ) -> SourceResult<Symbol> {
232 if variants.is_empty() {
233 bail!(span, "expected at least one variant");
234 }
235
236 let mut seen = FxHashMap::<u128, usize>::default();
239
240 let mut modifiers = Vec::new();
242
243 let mut errors = ecow::eco_vec![];
244
245 'variants: for (i, &Spanned { ref v, span }) in variants.iter().enumerate() {
247 modifiers.clear();
248
249 if v.1.is_empty() || v.1.graphemes(true).nth(1).is_some() {
250 errors.push(error!(
251 span, "invalid variant value: {}", v.1.repr();
252 hint: "variant value must be exactly one grapheme cluster";
253 ));
254 }
255
256 if !v.0.is_empty() {
257 for modifier in v.0.split('.') {
259 if !is_ident(modifier) {
260 errors.push(error!(
261 span,
262 "invalid symbol modifier: {}",
263 modifier.repr(),
264 ));
265 continue 'variants;
266 }
267 modifiers.push(modifier);
268 }
269 }
270
271 modifiers.sort();
273
274 if let Some(ms) = modifiers.windows(2).find(|ms| ms[0] == ms[1]) {
276 errors.push(error!(
277 span, "duplicate modifier within variant: {}", ms[0].repr();
278 hint: "modifiers are not ordered, so each one may appear only once";
279 ));
280 continue 'variants;
281 }
282
283 let hash = hash128(&modifiers);
285 if let Some(&i) = seen.get(&hash) {
286 errors.push(if v.0.is_empty() {
287 error!(span, "duplicate default variant")
288 } else if v.0 == variants[i].v.0 {
289 error!(span, "duplicate variant: {}", v.0.repr())
290 } else {
291 error!(
292 span, "duplicate variant: {}", v.0.repr();
293 hint: "variants with the same modifiers are identical, \
294 regardless of their order";
295 )
296 });
297 continue 'variants;
298 }
299
300 seen.insert(hash, i);
301 }
302 if !errors.is_empty() {
303 return Err(errors);
304 }
305
306 let list = variants
307 .into_iter()
308 .map(|s| (ModifierSet::from_raw_dotted(s.v.0), s.v.1, None))
309 .collect();
310 Ok(Symbol::runtime(list))
311 }
312}
313
314impl Display for Symbol {
315 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
316 f.write_str(self.get())
317 }
318}
319
320impl Debug for SymbolInner {
321 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
322 match self {
323 Self::Single(value) => Debug::fmt(value, f),
324 Self::Complex(list) => list.fmt(f),
325 Self::Modified(lists) => lists.fmt(f),
326 }
327 }
328}
329
330impl Debug for List {
331 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
332 match self {
333 Self::Static(list) => list.fmt(f),
334 Self::Runtime(list) => list.fmt(f),
335 }
336 }
337}
338
339impl Repr for Symbol {
340 fn repr(&self) -> EcoString {
341 match &self.0 {
342 SymbolInner::Single(value) => eco_format!("symbol({})", value.repr()),
343 SymbolInner::Complex(variants) => {
344 eco_format!(
345 "symbol{}",
346 repr_variants(variants.iter().copied(), ModifierSet::default())
347 )
348 }
349 SymbolInner::Modified(arc) => {
350 let Modified { list, modifiers, .. } = arc.as_ref();
351 if modifiers.is_empty() {
352 eco_format!(
353 "symbol{}",
354 repr_variants(list.variants(), ModifierSet::default())
355 )
356 } else {
357 eco_format!(
358 "symbol{}",
359 repr_variants(list.variants(), modifiers.as_deref())
360 )
361 }
362 }
363 }
364 }
365}
366
367fn repr_variants<'a>(
368 variants: impl Iterator<Item = Variant<&'a str>>,
369 applied_modifiers: ModifierSet<&str>,
370) -> String {
371 crate::foundations::repr::pretty_array_like(
372 &variants
373 .filter(|(modifiers, _, _)| {
374 applied_modifiers.iter().all(|am| modifiers.contains(am))
377 })
378 .map(|(modifiers, value, _)| {
379 let trimmed_modifiers =
380 modifiers.into_iter().filter(|&m| !applied_modifiers.contains(m));
381 if trimmed_modifiers.clone().all(|m| m.is_empty()) {
382 value.repr()
383 } else {
384 let trimmed_modifiers =
385 trimmed_modifiers.collect::<Vec<_>>().join(".");
386 eco_format!("({}, {})", trimmed_modifiers.repr(), value.repr())
387 }
388 })
389 .collect::<Vec<_>>(),
390 false,
391 )
392}
393
394impl Serialize for Symbol {
395 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
396 where
397 S: Serializer,
398 {
399 serializer.serialize_str(self.get())
400 }
401}
402
403impl List {
404 fn variants(&self) -> Variants<'_> {
406 match self {
407 List::Static(list) => Variants::Static(list.iter()),
408 List::Runtime(list) => Variants::Runtime(list.iter()),
409 }
410 }
411}
412
413pub struct SymbolVariant(EcoString, EcoString);
415
416cast! {
417 SymbolVariant,
418 s: EcoString => Self(EcoString::new(), s),
419 array: Array => {
420 let mut iter = array.into_iter();
421 match (iter.next(), iter.next(), iter.next()) {
422 (Some(a), Some(b), None) => Self(a.cast()?, b.cast()?),
423 _ => Err("variant array must contain exactly two entries")?,
424 }
425 },
426}
427
428enum Variants<'a> {
430 Single(std::iter::Once<&'static str>),
431 Static(std::slice::Iter<'static, Variant<&'static str>>),
432 Runtime(std::slice::Iter<'a, Variant<EcoString>>),
433}
434
435impl<'a> Iterator for Variants<'a> {
436 type Item = Variant<&'a str>;
437
438 fn next(&mut self) -> Option<Self::Item> {
439 match self {
440 Self::Single(iter) => Some((ModifierSet::default(), iter.next()?, None)),
441 Self::Static(list) => list.next().copied(),
442 Self::Runtime(list) => {
443 list.next().map(|(m, s, d)| (m.as_deref(), s.as_str(), d.as_deref()))
444 }
445 }
446 }
447}
448
449#[elem(Repr, PlainText)]
451pub struct SymbolElem {
452 #[required]
454 pub text: EcoString, }
456
457impl SymbolElem {
458 pub fn packed(text: impl Into<EcoString>) -> Content {
461 Self::new(text.into()).pack()
462 }
463}
464
465impl PlainText for Packed<SymbolElem> {
466 fn plain_text(&self, text: &mut EcoString) {
467 text.push_str(&self.text);
468 }
469}
470
471impl Repr for SymbolElem {
472 fn repr(&self) -> EcoString {
474 eco_format!("[{}]", self.text)
475 }
476}