use ecow::eco_format; use typst_library::diag::{At, SourceResult}; use typst_library::foundations::{Content, NativeElement, Symbol, SymbolElem, Value}; use typst_library::math::{ AlignPointElem, AttachElem, FracElem, LrElem, PrimesElem, RootElem, }; use typst_library::text::TextElem; use typst_syntax::ast::{self, AstNode, MathTextKind}; use crate::{Eval, Vm}; impl Eval for ast::Math<'_> { type Output = Content; fn eval(self, vm: &mut Vm) -> SourceResult { Ok(Content::sequence( self.exprs() .map(|expr| expr.eval_display(vm)) .collect::>>()?, )) } } impl Eval for ast::MathText<'_> { type Output = Content; fn eval(self, _: &mut Vm) -> SourceResult { match self.get() { MathTextKind::Character(c) => Ok(SymbolElem::packed(c)), MathTextKind::Number(text) => Ok(TextElem::packed(text.clone())), } } } impl Eval for ast::MathIdent<'_> { type Output = Value; fn eval(self, vm: &mut Vm) -> SourceResult { let span = self.span(); Ok(vm .scopes .get_in_math(&self) .at(span)? .read_checked((&mut vm.engine, span)) .clone()) } } impl Eval for ast::MathShorthand<'_> { type Output = Value; fn eval(self, _: &mut Vm) -> SourceResult { Ok(Value::Symbol(Symbol::single(self.get()))) } } impl Eval for ast::MathAlignPoint<'_> { type Output = Content; fn eval(self, _: &mut Vm) -> SourceResult { Ok(AlignPointElem::shared().clone()) } } impl Eval for ast::MathDelimited<'_> { type Output = Content; fn eval(self, vm: &mut Vm) -> SourceResult { let open = self.open().eval_display(vm)?; let body = self.body().eval(vm)?; let close = self.close().eval_display(vm)?; Ok(LrElem::new(open + body + close).pack()) } } impl Eval for ast::MathAttach<'_> { type Output = Content; fn eval(self, vm: &mut Vm) -> SourceResult { let base = self.base().eval_display(vm)?; let mut elem = AttachElem::new(base); if let Some(expr) = self.top() { elem.push_t(Some(expr.eval_display(vm)?)); } // Always attach primes in scripts style (not limits style), // i.e. at the top-right corner. if let Some(primes) = self.primes() { elem.push_tr(Some(primes.eval(vm)?)); } if let Some(expr) = self.bottom() { elem.push_b(Some(expr.eval_display(vm)?)); } Ok(elem.pack()) } } impl Eval for ast::MathPrimes<'_> { type Output = Content; fn eval(self, _: &mut Vm) -> SourceResult { Ok(PrimesElem::new(self.count()).pack()) } } impl Eval for ast::MathFrac<'_> { type Output = Content; fn eval(self, vm: &mut Vm) -> SourceResult { let num = self.num().eval_display(vm)?; let denom = self.denom().eval_display(vm)?; Ok(FracElem::new(num, denom).pack()) } } impl Eval for ast::MathRoot<'_> { type Output = Content; fn eval(self, vm: &mut Vm) -> SourceResult { // Use `TextElem` to match `MathTextKind::Number` above. let index = self.index().map(|i| TextElem::packed(eco_format!("{i}"))); let radicand = self.radicand().eval_display(vm)?; Ok(RootElem::new(radicand).with_index(index).pack()) } } trait ExprExt { fn eval_display(&self, vm: &mut Vm) -> SourceResult; } impl ExprExt for ast::Expr<'_> { fn eval_display(&self, vm: &mut Vm) -> SourceResult { Ok(self.eval(vm)?.display().spanned(self.span())) } }