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, EquationElem, 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::Equation<'_> {
13 type Output = Content;
14
15 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
16 let body = self.body().eval(vm)?;
17 let block = self.block();
18 Ok(EquationElem::new(body).with_block(block).pack())
19 }
20}
21
22impl Eval for ast::Math<'_> {
23 type Output = Content;
24
25 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
26 Ok(Content::sequence(
27 self.exprs()
28 .map(|expr| expr.eval_display(vm))
29 .collect::<SourceResult<Vec<_>>>()?,
30 ))
31 }
32}
33
34impl Eval for ast::MathText<'_> {
35 type Output = Content;
36
37 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
38 match self.get() {
39 MathTextKind::Grapheme(text) => Ok(SymbolElem::packed(text.clone())),
40 MathTextKind::Number(text) => Ok(TextElem::packed(text.clone())),
41 }
42 }
43}
44
45impl Eval for ast::MathIdent<'_> {
46 type Output = Value;
47
48 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
49 let span = self.span();
50 Ok(vm
51 .scopes
52 .get_in_math(&self)
53 .at(span)?
54 .read_checked((&mut vm.engine, span))
55 .clone())
56 }
57}
58
59impl Eval for ast::MathFieldAccess<'_> {
60 type Output = Value;
61
62 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
63 let target = self.target().eval(vm)?;
64 let field = self.field();
65 crate::code::access_field(vm, target, field.as_str(), field.span())
66 }
67}
68
69impl Eval for ast::MathAccess<'_> {
70 type Output = Value;
71
72 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
73 let value = match self {
74 ast::MathAccess::MathIdent(ident) => ident.eval(vm)?,
75 ast::MathAccess::MathFieldAccess(access) => access.eval(vm)?,
76 };
77 vm.trace_at(self.span(), &value);
80 Ok(value)
81 }
82}
83
84impl Eval for ast::MathShorthand<'_> {
85 type Output = Value;
86
87 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
88 Ok(Value::Symbol(Symbol::runtime_char(self.get())))
89 }
90}
91
92impl Eval for ast::MathAlignPoint<'_> {
93 type Output = Content;
94
95 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
96 Ok(AlignPointElem::shared().clone())
97 }
98}
99
100impl Eval for ast::MathDelimited<'_> {
101 type Output = Content;
102
103 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
104 let open = self.open().eval_display(vm)?;
105 let body = self.body().eval(vm)?;
106 let close = self.close().eval_display(vm)?;
107 Ok(LrElem::new(open + body + close).pack())
108 }
109}
110
111impl Eval for ast::MathAttach<'_> {
112 type Output = Content;
113
114 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
115 let base = self.base().eval_display(vm)?;
116 let mut elem = AttachElem::new(base);
117
118 if let Some(expr) = self.top() {
119 elem.t.set(Some(expr.eval_display(vm)?));
120 }
121
122 if let Some(primes) = self.primes() {
125 elem.tr.set(Some(primes.eval(vm)?));
126 }
127
128 if let Some(expr) = self.bottom() {
129 elem.b.set(Some(expr.eval_display(vm)?));
130 }
131
132 Ok(elem.pack())
133 }
134}
135
136impl Eval for ast::MathPrimes<'_> {
137 type Output = Content;
138
139 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
140 Ok(PrimesElem::new(self.count()).pack())
141 }
142}
143
144impl Eval for ast::MathFrac<'_> {
145 type Output = Content;
146
147 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
148 let num_expr = self.num();
149 let num = num_expr.eval_display(vm)?;
150 let denom_expr = self.denom();
151 let denom = denom_expr.eval_display(vm)?;
152
153 let num_depar =
154 matches!(num_expr, ast::Expr::Math(math) if math.was_deparenthesized());
155 let denom_depar =
156 matches!(denom_expr, ast::Expr::Math(math) if math.was_deparenthesized());
157
158 Ok(FracElem::new(num, denom)
159 .with_num_deparenthesized(num_depar)
160 .with_denom_deparenthesized(denom_depar)
161 .pack())
162 }
163}
164
165impl Eval for ast::MathRoot<'_> {
166 type Output = Content;
167
168 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
169 let index = self.index().map(|i| TextElem::packed(eco_format!("{i}")));
171 let radicand = self.radicand().eval_display(vm)?;
172 Ok(RootElem::new(radicand).with_index(index).pack())
173 }
174}
175
176trait ExprExt {
177 fn eval_display(&self, vm: &mut Vm) -> SourceResult<Content>;
178}
179
180impl ExprExt for ast::Expr<'_> {
181 fn eval_display(&self, vm: &mut Vm) -> SourceResult<Content> {
182 Ok(self.eval(vm)?.display().spanned(self.span()))
183 }
184}