1use ecow::eco_format;
2use typst_library::diag::{At, SourceResult};
3use typst_library::foundations::{Content, NativeElement, Symbol, SymbolElem, Value};
4use typst_library::math::{
5 AlignPointElem, AttachElem, FracElem, LrElem, PrimesElem, RootElem,
6};
7use typst_library::text::TextElem;
8use typst_syntax::ast::{self, AstNode, MathTextKind};
9
10use crate::{Eval, Vm};
11
12impl Eval for ast::Math<'_> {
13 type Output = Content;
14 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
15 Ok(Content::sequence(
16 self.exprs()
17 .map(|expr| expr.eval_display(vm))
18 .collect::<SourceResult<Vec<_>>>()?,
19 ))
20 }
21}
22
23impl Eval for ast::MathText<'_> {
24 type Output = Content;
25
26 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
27 match self.get() {
28 MathTextKind::Character(c) => Ok(SymbolElem::packed(c)),
29 MathTextKind::Number(text) => Ok(TextElem::packed(text.clone())),
30 }
31 }
32}
33
34impl Eval for ast::MathIdent<'_> {
35 type Output = Value;
36
37 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
38 let span = self.span();
39 Ok(vm
40 .scopes
41 .get_in_math(&self)
42 .at(span)?
43 .read_checked((&mut vm.engine, span))
44 .clone())
45 }
46}
47
48impl Eval for ast::MathShorthand<'_> {
49 type Output = Value;
50
51 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
52 Ok(Value::Symbol(Symbol::runtime_char(self.get())))
53 }
54}
55
56impl Eval for ast::MathAlignPoint<'_> {
57 type Output = Content;
58
59 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
60 Ok(AlignPointElem::shared().clone())
61 }
62}
63
64impl Eval for ast::MathDelimited<'_> {
65 type Output = Content;
66
67 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
68 let open = self.open().eval_display(vm)?;
69 let body = self.body().eval(vm)?;
70 let close = self.close().eval_display(vm)?;
71 Ok(LrElem::new(open + body + close).pack())
72 }
73}
74
75impl Eval for ast::MathAttach<'_> {
76 type Output = Content;
77
78 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
79 let base = self.base().eval_display(vm)?;
80 let mut elem = AttachElem::new(base);
81
82 if let Some(expr) = self.top() {
83 elem.t.set(Some(expr.eval_display(vm)?));
84 }
85
86 if let Some(primes) = self.primes() {
89 elem.tr.set(Some(primes.eval(vm)?));
90 }
91
92 if let Some(expr) = self.bottom() {
93 elem.b.set(Some(expr.eval_display(vm)?));
94 }
95
96 Ok(elem.pack())
97 }
98}
99
100impl Eval for ast::MathPrimes<'_> {
101 type Output = Content;
102
103 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
104 Ok(PrimesElem::new(self.count()).pack())
105 }
106}
107
108impl Eval for ast::MathFrac<'_> {
109 type Output = Content;
110
111 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
112 let num_expr = self.num();
113 let num = num_expr.eval_display(vm)?;
114 let denom_expr = self.denom();
115 let denom = denom_expr.eval_display(vm)?;
116
117 let num_depar =
118 matches!(num_expr, ast::Expr::Math(math) if math.was_deparenthesized());
119 let denom_depar =
120 matches!(denom_expr, ast::Expr::Math(math) if math.was_deparenthesized());
121
122 Ok(FracElem::new(num, denom)
123 .with_num_deparenthesized(num_depar)
124 .with_denom_deparenthesized(denom_depar)
125 .pack())
126 }
127}
128
129impl Eval for ast::MathRoot<'_> {
130 type Output = Content;
131
132 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
133 let index = self.index().map(|i| TextElem::packed(eco_format!("{i}")));
135 let radicand = self.radicand().eval_display(vm)?;
136 Ok(RootElem::new(radicand).with_index(index).pack())
137 }
138}
139
140trait ExprExt {
141 fn eval_display(&self, vm: &mut Vm) -> SourceResult<Content>;
142}
143
144impl ExprExt for ast::Expr<'_> {
145 fn eval_display(&self, vm: &mut Vm) -> SourceResult<Content> {
146 Ok(self.eval(vm)?.display().spanned(self.span()))
147 }
148}