1use typst_library::diag::{At, SourceResult, warning};
2use typst_library::foundations::{
3 Content, Label, NativeElement, Repr, Smart, Symbol, Unlabellable, Value,
4};
5use typst_library::math::EquationElem;
6use typst_library::model::{
7 EmphElem, EnumItem, HeadingElem, LinkElem, ListItem, ParbreakElem, RefElem,
8 StrongElem, Supplement, TermItem, Url,
9};
10use typst_library::text::{
11 LinebreakElem, RawContent, RawElem, SmartQuoteElem, SpaceElem, TextElem,
12};
13use typst_syntax::ast::{self, AstNode};
14use typst_utils::PicoStr;
15
16use crate::{Eval, Vm};
17
18impl Eval for ast::Markup<'_> {
19 type Output = Content;
20
21 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
22 eval_markup(vm, &mut self.exprs())
23 }
24}
25
26fn eval_markup<'a>(
28 vm: &mut Vm,
29 exprs: &mut impl Iterator<Item = ast::Expr<'a>>,
30) -> SourceResult<Content> {
31 let flow = vm.flow.take();
32 let mut seq = Vec::with_capacity(exprs.size_hint().1.unwrap_or_default());
33
34 while let Some(expr) = exprs.next() {
35 match expr {
36 ast::Expr::SetRule(set) => {
37 let styles = set.eval(vm)?;
38 if vm.flow.is_some() {
39 break;
40 }
41
42 seq.push(eval_markup(vm, exprs)?.styled_with_map(styles))
43 }
44 ast::Expr::ShowRule(show) => {
45 let recipe = show.eval(vm)?;
46 if vm.flow.is_some() {
47 break;
48 }
49
50 let tail = eval_markup(vm, exprs)?;
51 seq.push(tail.styled_with_recipe(&mut vm.engine, vm.context, recipe)?)
52 }
53 expr => match expr.eval(vm)? {
54 Value::Label(label) => {
55 if let Some(elem) =
56 seq.iter_mut().rev().find(|node| !node.can::<dyn Unlabellable>())
57 {
58 if elem.label().is_some() {
59 vm.engine.sink.warn(warning!(
60 elem.span(), "content labelled multiple times";
61 hint: "only the last label is used, the rest are ignored",
62 ));
63 }
64
65 *elem = std::mem::take(elem).labelled(label);
66 } else {
67 vm.engine.sink.warn(warning!(
68 expr.span(),
69 "label `{}` is not attached to anything",
70 label.repr()
71 ));
72 }
73 }
74 value => seq.push(value.display().spanned(expr.span())),
75 },
76 }
77
78 if vm.flow.is_some() {
79 break;
80 }
81 }
82
83 if flow.is_some() {
84 vm.flow = flow;
85 }
86
87 Ok(Content::sequence(seq))
88}
89
90impl Eval for ast::Text<'_> {
91 type Output = Content;
92
93 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
94 Ok(TextElem::packed(self.get().clone()))
95 }
96}
97
98impl Eval for ast::Space<'_> {
99 type Output = Content;
100
101 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
102 Ok(SpaceElem::shared().clone())
103 }
104}
105
106impl Eval for ast::Linebreak<'_> {
107 type Output = Content;
108
109 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
110 Ok(LinebreakElem::shared().clone())
111 }
112}
113
114impl Eval for ast::Parbreak<'_> {
115 type Output = Content;
116
117 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
118 Ok(ParbreakElem::shared().clone())
119 }
120}
121
122impl Eval for ast::Escape<'_> {
123 type Output = Value;
124
125 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
126 Ok(Value::Symbol(Symbol::runtime_char(self.get())))
127 }
128}
129
130impl Eval for ast::Shorthand<'_> {
131 type Output = Value;
132
133 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
134 Ok(Value::Symbol(Symbol::runtime_char(self.get())))
135 }
136}
137
138impl Eval for ast::SmartQuote<'_> {
139 type Output = Content;
140
141 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
142 Ok(SmartQuoteElem::new().with_double(self.double()).pack())
143 }
144}
145
146impl Eval for ast::Strong<'_> {
147 type Output = Content;
148
149 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
150 let body = self.body();
151 if body.exprs().next().is_none() {
152 vm.engine
153 .sink
154 .warn(warning!(
155 self.span(), "no text within stars";
156 hint: "using multiple consecutive stars (e.g. **) has no additional effect",
157 ));
158 }
159
160 Ok(StrongElem::new(body.eval(vm)?).pack())
161 }
162}
163
164impl Eval for ast::Emph<'_> {
165 type Output = Content;
166
167 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
168 let body = self.body();
169 if body.exprs().next().is_none() {
170 vm.engine
171 .sink
172 .warn(warning!(
173 self.span(), "no text within underscores";
174 hint: "using multiple consecutive underscores (e.g. __) has no additional effect"
175 ));
176 }
177
178 Ok(EmphElem::new(body.eval(vm)?).pack())
179 }
180}
181
182impl Eval for ast::Raw<'_> {
183 type Output = Content;
184
185 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
186 let lines = self.lines().map(|line| (line.get().clone(), line.span())).collect();
187 let mut elem = RawElem::new(RawContent::Lines(lines)).with_block(self.block());
188 if let Some(lang) = self.lang() {
189 elem.lang.set(Some(lang.get().clone()));
190 }
191 Ok(elem.pack())
192 }
193}
194
195impl Eval for ast::Link<'_> {
196 type Output = Content;
197
198 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
199 let url = Url::new(self.get().clone()).at(self.span())?;
200 Ok(LinkElem::from_url(url).pack())
201 }
202}
203
204impl Eval for ast::Label<'_> {
205 type Output = Value;
206
207 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
208 Ok(Value::Label(
209 Label::new(PicoStr::intern(self.get())).expect("unexpected empty label"),
210 ))
211 }
212}
213
214impl Eval for ast::Ref<'_> {
215 type Output = Content;
216
217 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
218 let target = Label::new(PicoStr::intern(self.target()))
219 .expect("unexpected empty reference");
220 let mut elem = RefElem::new(target);
221 if let Some(supplement) = self.supplement() {
222 elem.supplement
223 .set(Smart::Custom(Some(Supplement::Content(supplement.eval(vm)?))));
224 }
225 Ok(elem.pack())
226 }
227}
228
229impl Eval for ast::Heading<'_> {
230 type Output = Content;
231
232 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
233 let depth = self.depth();
234 let body = self.body().eval(vm)?;
235 Ok(HeadingElem::new(body).with_depth(depth).pack())
236 }
237}
238
239impl Eval for ast::ListItem<'_> {
240 type Output = Content;
241
242 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
243 Ok(ListItem::new(self.body().eval(vm)?).pack())
244 }
245}
246
247impl Eval for ast::EnumItem<'_> {
248 type Output = Content;
249
250 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
251 let body = self.body().eval(vm)?;
252 let mut elem = EnumItem::new(body);
253 if let Some(number) = self.number() {
254 elem.number.set(Smart::Custom(number));
255 }
256 Ok(elem.pack())
257 }
258}
259
260impl Eval for ast::TermItem<'_> {
261 type Output = Content;
262
263 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
264 let term = self.term().eval(vm)?;
265 let description = self.description().eval(vm)?;
266 Ok(TermItem::new(term, description).pack())
267 }
268}
269
270impl Eval for ast::Equation<'_> {
271 type Output = Content;
272
273 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
274 let body = self.body().eval(vm)?;
275 let block = self.block();
276 Ok(EquationElem::new(body).with_block(block).pack())
277 }
278}